From 9418fea231d482d3d92055c91686e15ccfd43367 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 21 Jan 2015 21:09:07 +0900 Subject: [PATCH 001/208] Enable coverage report (but there are not tests yet). --- observablescrollview/build.gradle | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/observablescrollview/build.gradle b/observablescrollview/build.gradle index 92ac1196..6f71604a 100644 --- a/observablescrollview/build.gradle +++ b/observablescrollview/build.gradle @@ -24,6 +24,16 @@ android { defaultConfig { minSdkVersion 9 } + + jacoco { + version = '0.7.2.201409121644' + } + + buildTypes { + debug { + testCoverageEnabled = true + } + } } // This is from 'https://github.com/chrisbanes/gradle-mvn-push' From ce4af1b132710795970c6a65aec57cf46dee78a6 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Thu, 22 Jan 2015 22:30:36 +0900 Subject: [PATCH 002/208] Add small tests. --- .../observablescrollview/ScrollUtilsTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollUtilsTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollUtilsTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollUtilsTest.java new file mode 100644 index 00000000..9ccd8562 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollUtilsTest.java @@ -0,0 +1,26 @@ +package com.github.ksoichiro.android.observablescrollview; + +import android.graphics.Color; +import android.test.InstrumentationTestCase; + +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; + +import junit.framework.Assert; + +public class ScrollUtilsTest extends InstrumentationTestCase { + + public void testGetFloat() { + Assert.assertEquals(1.0f, ScrollUtils.getFloat(1, 0, 2)); + assertEquals(0.0f, ScrollUtils.getFloat(-1, 0, 2)); + assertEquals(2.0f, ScrollUtils.getFloat(3, 0, 2)); + } + + public void testGetColorWithAlpha() { + assertEquals(Color.parseColor("#00123456"), ScrollUtils.getColorWithAlpha(0, Color.parseColor("#FF123456"))); + assertEquals(Color.parseColor("#FF123456"), ScrollUtils.getColorWithAlpha(1, Color.parseColor("#FF123456"))); + } + + public void testMixColors() { + assertEquals(Color.parseColor("#000000"), ScrollUtils.mixColors(Color.parseColor("#000000"), Color.parseColor("#FFFFFF"), 0)); + } +} From 310700e1f377b14ee99a7cfea765a330f1dfe234 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 23 Jan 2015 22:07:12 +0900 Subject: [PATCH 003/208] Add UI test. --- .../src/androidTest/AndroidManifest.xml | 25 +++++++++ .../ScrollViewActivity.java | 28 ++++++++++ .../ScrollViewActivityTest.java | 53 +++++++++++++++++++ .../res/layout/activity_scrollview.xml | 27 ++++++++++ .../src/androidTest/res/values/strings.xml | 23 ++++++++ 5 files changed, 156 insertions(+) create mode 100644 observablescrollview/src/androidTest/AndroidManifest.xml create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivityTest.java create mode 100644 observablescrollview/src/androidTest/res/layout/activity_scrollview.xml create mode 100644 observablescrollview/src/androidTest/res/values/strings.xml diff --git a/observablescrollview/src/androidTest/AndroidManifest.xml b/observablescrollview/src/androidTest/AndroidManifest.xml new file mode 100644 index 00000000..4178d806 --- /dev/null +++ b/observablescrollview/src/androidTest/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivity.java new file mode 100644 index 00000000..5334ca53 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivity.java @@ -0,0 +1,28 @@ +package com.github.ksoichiro.android.observablescrollview; + +import android.app.Activity; +import android.os.Bundle; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; + +public class ScrollViewActivity extends Activity implements ObservableScrollViewCallbacks { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(com.github.ksoichiro.android.observablescrollview.test.R.layout.activity_scrollview); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivityTest.java new file mode 100644 index 00000000..a4af0d26 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivityTest.java @@ -0,0 +1,53 @@ +package com.github.ksoichiro.android.observablescrollview; + +import android.test.ActivityInstrumentationTestCase2; +import android.test.InstrumentationTestCase; +import android.test.TouchUtils; +import android.util.TypedValue; +import android.view.View; + +public class ScrollViewActivityTest extends ActivityInstrumentationTestCase2 { + + private ScrollViewActivity activity; + private ObservableScrollView scrollable; + + public ScrollViewActivityTest() { + super(ScrollViewActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableScrollView) activity.findViewById(com.github.ksoichiro.android.observablescrollview.test.R.id.scrollable); + } + + public void testScroll() throws Throwable { + swipeVertically(this, scrollable, Direction.UP); + getInstrumentation().waitForIdleSync(); + + swipeVertically(this, scrollable, Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + private void swipeVertically(InstrumentationTestCase test, View v, Direction direction) { + int[] xy = new int[2]; + v.getLocationOnScreen(xy); + + final int viewWidth = v.getWidth(); + final int viewHeight = v.getHeight(); + + float distanceFromEdge = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, + activity.getResources().getDisplayMetrics()); + float x = xy[0] + (viewWidth / 2.0f); + float yStart = xy[1] + ((direction == Direction.UP) ? (viewHeight - distanceFromEdge) : distanceFromEdge); + float yEnd = xy[1] + ((direction == Direction.UP) ? distanceFromEdge : (viewHeight - distanceFromEdge)); + + TouchUtils.drag(test, x, x, yStart, yEnd, 100); + } + + public enum Direction { + UP, DOWN + } +} diff --git a/observablescrollview/src/androidTest/res/layout/activity_scrollview.xml b/observablescrollview/src/androidTest/res/layout/activity_scrollview.xml new file mode 100644 index 00000000..972537fe --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_scrollview.xml @@ -0,0 +1,27 @@ + + + + + + diff --git a/observablescrollview/src/androidTest/res/values/strings.xml b/observablescrollview/src/androidTest/res/values/strings.xml new file mode 100644 index 00000000..6f8f6062 --- /dev/null +++ b/observablescrollview/src/androidTest/res/values/strings.xml @@ -0,0 +1,23 @@ + + + + Lorem ipsum dolor sit amet, ut duis lorem provident sed felis blandit, condimentum donec lectus ipsum et mauris, morbi porttitor interdum feugiat nulla donec sodales, vestibulum nisl primis a molestie vestibulum quam, sapien mauris metus risus suspendisse magnis. Augue viverra nulla faucibus egestas eu, a etiam id congue rutrum ante, arcu tincidunt donec quam felis at ornare, iaculis ligula sodales venenatis commodo volutpat neque, suspendisse elit praesent tellus felis mi amet. Inceptos amet tempor lectus lorem est non, ac donec ac libero neque mauris, tellus ante metus eget leo consequat. Scelerisque dolor curabitur pretium blandit ut feugiat, amet lacus pulvinar justo convallis ut, sed natoque ipsum urna posuere nibh eu. Sed at sed vulputate sit orci, facilisis a aliquam tellus quam aliquam, eu aliquam donec at molestie ante, pellentesque mauris lorem ultrices libero faucibus porta, imperdiet adipiscing sit hac diam ut nulla. Lacus enim elit pulvinar donec vehicula dapibus, accumsan purus officia cursus dolor sapien, eu amet dis mauris mi nulla ut. Non accusamus etiam pede non urna tempus, vestibulum aliquam tortor eget pharetra sodales, in vestibulum ut justo orci nulla, lobortis purus sem semper consectetuer magni purus. Dolor a leo vestibulum amet ut sit, arcu ut eaque urna fusce aliquet turpis, sed fermentum sed vestibulum nisl pede, tristique enim lorem posuere in laborum ut. Vestibulum id id justo leo nulla, magna lobortis ullamcorper et dignissim pellentesque, duis suspendisse quis id lorem ante. Vivamus a nullam ante adipiscing amet, mi vel consectetuer nunc aenean pede quisque, eget rhoncus dis porttitor habitant nunc vivamus, duis cubilia blandit non donec justo dictumst, praesent vitae nulla nam pulvinar urna. Adipiscing adipiscing justo urna pulvinar imperdiet nullam, vitae fusce rhoncus proin nonummy suscipit, ullamcorper amet et non potenti platea ultrices, mauris nullam sapien nunc justo vel, eu semper pellentesque arcu fusce augue. Malesuada mauris nibh sit a a scelerisque, velit sem lectus tellus convallis consectetuer, ultricies auctor a ante eros amet sed.\n\n +Risus lacus duis leo platea wisi, felis maecenas rutrum in id in donec, non id a potenti libero eget, posuere elit ea sed pellentesque quis. Sunt lacus urna lorem elit duis, nibh donec purus quisque consectetuer dolor, neque vestibulum proin ornare eros nonummy phasellus. Iaculis cras eu at egestas dolor montes, viverra quisque malesuada consectetuer semper maecenas, a sed vitae donec tempor aliqua metus, ornare mollis suscipit et erat fusce, sit orci aut auctor elementum fames aliquam. Platea dui integer magnis non metus, minus dignissimos ante massa nostra et, rutrum sapien egestas quis sapien donec donec. Erat sit a eros aenean natoque, quam libero id lorem enim proin, lorem ipsum fermentum mattis metus et. Aliquam aliquet suscipit purus conubia at neque, platea vivamus vestibulum nulla quibusdam senectus, et morbi lectus malesuada gravida donec, elementum sit convallis pellentesque velit amet. Et eveniet viverra vehicula consectetuer justo, provident sed commodo non lacinia velit, tempor phasellus vel leo nisl cras, vivamus et arcu interdum dui eu amet. Volutpat wisi rhoncus vel turpis diam quibusdam, dapibus elit est quisque cubilia mauris, nulla elit magna tempor accumsan bibendum, lorem varius sed interdum eget mattis, scelerisque egestas feugiat donec dui molestie. Leo facilisis nisl sit montes ligula sed, enim commodo consectetuer nunc est et, ut sed vehicula dolor luctus elit. Fermentum cras donec eget nibh est vel, sed justo risus et pharetra diam, eu vivamus egestas ligula risus diam, sed justo eget hac ut mauris. Vestibulum diam nec vitae mi eget suspendisse, aenean arcu purus facilisis purus class in, id aliquam sit id scelerisque sapien etiam. Ut nullam sit sed at mauris lobortis, consequat dolor autem ipsum euismod nulla, elit quis proin eget conubia varius, erat arcu massa mus in mauris, scelerisque ut eu sollicitudin libero leo urna.\n\n +Consectetuer luctus tempor elit ut dolor ligula, quis dui per dui hendrerit ante sagittis, in quisque pretium in eleifend enim. Condimentum iaculis vitae feugiat dis tellus vel, lectus dolor nec dui nulla nascetur, et pellentesque curabitur lorem leo velit eget. Id nascetur arcu lobortis suspendisse imperdiet urna, natoque nascetur ante in porta a, interdum hendrerit mi bibendum platea tellus, urna in enim ornare vestibulum faucibus enim. Leo fusce egestas ante nec volutpat, in tempor vel facilisis potenti ut, pede at non lorem a commodo, nulla dolor orci interdum vestibulum nulla. Dui nulla vestibulum quisque a pharetra porta, integer nec ipsum nec sed dui pharetra, magna et dignissim ipsum sed dictum, litora eros vivamus scelerisque libero ipsum. Sed ac ac lorem molestie adipiscing morbi, pellentesque imperdiet nunc quis morbi amet ante, libero dui ligula nec risus neque et, velit nonummy phasellus et facilisi amet, ligula in elementum non sapien pulvinar faucibus. Eu leo ut posuere sed aliquet, tincidunt vel urna volutpat tempus sem, sit felis aliquet vestibulum condimentum sit, amet nibh vel tellus purus ullamcorper libero, nulla vestibulum pede ut vestibulum pretium. Eu nulla vestibulum a neque in metus, quisquam nam sed cursus eget luctus, pede ultrices nec sed dignissim pellentesque, sit class cursus metus nulla placerat mauris, consequat mollis neque vivamus amet pede. Mauris dolor nulla diam eros bibendum, quam ante vestibulum morbi non ligula vel, molestie curabitur rhoncus nulla euismod interdum non. Nulla fringilla lorem mollis ad massa, sit molestie nibh lorem arcu volutpat, accumsan commodo lectus eu et donec, sit tempor tempus rutrum in curabitur amet. Nec urna euismod a tincidunt commodo, eu pede turpis libero vitae viverra, ante vestibulum nam non habitasse potenti, mauris imperdiet in in nunc convallis. Et nostra wisi in est accumsan vehicula, quisque vitae felis mauris sed vulputate nec, ante imperdiet sollicitudin massa iaculis massa sit.\n\n +Quam libero nulla netus eu porta curae, ut nulla bibendum facilisis et urna sed, quis congue vestibulum aliquam interdum etiam. Nulla vel lobortis ullamcorper vitae excepturi, neque urna feugiat lectus vel lacinia, massa pretium orci eu metus neque vulputate. Imperdiet ac velit rhoncus nulla malesuada nullam, nec pulvinar justo gravida lorem rutrum magna, habitasse repudiandae mi eros vestibulum ante, nec euismod dui iaculis in turpis pretium, ac id metus egestas proin lacus lectus. Laoreet lorem nec vitae risus erat arcu, vitae quam ut in ante tristique, porta dolor pede quam et odio nam, arcu lacus sem congue ante cursus massa. Et mattis sagittis erat accumsan fusce quam, vehicula ligula beatae natoque fusce sodales conubia, habitasse metus cum magnis viverra nam cursus, egestas urna wisi primis blandit eu magna, eget libero elit lacus lorem dis aliquam. Ut mauris ante natoque lacus massa, justo a lectus sodales enim adipiscing id, accumsan ut ipsum vestibulum sed enim auctor, vitae congue tincidunt id phasellus lacinia scelerisque, tincidunt sapien nulla euismod volutpat iaculis. Platea sociis nec aliquet nec molestie, in mi et augue sapien in vivamus, integer fames proin vitae in ullamcorper et. Fringilla etiam sapiente rhoncus suspendisse nec id, lobortis cras eget egestas dui ac nec, justo lacus ut lorem bibendum quia eros, eget a gravida id donec nunc suscipit, porta sed in sodales non rutrum. Lectus vel dui elementum pellentesque magna aliquam, vitae non sit pede et fusce nibh, id id deserunt ornare dui sit condimentum, in adipiscing imperdiet turpis nam aliquet, facilisis metus magna lacus wisi facilisis tortor. Vulputate elit accumsan quam amet ligula, suspendisse lacus mi nonummy integer urna, libero nulla nunc varius in odio, laoreet nulla amet placerat amet nec. Consectetuer vel massa hendrerit vitae iaculis id, sed ut ut laudantium odio in, elit vestibulum duis ante maecenas interdum in, neque vehicula ultrices varius in quam, pede tellus pellentesque sed nullam quis. + + From a64961db553ad5806844e485890dfa9a4d1bda44 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 24 Jan 2015 19:02:40 +0900 Subject: [PATCH 004/208] Remove template test class. --- .../observablescrollview/ApplicationTest.java | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ApplicationTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ApplicationTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ApplicationTest.java deleted file mode 100644 index ff68ca2d..00000000 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ApplicationTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.github.ksoichiro.android.observablescrollview; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); - } -} \ No newline at end of file From 526476d70debff441d3aecba42c376b23e61548f Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 24 Jan 2015 19:06:21 +0900 Subject: [PATCH 005/208] Create common UI test methods class. --- .../ScrollViewActivityTest.java | 30 ++--------------- .../observablescrollview/UiTestUtils.java | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/UiTestUtils.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivityTest.java index a4af0d26..71fa23d4 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivityTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivityTest.java @@ -1,14 +1,9 @@ package com.github.ksoichiro.android.observablescrollview; import android.test.ActivityInstrumentationTestCase2; -import android.test.InstrumentationTestCase; -import android.test.TouchUtils; -import android.util.TypedValue; -import android.view.View; public class ScrollViewActivityTest extends ActivityInstrumentationTestCase2 { - private ScrollViewActivity activity; private ObservableScrollView scrollable; public ScrollViewActivityTest() { @@ -19,35 +14,16 @@ public ScrollViewActivityTest() { protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); - activity = getActivity(); + ScrollViewActivity activity = getActivity(); scrollable = (ObservableScrollView) activity.findViewById(com.github.ksoichiro.android.observablescrollview.test.R.id.scrollable); } public void testScroll() throws Throwable { - swipeVertically(this, scrollable, Direction.UP); + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); getInstrumentation().waitForIdleSync(); - swipeVertically(this, scrollable, Direction.DOWN); + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); getInstrumentation().waitForIdleSync(); } - private void swipeVertically(InstrumentationTestCase test, View v, Direction direction) { - int[] xy = new int[2]; - v.getLocationOnScreen(xy); - - final int viewWidth = v.getWidth(); - final int viewHeight = v.getHeight(); - - float distanceFromEdge = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, - activity.getResources().getDisplayMetrics()); - float x = xy[0] + (viewWidth / 2.0f); - float yStart = xy[1] + ((direction == Direction.UP) ? (viewHeight - distanceFromEdge) : distanceFromEdge); - float yEnd = xy[1] + ((direction == Direction.UP) ? distanceFromEdge : (viewHeight - distanceFromEdge)); - - TouchUtils.drag(test, x, x, yStart, yEnd, 100); - } - - public enum Direction { - UP, DOWN - } } diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/UiTestUtils.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/UiTestUtils.java new file mode 100644 index 00000000..452426cb --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/UiTestUtils.java @@ -0,0 +1,33 @@ +package com.github.ksoichiro.android.observablescrollview; + +import android.test.InstrumentationTestCase; +import android.test.TouchUtils; +import android.util.TypedValue; +import android.view.View; + +public class UiTestUtils { + + public enum Direction { + UP, DOWN + } + + private UiTestUtils() { + } + + public static void swipeVertically(InstrumentationTestCase test, View v, Direction direction) { + int[] xy = new int[2]; + v.getLocationOnScreen(xy); + + final int viewWidth = v.getWidth(); + final int viewHeight = v.getHeight(); + + float distanceFromEdge = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, + v.getResources().getDisplayMetrics()); + float x = xy[0] + (viewWidth / 2.0f); + float yStart = xy[1] + ((direction == Direction.UP) ? (viewHeight - distanceFromEdge) : distanceFromEdge); + float yEnd = xy[1] + ((direction == Direction.UP) ? distanceFromEdge : (viewHeight - distanceFromEdge)); + + TouchUtils.drag(test, x, x, yStart, yEnd, 100); + } + +} From 8d08aced6c9f459e2b739dfbd0d513f955dc76b5 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 24 Jan 2015 19:19:24 +0900 Subject: [PATCH 006/208] Add ListView test. --- .../src/androidTest/AndroidManifest.xml | 1 + .../ListViewActivity.java | 26 ++++++++++++++++ .../ListViewActivityTest.java | 29 +++++++++++++++++ .../observablescrollview/UiTestUtils.java | 31 +++++++++++++++++++ .../res/layout/activity_listview.xml | 19 ++++++++++++ 5 files changed, 106 insertions(+) create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivityTest.java create mode 100644 observablescrollview/src/androidTest/res/layout/activity_listview.xml diff --git a/observablescrollview/src/androidTest/AndroidManifest.xml b/observablescrollview/src/androidTest/AndroidManifest.xml index 4178d806..5dda6339 100644 --- a/observablescrollview/src/androidTest/AndroidManifest.xml +++ b/observablescrollview/src/androidTest/AndroidManifest.xml @@ -19,6 +19,7 @@ + diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivity.java new file mode 100644 index 00000000..354b6d53 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivity.java @@ -0,0 +1,26 @@ +package com.github.ksoichiro.android.observablescrollview; + +import android.app.Activity; +import android.os.Bundle; + +public class ListViewActivity extends Activity implements ObservableScrollViewCallbacks { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(com.github.ksoichiro.android.observablescrollview.test.R.layout.activity_listview); + UiTestUtils.setDummyData(this, (ObservableListView) findViewById(com.github.ksoichiro.android.observablescrollview.test.R.id.scrollable)); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivityTest.java new file mode 100644 index 00000000..1f0609c0 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivityTest.java @@ -0,0 +1,29 @@ +package com.github.ksoichiro.android.observablescrollview; + +import android.test.ActivityInstrumentationTestCase2; + +public class ListViewActivityTest extends ActivityInstrumentationTestCase2 { + + private ObservableListView scrollable; + + public ListViewActivityTest() { + super(ListViewActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + final ListViewActivity activity = getActivity(); + scrollable = (ObservableListView) activity.findViewById(com.github.ksoichiro.android.observablescrollview.test.R.id.scrollable); + } + + public void testScroll() throws Throwable { + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/UiTestUtils.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/UiTestUtils.java index 452426cb..af82db83 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/UiTestUtils.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/UiTestUtils.java @@ -1,12 +1,20 @@ package com.github.ksoichiro.android.observablescrollview; +import android.content.Context; import android.test.InstrumentationTestCase; import android.test.TouchUtils; import android.util.TypedValue; import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +import java.util.ArrayList; public class UiTestUtils { + private static final int NUM_OF_ITEMS = 100; + private static final int NUM_OF_ITEMS_FEW = 3; + public enum Direction { UP, DOWN } @@ -30,4 +38,27 @@ public static void swipeVertically(InstrumentationTestCase test, View v, Directi TouchUtils.drag(test, x, x, yStart, yEnd, 100); } + public static ArrayList getDummyData() { + return getDummyData(NUM_OF_ITEMS); + } + + public static ArrayList getDummyData(int num) { + ArrayList items = new ArrayList(); + for (int i = 1; i <= num; i++) { + items.add("Item " + i); + } + return items; + } + + public static void setDummyData(Context context, ListView listView) { + setDummyData(context, listView, NUM_OF_ITEMS); + } + + public static void setDummyDataFew(Context context, ListView listView) { + setDummyData(context, listView, NUM_OF_ITEMS_FEW); + } + + public static void setDummyData(Context context, ListView listView, int num) { + listView.setAdapter(new ArrayAdapter(context, android.R.layout.simple_list_item_1, getDummyData(num))); + } } diff --git a/observablescrollview/src/androidTest/res/layout/activity_listview.xml b/observablescrollview/src/androidTest/res/layout/activity_listview.xml new file mode 100644 index 00000000..18dd210b --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_listview.xml @@ -0,0 +1,19 @@ + + From c3c39a0324d5cc24e1ce948f0788ecee4fe86a86 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 24 Jan 2015 19:22:16 +0900 Subject: [PATCH 007/208] Change test classes's package to .test. --- .../src/androidTest/AndroidManifest.xml | 2 +- .../{ => test}/ListViewActivity.java | 10 +++++++--- .../{ => test}/ListViewActivityTest.java | 6 ++++-- .../{ => test}/ScrollUtilsTest.java | 2 +- .../{ => test}/ScrollViewActivity.java | 4 ++-- .../{ => test}/ScrollViewActivityTest.java | 6 ++++-- .../observablescrollview/{ => test}/UiTestUtils.java | 2 +- 7 files changed, 20 insertions(+), 12 deletions(-) rename observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/{ => test}/ListViewActivity.java (60%) rename observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/{ => test}/ListViewActivityTest.java (84%) rename observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/{ => test}/ScrollUtilsTest.java (93%) rename observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/{ => test}/ScrollViewActivity.java (80%) rename observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/{ => test}/ScrollViewActivityTest.java (83%) rename observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/{ => test}/UiTestUtils.java (96%) diff --git a/observablescrollview/src/androidTest/AndroidManifest.xml b/observablescrollview/src/androidTest/AndroidManifest.xml index 5dda6339..98b425dc 100644 --- a/observablescrollview/src/androidTest/AndroidManifest.xml +++ b/observablescrollview/src/androidTest/AndroidManifest.xml @@ -15,7 +15,7 @@ --> + package="com.github.ksoichiro.android.observablescrollview.test"> diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java similarity index 60% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivity.java rename to observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java index 354b6d53..7c932cd1 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivity.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java @@ -1,15 +1,19 @@ -package com.github.ksoichiro.android.observablescrollview; +package com.github.ksoichiro.android.observablescrollview.test; import android.app.Activity; import android.os.Bundle; +import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; + public class ListViewActivity extends Activity implements ObservableScrollViewCallbacks { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(com.github.ksoichiro.android.observablescrollview.test.R.layout.activity_listview); - UiTestUtils.setDummyData(this, (ObservableListView) findViewById(com.github.ksoichiro.android.observablescrollview.test.R.id.scrollable)); + setContentView(R.layout.activity_listview); + UiTestUtils.setDummyData(this, (ObservableListView) findViewById(R.id.scrollable)); } @Override diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java similarity index 84% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivityTest.java rename to observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java index 1f0609c0..9fc12ea6 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ListViewActivityTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java @@ -1,7 +1,9 @@ -package com.github.ksoichiro.android.observablescrollview; +package com.github.ksoichiro.android.observablescrollview.test; import android.test.ActivityInstrumentationTestCase2; +import com.github.ksoichiro.android.observablescrollview.ObservableListView; + public class ListViewActivityTest extends ActivityInstrumentationTestCase2 { private ObservableListView scrollable; @@ -15,7 +17,7 @@ protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); final ListViewActivity activity = getActivity(); - scrollable = (ObservableListView) activity.findViewById(com.github.ksoichiro.android.observablescrollview.test.R.id.scrollable); + scrollable = (ObservableListView) activity.findViewById(R.id.scrollable); } public void testScroll() throws Throwable { diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollUtilsTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollUtilsTest.java similarity index 93% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollUtilsTest.java rename to observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollUtilsTest.java index 9ccd8562..6b2aecdf 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollUtilsTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollUtilsTest.java @@ -1,4 +1,4 @@ -package com.github.ksoichiro.android.observablescrollview; +package com.github.ksoichiro.android.observablescrollview.test; import android.graphics.Color; import android.test.InstrumentationTestCase; diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java similarity index 80% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivity.java rename to observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java index 5334ca53..80973c2e 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivity.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java @@ -1,4 +1,4 @@ -package com.github.ksoichiro.android.observablescrollview; +package com.github.ksoichiro.android.observablescrollview.test; import android.app.Activity; import android.os.Bundle; @@ -11,7 +11,7 @@ public class ScrollViewActivity extends Activity implements ObservableScrollView @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(com.github.ksoichiro.android.observablescrollview.test.R.layout.activity_scrollview); + setContentView(R.layout.activity_scrollview); } @Override diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java similarity index 83% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivityTest.java rename to observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java index 71fa23d4..016ea75c 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/ScrollViewActivityTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java @@ -1,7 +1,9 @@ -package com.github.ksoichiro.android.observablescrollview; +package com.github.ksoichiro.android.observablescrollview.test; import android.test.ActivityInstrumentationTestCase2; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; + public class ScrollViewActivityTest extends ActivityInstrumentationTestCase2 { private ObservableScrollView scrollable; @@ -15,7 +17,7 @@ protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); ScrollViewActivity activity = getActivity(); - scrollable = (ObservableScrollView) activity.findViewById(com.github.ksoichiro.android.observablescrollview.test.R.id.scrollable); + scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable); } public void testScroll() throws Throwable { diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/UiTestUtils.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java similarity index 96% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/UiTestUtils.java rename to observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java index af82db83..12826bbf 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/UiTestUtils.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java @@ -1,4 +1,4 @@ -package com.github.ksoichiro.android.observablescrollview; +package com.github.ksoichiro.android.observablescrollview.test; import android.content.Context; import android.test.InstrumentationTestCase; From 05ecc63f3e806639090e81f24803668b2b4ad5b5 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 24 Jan 2015 20:30:13 +0900 Subject: [PATCH 008/208] Add tests for all the Observable widgets. --- .../src/androidTest/AndroidManifest.xml | 3 + .../src/androidTest/assets/lipsum.html | 17 ++++++ .../test/GridViewActivity.java | 33 ++++++++++ .../test/GridViewActivityTest.java | 37 ++++++++++++ .../test/ListViewActivity.java | 4 +- .../test/ListViewActivityTest.java | 8 ++- .../test/RecyclerViewActivity.java | 36 +++++++++++ .../test/RecyclerViewActivityTest.java | 38 ++++++++++++ .../test/ScrollViewActivity.java | 3 + .../test/ScrollViewActivityTest.java | 8 ++- .../test/SimpleRecyclerAdapter.java | 60 +++++++++++++++++++ .../test/UiTestUtils.java | 34 +++++++++++ .../test/WebViewActivity.java | 33 ++++++++++ .../test/WebViewActivityTest.java | 37 ++++++++++++ .../res/layout/activity_gridview.xml | 20 +++++++ .../res/layout/activity_recyclerview.xml | 19 ++++++ .../res/layout/activity_webview.xml | 19 ++++++ 17 files changed, 406 insertions(+), 3 deletions(-) create mode 100644 observablescrollview/src/androidTest/assets/lipsum.html create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleRecyclerAdapter.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java create mode 100644 observablescrollview/src/androidTest/res/layout/activity_gridview.xml create mode 100644 observablescrollview/src/androidTest/res/layout/activity_recyclerview.xml create mode 100644 observablescrollview/src/androidTest/res/layout/activity_webview.xml diff --git a/observablescrollview/src/androidTest/AndroidManifest.xml b/observablescrollview/src/androidTest/AndroidManifest.xml index 98b425dc..6929bee9 100644 --- a/observablescrollview/src/androidTest/AndroidManifest.xml +++ b/observablescrollview/src/androidTest/AndroidManifest.xml @@ -19,8 +19,11 @@ + + + diff --git a/observablescrollview/src/androidTest/assets/lipsum.html b/observablescrollview/src/androidTest/assets/lipsum.html new file mode 100644 index 00000000..7d71889e --- /dev/null +++ b/observablescrollview/src/androidTest/assets/lipsum.html @@ -0,0 +1,17 @@ + + + + + +
+

Lorem ipsum dolor sit amet, ut duis lorem provident sed felis blandit, condimentum donec lectus ipsum et mauris, morbi porttitor interdum feugiat nulla donec sodales, vestibulum nisl primis a molestie vestibulum quam, sapien mauris metus risus suspendisse magnis. Augue viverra nulla faucibus egestas eu, a etiam id congue rutrum ante, arcu tincidunt donec quam felis at ornare, iaculis ligula sodales venenatis commodo volutpat neque, suspendisse elit praesent tellus felis mi amet. Inceptos amet tempor lectus lorem est non, ac donec ac libero neque mauris, tellus ante metus eget leo consequat. Scelerisque dolor curabitur pretium blandit ut feugiat, amet lacus pulvinar justo convallis ut, sed natoque ipsum urna posuere nibh eu. Sed at sed vulputate sit orci, facilisis a aliquam tellus quam aliquam, eu aliquam donec at molestie ante, pellentesque mauris lorem ultrices libero faucibus porta, imperdiet adipiscing sit hac diam ut nulla. Lacus enim elit pulvinar donec vehicula dapibus, accumsan purus officia cursus dolor sapien, eu amet dis mauris mi nulla ut. Non accusamus etiam pede non urna tempus, vestibulum aliquam tortor eget pharetra sodales, in vestibulum ut justo orci nulla, lobortis purus sem semper consectetuer magni purus. Dolor a leo vestibulum amet ut sit, arcu ut eaque urna fusce aliquet turpis, sed fermentum sed vestibulum nisl pede, tristique enim lorem posuere in laborum ut. Vestibulum id id justo leo nulla, magna lobortis ullamcorper et dignissim pellentesque, duis suspendisse quis id lorem ante. Vivamus a nullam ante adipiscing amet, mi vel consectetuer nunc aenean pede quisque, eget rhoncus dis porttitor habitant nunc vivamus, duis cubilia blandit non donec justo dictumst, praesent vitae nulla nam pulvinar urna. Adipiscing adipiscing justo urna pulvinar imperdiet nullam, vitae fusce rhoncus proin nonummy suscipit, ullamcorper amet et non potenti platea ultrices, mauris nullam sapien nunc justo vel, eu semper pellentesque arcu fusce augue. Malesuada mauris nibh sit a a scelerisque, velit sem lectus tellus convallis consectetuer, ultricies auctor a ante eros amet sed.

+

Risus lacus duis leo platea wisi, felis maecenas rutrum in id in donec, non id a potenti libero eget, posuere elit ea sed pellentesque quis. Sunt lacus urna lorem elit duis, nibh donec purus quisque consectetuer dolor, neque vestibulum proin ornare eros nonummy phasellus. Iaculis cras eu at egestas dolor montes, viverra quisque malesuada consectetuer semper maecenas, a sed vitae donec tempor aliqua metus, ornare mollis suscipit et erat fusce, sit orci aut auctor elementum fames aliquam. Platea dui integer magnis non metus, minus dignissimos ante massa nostra et, rutrum sapien egestas quis sapien donec donec. Erat sit a eros aenean natoque, quam libero id lorem enim proin, lorem ipsum fermentum mattis metus et. Aliquam aliquet suscipit purus conubia at neque, platea vivamus vestibulum nulla quibusdam senectus, et morbi lectus malesuada gravida donec, elementum sit convallis pellentesque velit amet. Et eveniet viverra vehicula consectetuer justo, provident sed commodo non lacinia velit, tempor phasellus vel leo nisl cras, vivamus et arcu interdum dui eu amet. Volutpat wisi rhoncus vel turpis diam quibusdam, dapibus elit est quisque cubilia mauris, nulla elit magna tempor accumsan bibendum, lorem varius sed interdum eget mattis, scelerisque egestas feugiat donec dui molestie. Leo facilisis nisl sit montes ligula sed, enim commodo consectetuer nunc est et, ut sed vehicula dolor luctus elit. Fermentum cras donec eget nibh est vel, sed justo risus et pharetra diam, eu vivamus egestas ligula risus diam, sed justo eget hac ut mauris. Vestibulum diam nec vitae mi eget suspendisse, aenean arcu purus facilisis purus class in, id aliquam sit id scelerisque sapien etiam. Ut nullam sit sed at mauris lobortis, consequat dolor autem ipsum euismod nulla, elit quis proin eget conubia varius, erat arcu massa mus in mauris, scelerisque ut eu sollicitudin libero leo urna.

+

Consectetuer luctus tempor elit ut dolor ligula, quis dui per dui hendrerit ante sagittis, in quisque pretium in eleifend enim. Condimentum iaculis vitae feugiat dis tellus vel, lectus dolor nec dui nulla nascetur, et pellentesque curabitur lorem leo velit eget. Id nascetur arcu lobortis suspendisse imperdiet urna, natoque nascetur ante in porta a, interdum hendrerit mi bibendum platea tellus, urna in enim ornare vestibulum faucibus enim. Leo fusce egestas ante nec volutpat, in tempor vel facilisis potenti ut, pede at non lorem a commodo, nulla dolor orci interdum vestibulum nulla. Dui nulla vestibulum quisque a pharetra porta, integer nec ipsum nec sed dui pharetra, magna et dignissim ipsum sed dictum, litora eros vivamus scelerisque libero ipsum. Sed ac ac lorem molestie adipiscing morbi, pellentesque imperdiet nunc quis morbi amet ante, libero dui ligula nec risus neque et, velit nonummy phasellus et facilisi amet, ligula in elementum non sapien pulvinar faucibus. Eu leo ut posuere sed aliquet, tincidunt vel urna volutpat tempus sem, sit felis aliquet vestibulum condimentum sit, amet nibh vel tellus purus ullamcorper libero, nulla vestibulum pede ut vestibulum pretium. Eu nulla vestibulum a neque in metus, quisquam nam sed cursus eget luctus, pede ultrices nec sed dignissim pellentesque, sit class cursus metus nulla placerat mauris, consequat mollis neque vivamus amet pede. Mauris dolor nulla diam eros bibendum, quam ante vestibulum morbi non ligula vel, molestie curabitur rhoncus nulla euismod interdum non. Nulla fringilla lorem mollis ad massa, sit molestie nibh lorem arcu volutpat, accumsan commodo lectus eu et donec, sit tempor tempus rutrum in curabitur amet. Nec urna euismod a tincidunt commodo, eu pede turpis libero vitae viverra, ante vestibulum nam non habitasse potenti, mauris imperdiet in in nunc convallis. Et nostra wisi in est accumsan vehicula, quisque vitae felis mauris sed vulputate nec, ante imperdiet sollicitudin massa iaculis massa sit.

+

Quam libero nulla netus eu porta curae, ut nulla bibendum facilisis et urna sed, quis congue vestibulum aliquam interdum etiam. Nulla vel lobortis ullamcorper vitae excepturi, neque urna feugiat lectus vel lacinia, massa pretium orci eu metus neque vulputate. Imperdiet ac velit rhoncus nulla malesuada nullam, nec pulvinar justo gravida lorem rutrum magna, habitasse repudiandae mi eros vestibulum ante, nec euismod dui iaculis in turpis pretium, ac id metus egestas proin lacus lectus. Laoreet lorem nec vitae risus erat arcu, vitae quam ut in ante tristique, porta dolor pede quam et odio nam, arcu lacus sem congue ante cursus massa. Et mattis sagittis erat accumsan fusce quam, vehicula ligula beatae natoque fusce sodales conubia, habitasse metus cum magnis viverra nam cursus, egestas urna wisi primis blandit eu magna, eget libero elit lacus lorem dis aliquam. Ut mauris ante natoque lacus massa, justo a lectus sodales enim adipiscing id, accumsan ut ipsum vestibulum sed enim auctor, vitae congue tincidunt id phasellus lacinia scelerisque, tincidunt sapien nulla euismod volutpat iaculis. Platea sociis nec aliquet nec molestie, in mi et augue sapien in vivamus, integer fames proin vitae in ullamcorper et. Fringilla etiam sapiente rhoncus suspendisse nec id, lobortis cras eget egestas dui ac nec, justo lacus ut lorem bibendum quia eros, eget a gravida id donec nunc suscipit, porta sed in sodales non rutrum. Lectus vel dui elementum pellentesque magna aliquam, vitae non sit pede et fusce nibh, id id deserunt ornare dui sit condimentum, in adipiscing imperdiet turpis nam aliquet, facilisis metus magna lacus wisi facilisis tortor. Vulputate elit accumsan quam amet ligula, suspendisse lacus mi nonummy integer urna, libero nulla nunc varius in odio, laoreet nulla amet placerat amet nec. Consectetuer vel massa hendrerit vitae iaculis id, sed ut ut laudantium odio in, elit vestibulum duis ante maecenas interdum in, neque vehicula ultrices varius in quam, pede tellus pellentesque sed nullam quis.

+
+ + \ No newline at end of file diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java new file mode 100644 index 00000000..e4b3e5fc --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java @@ -0,0 +1,33 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; + +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; +import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; + +public class GridViewActivity extends Activity implements ObservableScrollViewCallbacks { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_gridview); + ObservableGridView scrollable = (ObservableGridView) findViewById(R.id.scrollable); + scrollable.setScrollViewCallbacks(this); + UiTestUtils.setDummyData(this, scrollable); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java new file mode 100644 index 00000000..cf114c87 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java @@ -0,0 +1,37 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; + +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; + +public class GridViewActivityTest extends ActivityInstrumentationTestCase2 { + + private Activity activity; + private ObservableGridView scrollable; + + public GridViewActivityTest() { + super(GridViewActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable); + } + + public void testScroll() throws Throwable { + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + public void testSaveAndRestoreInstanceState() throws Throwable { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + testScroll(); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java index 7c932cd1..e91c3373 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java @@ -13,7 +13,9 @@ public class ListViewActivity extends Activity implements ObservableScrollViewCa protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_listview); - UiTestUtils.setDummyData(this, (ObservableListView) findViewById(R.id.scrollable)); + ObservableListView scrollable = (ObservableListView) findViewById(R.id.scrollable); + scrollable.setScrollViewCallbacks(this); + UiTestUtils.setDummyData(this, scrollable); } @Override diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java index 9fc12ea6..f17a5012 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java @@ -1,11 +1,13 @@ package com.github.ksoichiro.android.observablescrollview.test; +import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; import com.github.ksoichiro.android.observablescrollview.ObservableListView; public class ListViewActivityTest extends ActivityInstrumentationTestCase2 { + private Activity activity; private ObservableListView scrollable; public ListViewActivityTest() { @@ -16,7 +18,7 @@ public ListViewActivityTest() { protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); - final ListViewActivity activity = getActivity(); + activity = getActivity(); scrollable = (ObservableListView) activity.findViewById(R.id.scrollable); } @@ -28,4 +30,8 @@ public void testScroll() throws Throwable { getInstrumentation().waitForIdleSync(); } + public void testSaveAndRestoreInstanceState() throws Throwable { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + testScroll(); + } } diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivity.java new file mode 100644 index 00000000..01753974 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivity.java @@ -0,0 +1,36 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; + +import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; + +public class RecyclerViewActivity extends Activity implements ObservableScrollViewCallbacks { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_recyclerview); + + ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.scrollable); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setHasFixedSize(true); + recyclerView.setScrollViewCallbacks(this); + UiTestUtils.setDummyData(this, recyclerView); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java new file mode 100644 index 00000000..37f6f396 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java @@ -0,0 +1,38 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; + +import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; + +public class RecyclerViewActivityTest extends ActivityInstrumentationTestCase2 { + + private Activity activity; + private ObservableRecyclerView scrollable; + + public RecyclerViewActivityTest() { + super(RecyclerViewActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableRecyclerView) activity.findViewById(R.id.scrollable); + getInstrumentation().waitForIdleSync(); + } + + public void testScroll() throws Throwable { + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + public void testSaveAndRestoreInstanceState() throws Throwable { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + testScroll(); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java index 80973c2e..d98a9727 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java @@ -3,8 +3,10 @@ import android.app.Activity; import android.os.Bundle; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.Scrollable; public class ScrollViewActivity extends Activity implements ObservableScrollViewCallbacks { @@ -12,6 +14,7 @@ public class ScrollViewActivity extends Activity implements ObservableScrollView protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_scrollview); + ((Scrollable) findViewById(R.id.scrollable)).setScrollViewCallbacks(this); } @Override diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java index 016ea75c..c7bcf06a 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java @@ -1,11 +1,13 @@ package com.github.ksoichiro.android.observablescrollview.test; +import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; public class ScrollViewActivityTest extends ActivityInstrumentationTestCase2 { + private Activity activity; private ObservableScrollView scrollable; public ScrollViewActivityTest() { @@ -16,7 +18,7 @@ public ScrollViewActivityTest() { protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); - ScrollViewActivity activity = getActivity(); + activity = getActivity(); scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable); } @@ -28,4 +30,8 @@ public void testScroll() throws Throwable { getInstrumentation().waitForIdleSync(); } + public void testSaveAndRestoreInstanceState() throws Throwable { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + testScroll(); + } } diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleRecyclerAdapter.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleRecyclerAdapter.java new file mode 100644 index 00000000..daf2b5df --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleRecyclerAdapter.java @@ -0,0 +1,60 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.test; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import java.util.ArrayList; + +public class SimpleRecyclerAdapter extends RecyclerView.Adapter { + private LayoutInflater mInflater; + private ArrayList mItems; + + public SimpleRecyclerAdapter(Context context, ArrayList items) { + mInflater = LayoutInflater.from(context); + mItems = items; + } + + @Override + public int getItemCount() { + return mItems.size(); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new ViewHolder(mInflater.inflate(android.R.layout.simple_list_item_1, parent, false)); + } + + @Override + public void onBindViewHolder(ViewHolder viewHolder, int position) { + viewHolder.textView.setText(mItems.get(position)); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + TextView textView; + + public ViewHolder(View view) { + super(view); + textView = (TextView) view.findViewById(android.R.id.text1); + } + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java index 12826bbf..0bb960c6 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java @@ -1,11 +1,15 @@ package com.github.ksoichiro.android.observablescrollview.test; +import android.app.Activity; import android.content.Context; +import android.os.Bundle; +import android.support.v7.widget.RecyclerView; import android.test.InstrumentationTestCase; import android.test.TouchUtils; import android.util.TypedValue; import android.view.View; import android.widget.ArrayAdapter; +import android.widget.GridView; import android.widget.ListView; import java.util.ArrayList; @@ -22,6 +26,20 @@ public enum Direction { private UiTestUtils() { } + public static void saveAndRestoreInstanceState(final InstrumentationTestCase test, final Activity activity) throws Throwable { + test.runTestOnUiThread(new Runnable() { + @Override + public void run() { + Bundle outState = new Bundle(); + test.getInstrumentation().callActivityOnSaveInstanceState(activity, outState); + test.getInstrumentation().callActivityOnPause(activity); + test.getInstrumentation().callActivityOnResume(activity); + test.getInstrumentation().callActivityOnRestoreInstanceState(activity, outState); + } + }); + test.getInstrumentation().waitForIdleSync(); + } + public static void swipeVertically(InstrumentationTestCase test, View v, Direction direction) { int[] xy = new int[2]; v.getLocationOnScreen(xy); @@ -50,6 +68,10 @@ public static ArrayList getDummyData(int num) { return items; } + public static void setDummyData(Context context, GridView gridView) { + gridView.setAdapter(new ArrayAdapter(context, android.R.layout.simple_list_item_1, getDummyData())); + } + public static void setDummyData(Context context, ListView listView) { setDummyData(context, listView, NUM_OF_ITEMS); } @@ -61,4 +83,16 @@ public static void setDummyDataFew(Context context, ListView listView) { public static void setDummyData(Context context, ListView listView, int num) { listView.setAdapter(new ArrayAdapter(context, android.R.layout.simple_list_item_1, getDummyData(num))); } + + public static void setDummyData(Context context, RecyclerView recyclerView) { + setDummyData(context, recyclerView, NUM_OF_ITEMS); + } + + public static void setDummyDataFew(Context context, RecyclerView recyclerView) { + setDummyData(context, recyclerView, NUM_OF_ITEMS_FEW); + } + + public static void setDummyData(Context context, RecyclerView recyclerView, int num) { + recyclerView.setAdapter(new SimpleRecyclerAdapter(context, getDummyData(num))); + } } diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivity.java new file mode 100644 index 00000000..dd13963a --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivity.java @@ -0,0 +1,33 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ObservableWebView; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.Scrollable; + +public class WebViewActivity extends Activity implements ObservableScrollViewCallbacks { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_webview); + ObservableWebView scrollable = (ObservableWebView) findViewById(R.id.scrollable); + scrollable.setScrollViewCallbacks(this); + scrollable.loadUrl("file:///android_asset/lipsum.html"); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java new file mode 100644 index 00000000..c44c1db2 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java @@ -0,0 +1,37 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; + +import com.github.ksoichiro.android.observablescrollview.ObservableWebView; + +public class WebViewActivityTest extends ActivityInstrumentationTestCase2 { + + private Activity activity; + private ObservableWebView scrollable; + + public WebViewActivityTest() { + super(WebViewActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableWebView) activity.findViewById(R.id.scrollable); + } + + public void testScroll() throws Throwable { + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + public void testSaveAndRestoreInstanceState() throws Throwable { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + testScroll(); + } +} diff --git a/observablescrollview/src/androidTest/res/layout/activity_gridview.xml b/observablescrollview/src/androidTest/res/layout/activity_gridview.xml new file mode 100644 index 00000000..d5ddb31d --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_gridview.xml @@ -0,0 +1,20 @@ + + diff --git a/observablescrollview/src/androidTest/res/layout/activity_recyclerview.xml b/observablescrollview/src/androidTest/res/layout/activity_recyclerview.xml new file mode 100644 index 00000000..dce42c47 --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_recyclerview.xml @@ -0,0 +1,19 @@ + + diff --git a/observablescrollview/src/androidTest/res/layout/activity_webview.xml b/observablescrollview/src/androidTest/res/layout/activity_webview.xml new file mode 100644 index 00000000..75d62617 --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_webview.xml @@ -0,0 +1,19 @@ + + From 0a816f5fbae5fbfec9cae283c618550f577573db Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 24 Jan 2015 21:08:14 +0900 Subject: [PATCH 009/208] Add ViewPager tests. --- observablescrollview/build.gradle | 6 + .../src/androidTest/AndroidManifest.xml | 1 + .../test/SimpleHeaderRecyclerAdapter.java | 86 +++++ .../test/UiTestUtils.java | 27 +- .../test/ViewPagerTabActivity.java | 276 +++++++++++++++ .../test/ViewPagerTabActivityTest.java | 56 +++ .../test/ViewPagerTabListViewFragment.java | 59 ++++ .../ViewPagerTabRecyclerViewFragment.java | 62 ++++ .../test/ViewPagerTabScrollViewFragment.java | 56 +++ .../iosched/ui/widget/SlidingTabLayout.java | 321 ++++++++++++++++++ .../iosched/ui/widget/SlidingTabStrip.java | 168 +++++++++ .../androidTest/res/color/tab_text_color.xml | 21 ++ .../res/layout/activity_viewpagertab.xml | 59 ++++ .../res/layout/fragment_listview.xml | 19 ++ .../res/layout/fragment_recyclerview.xml | 20 ++ .../res/layout/fragment_scrollview.xml | 37 ++ .../src/androidTest/res/layout/padding.xml | 19 ++ .../androidTest/res/layout/tab_indicator.xml | 25 ++ .../src/androidTest/res/values/colors.xml | 22 ++ .../src/androidTest/res/values/dimens.xml | 18 + .../src/androidTest/res/values/styles.xml | 36 ++ 21 files changed, 1393 insertions(+), 1 deletion(-) create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleHeaderRecyclerAdapter.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabListViewFragment.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabRecyclerViewFragment.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabScrollViewFragment.java create mode 100644 observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java create mode 100644 observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java create mode 100644 observablescrollview/src/androidTest/res/color/tab_text_color.xml create mode 100644 observablescrollview/src/androidTest/res/layout/activity_viewpagertab.xml create mode 100644 observablescrollview/src/androidTest/res/layout/fragment_listview.xml create mode 100644 observablescrollview/src/androidTest/res/layout/fragment_recyclerview.xml create mode 100644 observablescrollview/src/androidTest/res/layout/fragment_scrollview.xml create mode 100644 observablescrollview/src/androidTest/res/layout/padding.xml create mode 100644 observablescrollview/src/androidTest/res/layout/tab_indicator.xml create mode 100644 observablescrollview/src/androidTest/res/values/colors.xml create mode 100644 observablescrollview/src/androidTest/res/values/dimens.xml create mode 100644 observablescrollview/src/androidTest/res/values/styles.xml diff --git a/observablescrollview/build.gradle b/observablescrollview/build.gradle index 6f71604a..9ddd57be 100644 --- a/observablescrollview/build.gradle +++ b/observablescrollview/build.gradle @@ -15,6 +15,12 @@ repositories { dependencies { compile 'com.android.support:recyclerview-v7:21.0.0' + androidTestCompile ('com.android.support:appcompat-v7:21.0.2') { + exclude module: 'support-v4' + } + androidTestCompile ('com.nineoldandroids:library:2.4.0') { + exclude module: 'support-v4' + } } android { diff --git a/observablescrollview/src/androidTest/AndroidManifest.xml b/observablescrollview/src/androidTest/AndroidManifest.xml index 6929bee9..ab6745cf 100644 --- a/observablescrollview/src/androidTest/AndroidManifest.xml +++ b/observablescrollview/src/androidTest/AndroidManifest.xml @@ -23,6 +23,7 @@ +
diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleHeaderRecyclerAdapter.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleHeaderRecyclerAdapter.java new file mode 100644 index 00000000..18a3887f --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleHeaderRecyclerAdapter.java @@ -0,0 +1,86 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.test; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import java.util.ArrayList; + +public class SimpleHeaderRecyclerAdapter extends RecyclerView.Adapter { + private static final int VIEW_TYPE_HEADER = 0; + private static final int VIEW_TYPE_ITEM = 1; + + private LayoutInflater mInflater; + private ArrayList mItems; + private View mHeaderView; + + public SimpleHeaderRecyclerAdapter(Context context, ArrayList items, View headerView) { + mInflater = LayoutInflater.from(context); + mItems = items; + mHeaderView = headerView; + } + + @Override + public int getItemCount() { + if (mHeaderView == null) { + return mItems.size(); + } else { + return mItems.size() + 1; + } + } + + @Override + public int getItemViewType(int position) { + return (position == 0) ? VIEW_TYPE_HEADER : VIEW_TYPE_ITEM; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == VIEW_TYPE_HEADER) { + return new HeaderViewHolder(mHeaderView); + } else { + return new ItemViewHolder(mInflater.inflate(android.R.layout.simple_list_item_1, parent, false)); + } + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { + if (viewHolder instanceof ItemViewHolder) { + ((ItemViewHolder) viewHolder).textView.setText(mItems.get(position - 1)); + } + } + + static class HeaderViewHolder extends RecyclerView.ViewHolder { + public HeaderViewHolder(View view) { + super(view); + } + } + + static class ItemViewHolder extends RecyclerView.ViewHolder { + TextView textView; + + public ItemViewHolder(View view) { + super(view); + textView = (TextView) view.findViewById(android.R.id.text1); + } + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java index 0bb960c6..623553ed 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java @@ -20,7 +20,7 @@ public class UiTestUtils { private static final int NUM_OF_ITEMS_FEW = 3; public enum Direction { - UP, DOWN + LEFT, RIGHT, UP, DOWN } private UiTestUtils() { @@ -40,6 +40,22 @@ public void run() { test.getInstrumentation().waitForIdleSync(); } + public static void swipeHorizontally(InstrumentationTestCase test, View v, Direction direction) { + int[] xy = new int[2]; + v.getLocationOnScreen(xy); + + final int viewWidth = v.getWidth(); + final int viewHeight = v.getHeight(); + + float distanceFromEdge = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, + v.getResources().getDisplayMetrics()); + float xStart = xy[0] + ((direction == Direction.LEFT) ? (viewWidth - distanceFromEdge) : distanceFromEdge); + float xEnd = xy[0] + ((direction == Direction.LEFT) ? distanceFromEdge : (viewWidth - distanceFromEdge)); + float y = xy[1] + (viewHeight / 2.0f); + + TouchUtils.drag(test, xStart, xEnd, y, y, 100); + } + public static void swipeVertically(InstrumentationTestCase test, View v, Direction direction) { int[] xy = new int[2]; v.getLocationOnScreen(xy); @@ -84,6 +100,11 @@ public static void setDummyData(Context context, ListView listView, int num) { listView.setAdapter(new ArrayAdapter(context, android.R.layout.simple_list_item_1, getDummyData(num))); } + public static void setDummyDataWithHeader(Context context, ListView listView, View headerView) { + listView.addHeaderView(headerView); + setDummyData(context, listView); + } + public static void setDummyData(Context context, RecyclerView recyclerView) { setDummyData(context, recyclerView, NUM_OF_ITEMS); } @@ -95,4 +116,8 @@ public static void setDummyDataFew(Context context, RecyclerView recyclerView) { public static void setDummyData(Context context, RecyclerView recyclerView, int num) { recyclerView.setAdapter(new SimpleRecyclerAdapter(context, getDummyData(num))); } + + public static void setDummyDataWithHeader(Context context, RecyclerView recyclerView, View headerView) { + recyclerView.setAdapter(new SimpleHeaderRecyclerAdapter(context, getDummyData(), headerView)); + } } diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java new file mode 100644 index 00000000..475bb518 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java @@ -0,0 +1,276 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.Toolbar; +import android.view.View; + +import com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.github.ksoichiro.android.observablescrollview.Scrollable; +import com.google.samples.apps.iosched.ui.widget.SlidingTabLayout; +import com.nineoldandroids.view.ViewHelper; +import com.nineoldandroids.view.ViewPropertyAnimator; + +public class ViewPagerTabActivity extends ActionBarActivity implements ObservableScrollViewCallbacks { + + private View mHeaderView; + private View mToolbarView; + private int mBaseTranslationY; + private ViewPager mPager; + private NavigationAdapter mPagerAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_viewpagertab); + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + + mHeaderView = findViewById(R.id.header); + mToolbarView = findViewById(R.id.toolbar); + mPagerAdapter = new NavigationAdapter(getSupportFragmentManager()); + mPager = (ViewPager) findViewById(R.id.pager); + mPager.setAdapter(mPagerAdapter); + + SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs); + slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1); + slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent)); + slidingTabLayout.setDistributeEvenly(true); + slidingTabLayout.setViewPager(mPager); + + // When the page is selected, other fragments' scrollY should be adjusted + // according to the toolbar status(shown/hidden) + slidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int i, float v, int i2) { + } + + @Override + public void onPageSelected(int i) { + propagateToolbarState(toolbarIsShown()); + } + + @Override + public void onPageScrollStateChanged(int i) { + } + }); + + propagateToolbarState(toolbarIsShown()); + } + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (dragging) { + int toolbarHeight = mToolbarView.getHeight(); + float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView); + if (firstScroll) { + if (-toolbarHeight < currentHeaderTranslationY) { + mBaseTranslationY = scrollY; + } + } + float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0); + ViewPropertyAnimator.animate(mHeaderView).cancel(); + ViewHelper.setTranslationY(mHeaderView, headerTranslationY); + } + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + mBaseTranslationY = 0; + + Fragment fragment = getCurrentFragment(); + if (fragment == null) { + return; + } + View view = fragment.getView(); + if (view == null) { + return; + } + + // ObservableXxxViews have same API + // but currently they don't have any common interfaces. + adjustToolbar(scrollState, view); + } + + private void adjustToolbar(ScrollState scrollState, View view) { + int toolbarHeight = mToolbarView.getHeight(); + final Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll); + if (scrollView == null) { + return; + } + int scrollY = scrollView.getCurrentScrollY(); + if (scrollState == ScrollState.DOWN) { + showToolbar(); + } else if (scrollState == ScrollState.UP) { + if (toolbarHeight <= scrollY) { + hideToolbar(); + } else { + showToolbar(); + } + } else { + // Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted + if (toolbarIsShown() || toolbarIsHidden()) { + // Toolbar is completely moved, so just keep its state + // and propagate it to other pages + propagateToolbarState(toolbarIsShown()); + } else { + // Toolbar is moving but doesn't know which to move: + // you can change this to hideToolbar() + showToolbar(); + } + } + } + + private Fragment getCurrentFragment() { + return mPagerAdapter.getItemAt(mPager.getCurrentItem()); + } + + private void propagateToolbarState(boolean isShown) { + int toolbarHeight = mToolbarView.getHeight(); + + // Set scrollY for the fragments that are not created yet + mPagerAdapter.setScrollY(isShown ? 0 : toolbarHeight); + + // Set scrollY for the active fragments + for (int i = 0; i < mPagerAdapter.getCount(); i++) { + // Skip current item + if (i == mPager.getCurrentItem()) { + continue; + } + + // Skip destroyed or not created item + Fragment f = mPagerAdapter.getItemAt(i); + if (f == null) { + continue; + } + + View view = f.getView(); + if (view == null) { + continue; + } + propagateToolbarState(isShown, view, toolbarHeight); + } + } + + private void propagateToolbarState(boolean isShown, View view, int toolbarHeight) { + Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll); + if (scrollView == null) { + return; + } + if (isShown) { + // Scroll up + if (0 < scrollView.getCurrentScrollY()) { + scrollView.scrollVerticallyTo(0); + } + } else { + // Scroll down (to hide padding) + if (scrollView.getCurrentScrollY() < toolbarHeight) { + scrollView.scrollVerticallyTo(toolbarHeight); + } + } + } + + private boolean toolbarIsShown() { + return ViewHelper.getTranslationY(mHeaderView) == 0; + } + + private boolean toolbarIsHidden() { + return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight(); + } + + private void showToolbar() { + float headerTranslationY = ViewHelper.getTranslationY(mHeaderView); + if (headerTranslationY != 0) { + ViewPropertyAnimator.animate(mHeaderView).cancel(); + ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start(); + } + propagateToolbarState(true); + } + + private void hideToolbar() { + float headerTranslationY = ViewHelper.getTranslationY(mHeaderView); + int toolbarHeight = mToolbarView.getHeight(); + if (headerTranslationY != -toolbarHeight) { + ViewPropertyAnimator.animate(mHeaderView).cancel(); + ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start(); + } + propagateToolbarState(false); + } + + /** + * This adapter provides two types of fragments as an example. + * {@linkplain #createItem(int)} should be modified if you use this example for your app. + */ + private static class NavigationAdapter extends CacheFragmentStatePagerAdapter { + + private static final String[] TITLES = new String[]{"Applepie", "Butter Cookie", "Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb", "Ice Cream Sandwich", "Jelly Bean", "KitKat", "Lollipop"}; + + private int mScrollY; + + public NavigationAdapter(FragmentManager fm) { + super(fm); + } + + public void setScrollY(int scrollY) { + mScrollY = scrollY; + } + + @Override + protected Fragment createItem(int position) { + // Initialize fragments. + // Please be sure to pass scroll position to each fragments using setArguments. + Fragment f; + final int pattern = position % 3; + switch (pattern) { + case 0: { + f = new ViewPagerTabScrollViewFragment(); + if (0 <= mScrollY) { + Bundle args = new Bundle(); + args.putInt(ViewPagerTabScrollViewFragment.ARG_SCROLL_Y, mScrollY); + f.setArguments(args); + } + break; + } + case 1: { + f = new ViewPagerTabListViewFragment(); + if (0 < mScrollY) { + Bundle args = new Bundle(); + args.putInt(ViewPagerTabListViewFragment.ARG_INITIAL_POSITION, 1); + f.setArguments(args); + } + break; + } + case 2: + default: { + f = new ViewPagerTabRecyclerViewFragment(); + if (0 < mScrollY) { + Bundle args = new Bundle(); + args.putInt(ViewPagerTabRecyclerViewFragment.ARG_INITIAL_POSITION, 1); + f.setArguments(args); + } + break; + } + } + return f; + } + + @Override + public int getCount() { + return TITLES.length; + } + + @Override + public CharSequence getPageTitle(int position) { + return TITLES[position]; + } + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java new file mode 100644 index 00000000..29dd761d --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java @@ -0,0 +1,56 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; +import android.view.View; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; + +public class ViewPagerTabActivityTest extends ActivityInstrumentationTestCase2 { + + private Activity activity; + + public ViewPagerTabActivityTest() { + super(ViewPagerTabActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + } + + public void testScroll() throws Throwable { + for (int i = 0; i < 3; i++) { + UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.LEFT); + getInstrumentation().waitForIdleSync(); + scroll(); + } + for (int i = 0; i < 3; i++) { + UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.RIGHT); + getInstrumentation().waitForIdleSync(); + scroll(); + } + } + + public void scroll() throws Throwable { + View scrollable = activity.findViewById(R.id.scroll); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + public void testSaveAndRestoreInstanceState() throws Throwable { + for (int i = 0; i < 3; i++) { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + scroll(); + + UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.LEFT); + getInstrumentation().waitForIdleSync(); + } + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabListViewFragment.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabListViewFragment.java new file mode 100644 index 00000000..447e54b2 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabListViewFragment.java @@ -0,0 +1,59 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; + +public class ViewPagerTabListViewFragment extends Fragment { + + public static final String ARG_INITIAL_POSITION = "ARG_INITIAL_POSITION"; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_listview, container, false); + + Activity parentActivity = getActivity(); + final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll); + UiTestUtils.setDummyDataWithHeader(getActivity(), listView, inflater.inflate(R.layout.padding, null)); + + if (parentActivity instanceof ObservableScrollViewCallbacks) { + // Scroll to the specified position after layout + Bundle args = getArguments(); + if (args != null && args.containsKey(ARG_INITIAL_POSITION)) { + final int initialPosition = args.getInt(ARG_INITIAL_POSITION, 0); + ScrollUtils.addOnGlobalLayoutListener(listView, new Runnable() { + @Override + public void run() { + // scrollTo() doesn't work, should use setSelection() + listView.setSelection(initialPosition); + } + }); + } + listView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); + } + return view; + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabRecyclerViewFragment.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabRecyclerViewFragment.java new file mode 100644 index 00000000..f91988e8 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabRecyclerViewFragment.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.LinearLayoutManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; + +public class ViewPagerTabRecyclerViewFragment extends Fragment { + + public static final String ARG_INITIAL_POSITION = "ARG_INITIAL_POSITION"; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_recyclerview, container, false); + + Activity parentActivity = getActivity(); + final ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll); + recyclerView.setLayoutManager(new LinearLayoutManager(parentActivity)); + recyclerView.setHasFixedSize(false); + View headerView = LayoutInflater.from(parentActivity).inflate(R.layout.padding, null); + UiTestUtils.setDummyDataWithHeader(getActivity(), recyclerView, headerView); + + if (parentActivity instanceof ObservableScrollViewCallbacks) { + // Scroll to the specified offset after layout + Bundle args = getArguments(); + if (args != null && args.containsKey(ARG_INITIAL_POSITION)) { + final int initialPosition = args.getInt(ARG_INITIAL_POSITION, 0); + ScrollUtils.addOnGlobalLayoutListener(recyclerView, new Runnable() { + @Override + public void run() { + recyclerView.scrollVerticallyToPosition(initialPosition); + } + }); + } + recyclerView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); + } + return view; + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabScrollViewFragment.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabScrollViewFragment.java new file mode 100644 index 00000000..16c62658 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabScrollViewFragment.java @@ -0,0 +1,56 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; + +public class ViewPagerTabScrollViewFragment extends Fragment { + + public static final String ARG_SCROLL_Y = "ARG_SCROLL_Y"; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_scrollview, container, false); + + final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll); + Activity parentActivity = getActivity(); + if (parentActivity instanceof ObservableScrollViewCallbacks) { + // Scroll to the specified offset after layout + Bundle args = getArguments(); + if (args != null && args.containsKey(ARG_SCROLL_Y)) { + final int scrollY = args.getInt(ARG_SCROLL_Y, 0); + ScrollUtils.addOnGlobalLayoutListener(scrollView, new Runnable() { + @Override + public void run() { + scrollView.scrollTo(0, scrollY); + } + }); + } + scrollView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); + } + return view; + } +} diff --git a/observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java b/observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java new file mode 100644 index 00000000..06f60f7a --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java @@ -0,0 +1,321 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.iosched.ui.widget; + +import android.content.Context; +import android.graphics.Typeface; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * To be used with ViewPager to provide a tab indicator component which give constant feedback as to + * the user's scroll progress. + *

+ * To use the component, simply add it to your view hierarchy. Then in your + * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call + * {@link #setViewPager(android.support.v4.view.ViewPager)} providing it the ViewPager this layout is being used for. + *

+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors + * via {@link #setSelectedIndicatorColors(int...)}. The + * alternative is via the {@link com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer} interface which provides you complete control over + * which color is used for any individual position. + *

+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, + * providing the layout ID of your custom layout. + */ +public class SlidingTabLayout extends HorizontalScrollView { + /** + * Allows complete control over the colors drawn in the tab layout. Set with + * {@link #setCustomTabColorizer(com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer)}. + */ + public interface TabColorizer { + + /** + * @return return the color of the indicator used when {@code position} is selected. + */ + int getIndicatorColor(int position); + + } + + private static final int TITLE_OFFSET_DIPS = 24; + private static final int TAB_VIEW_PADDING_DIPS = 16; + private static final int TAB_VIEW_TEXT_SIZE_SP = 12; + + private int mTitleOffset; + + private int mTabViewLayoutId; + private int mTabViewTextViewId; + private boolean mDistributeEvenly; + + private ViewPager mViewPager; + private SparseArray mContentDescriptions = new SparseArray(); + private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; + + private final SlidingTabStrip mTabStrip; + + public SlidingTabLayout(Context context) { + this(context, null); + } + + public SlidingTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + // Disable the Scroll Bar + setHorizontalScrollBarEnabled(false); + // Make sure that the Tab Strips fills this View + setFillViewport(true); + + mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); + + mTabStrip = new SlidingTabStrip(context); + addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + } + + /** + * Set the custom {@link com.google.samples.apps.iosched.ui.widget.SlidingTabLayout.TabColorizer} to be used. + * + * If you only require simple custmisation then you can use + * {@link #setSelectedIndicatorColors(int...)} to achieve + * similar effects. + */ + public void setCustomTabColorizer(TabColorizer tabColorizer) { + mTabStrip.setCustomTabColorizer(tabColorizer); + } + + public void setDistributeEvenly(boolean distributeEvenly) { + mDistributeEvenly = distributeEvenly; + } + + /** + * Sets the colors to be used for indicating the selected tab. These colors are treated as a + * circular array. Providing one color will mean that all tabs are indicated with the same color. + */ + public void setSelectedIndicatorColors(int... colors) { + mTabStrip.setSelectedIndicatorColors(colors); + } + + /** + * Set the {@link android.support.v4.view.ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are + * required to set any {@link android.support.v4.view.ViewPager.OnPageChangeListener} through this method. This is so + * that the layout can update it's scroll position correctly. + * + * @see android.support.v4.view.ViewPager#setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener) + */ + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + mViewPagerPageChangeListener = listener; + } + + /** + * Set the custom layout to be inflated for the tab views. + * + * @param layoutResId Layout id to be inflated + * @param textViewId id of the {@link android.widget.TextView} in the inflated view + */ + public void setCustomTabView(int layoutResId, int textViewId) { + mTabViewLayoutId = layoutResId; + mTabViewTextViewId = textViewId; + } + + /** + * Sets the associated view pager. Note that the assumption here is that the pager content + * (number of tabs and tab titles) does not change after this call has been made. + */ + public void setViewPager(ViewPager viewPager) { + mTabStrip.removeAllViews(); + + mViewPager = viewPager; + if (viewPager != null) { + viewPager.setOnPageChangeListener(new InternalViewPagerListener()); + populateTabStrip(); + } + } + + /** + * Create a default view to be used for tabs. This is called if a custom tab view is not set via + * {@link #setCustomTabView(int, int)}. + */ + protected TextView createDefaultTabView(Context context) { + TextView textView = new TextView(context); + textView.setGravity(Gravity.CENTER); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); + textView.setTypeface(Typeface.DEFAULT_BOLD); + textView.setLayoutParams(new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + TypedValue outValue = new TypedValue(); + getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, + outValue, true); + textView.setBackgroundResource(outValue.resourceId); + textView.setAllCaps(true); + + int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); + textView.setPadding(padding, padding, padding, padding); + + return textView; + } + + private void populateTabStrip() { + final PagerAdapter adapter = mViewPager.getAdapter(); + final OnClickListener tabClickListener = new TabClickListener(); + + for (int i = 0; i < adapter.getCount(); i++) { + View tabView = null; + TextView tabTitleView = null; + + if (mTabViewLayoutId != 0) { + // If there is a custom tab view layout id set, try and inflate it + tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, + false); + tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); + } + + if (tabView == null) { + tabView = createDefaultTabView(getContext()); + } + + if (tabTitleView == null && TextView.class.isInstance(tabView)) { + tabTitleView = (TextView) tabView; + } + + if (mDistributeEvenly) { + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams(); + lp.width = 0; + lp.weight = 1; + } + + tabTitleView.setText(adapter.getPageTitle(i)); + tabView.setOnClickListener(tabClickListener); + String desc = mContentDescriptions.get(i, null); + if (desc != null) { + tabView.setContentDescription(desc); + } + + mTabStrip.addView(tabView); + if (i == mViewPager.getCurrentItem()) { + tabView.setSelected(true); + } + } + } + + public void setContentDescription(int i, String desc) { + mContentDescriptions.put(i, desc); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mViewPager != null) { + scrollToTab(mViewPager.getCurrentItem(), 0); + } + } + + private void scrollToTab(int tabIndex, int positionOffset) { + final int tabStripChildCount = mTabStrip.getChildCount(); + if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { + return; + } + + View selectedChild = mTabStrip.getChildAt(tabIndex); + if (selectedChild != null) { + int targetScrollX = selectedChild.getLeft() + positionOffset; + + if (tabIndex > 0 || positionOffset > 0) { + // If we're not at the first child and are mid-scroll, make sure we obey the offset + targetScrollX -= mTitleOffset; + } + + scrollTo(targetScrollX, 0); + } + } + + private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { + private int mScrollState; + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + int tabStripChildCount = mTabStrip.getChildCount(); + if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { + return; + } + + mTabStrip.onViewPagerPageChanged(position, positionOffset); + + View selectedTitle = mTabStrip.getChildAt(position); + int extraOffset = (selectedTitle != null) + ? (int) (positionOffset * selectedTitle.getWidth()) + : 0; + scrollToTab(position, extraOffset); + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, + positionOffsetPixels); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + mScrollState = state; + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageSelected(int position) { + if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { + mTabStrip.onViewPagerPageChanged(position, 0f); + scrollToTab(position, 0); + } + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + mTabStrip.getChildAt(i).setSelected(position == i); + } + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageSelected(position); + } + } + + } + + private class TabClickListener implements OnClickListener { + @Override + public void onClick(View v) { + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + if (v == mTabStrip.getChildAt(i)) { + mViewPager.setCurrentItem(i); + return; + } + } + } + } + +} diff --git a/observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java b/observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java new file mode 100644 index 00000000..803c4fb4 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java @@ -0,0 +1,168 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.iosched.ui.widget; + +import android.R; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.LinearLayout; + +class SlidingTabStrip extends LinearLayout { + + private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0; + private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; + private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3; + private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5; + + private final int mBottomBorderThickness; + private final Paint mBottomBorderPaint; + + private final int mSelectedIndicatorThickness; + private final Paint mSelectedIndicatorPaint; + + private final int mDefaultBottomBorderColor; + + private int mSelectedPosition; + private float mSelectionOffset; + + private SlidingTabLayout.TabColorizer mCustomTabColorizer; + private final SimpleTabColorizer mDefaultTabColorizer; + + SlidingTabStrip(Context context) { + this(context, null); + } + + SlidingTabStrip(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + + final float density = getResources().getDisplayMetrics().density; + + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true); + final int themeForegroundColor = outValue.data; + + mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, + DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); + + mDefaultTabColorizer = new SimpleTabColorizer(); + mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR); + + mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); + mBottomBorderPaint = new Paint(); + mBottomBorderPaint.setColor(mDefaultBottomBorderColor); + + mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); + mSelectedIndicatorPaint = new Paint(); + } + + void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { + mCustomTabColorizer = customTabColorizer; + invalidate(); + } + + void setSelectedIndicatorColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setIndicatorColors(colors); + invalidate(); + } + + void onViewPagerPageChanged(int position, float positionOffset) { + mSelectedPosition = position; + mSelectionOffset = positionOffset; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + final int height = getHeight(); + final int childCount = getChildCount(); + final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null + ? mCustomTabColorizer + : mDefaultTabColorizer; + + // Thick colored underline below the current selection + if (childCount > 0) { + View selectedTitle = getChildAt(mSelectedPosition); + int left = selectedTitle.getLeft(); + int right = selectedTitle.getRight(); + int color = tabColorizer.getIndicatorColor(mSelectedPosition); + + if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { + int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); + if (color != nextColor) { + color = blendColors(nextColor, color, mSelectionOffset); + } + + // Draw the selection partway between the tabs + View nextTitle = getChildAt(mSelectedPosition + 1); + left = (int) (mSelectionOffset * nextTitle.getLeft() + + (1.0f - mSelectionOffset) * left); + right = (int) (mSelectionOffset * nextTitle.getRight() + + (1.0f - mSelectionOffset) * right); + } + + mSelectedIndicatorPaint.setColor(color); + + canvas.drawRect(left, height - mSelectedIndicatorThickness, right, + height, mSelectedIndicatorPaint); + } + + // Thin underline along the entire bottom edge + canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); + } + + /** + * Set the alpha value of the {@code color} to be the given {@code alpha} value. + */ + private static int setColorAlpha(int color, byte alpha) { + return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + /** + * Blend {@code color1} and {@code color2} using the given ratio. + * + * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, + * 0.0 will return {@code color2}. + */ + private static int blendColors(int color1, int color2, float ratio) { + final float inverseRation = 1f - ratio; + float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); + float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); + float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); + return Color.rgb((int) r, (int) g, (int) b); + } + + private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { + private int[] mIndicatorColors; + + @Override + public final int getIndicatorColor(int position) { + return mIndicatorColors[position % mIndicatorColors.length]; + } + + void setIndicatorColors(int... colors) { + mIndicatorColors = colors; + } + } +} diff --git a/observablescrollview/src/androidTest/res/color/tab_text_color.xml b/observablescrollview/src/androidTest/res/color/tab_text_color.xml new file mode 100644 index 00000000..48f21f86 --- /dev/null +++ b/observablescrollview/src/androidTest/res/color/tab_text_color.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/observablescrollview/src/androidTest/res/layout/activity_viewpagertab.xml b/observablescrollview/src/androidTest/res/layout/activity_viewpagertab.xml new file mode 100644 index 00000000..c55804bd --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_viewpagertab.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + diff --git a/observablescrollview/src/androidTest/res/layout/fragment_listview.xml b/observablescrollview/src/androidTest/res/layout/fragment_listview.xml new file mode 100644 index 00000000..08d5d41b --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/fragment_listview.xml @@ -0,0 +1,19 @@ + + diff --git a/observablescrollview/src/androidTest/res/layout/fragment_recyclerview.xml b/observablescrollview/src/androidTest/res/layout/fragment_recyclerview.xml new file mode 100644 index 00000000..49f09590 --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/fragment_recyclerview.xml @@ -0,0 +1,20 @@ + + diff --git a/observablescrollview/src/androidTest/res/layout/fragment_scrollview.xml b/observablescrollview/src/androidTest/res/layout/fragment_scrollview.xml new file mode 100644 index 00000000..0be95c85 --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/fragment_scrollview.xml @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/observablescrollview/src/androidTest/res/layout/padding.xml b/observablescrollview/src/androidTest/res/layout/padding.xml new file mode 100644 index 00000000..b3550e37 --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/padding.xml @@ -0,0 +1,19 @@ + + diff --git a/observablescrollview/src/androidTest/res/layout/tab_indicator.xml b/observablescrollview/src/androidTest/res/layout/tab_indicator.xml new file mode 100644 index 00000000..3bf50059 --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/tab_indicator.xml @@ -0,0 +1,25 @@ + + \ No newline at end of file diff --git a/observablescrollview/src/androidTest/res/values/colors.xml b/observablescrollview/src/androidTest/res/values/colors.xml new file mode 100644 index 00000000..221c75c7 --- /dev/null +++ b/observablescrollview/src/androidTest/res/values/colors.xml @@ -0,0 +1,22 @@ + + + + #009688 + #00796b + #eeff41 + + diff --git a/observablescrollview/src/androidTest/res/values/dimens.xml b/observablescrollview/src/androidTest/res/values/dimens.xml new file mode 100644 index 00000000..f16a5655 --- /dev/null +++ b/observablescrollview/src/androidTest/res/values/dimens.xml @@ -0,0 +1,18 @@ + + + 48dp + diff --git a/observablescrollview/src/androidTest/res/values/styles.xml b/observablescrollview/src/androidTest/res/values/styles.xml new file mode 100644 index 00000000..4c1781a1 --- /dev/null +++ b/observablescrollview/src/androidTest/res/values/styles.xml @@ -0,0 +1,36 @@ + + + + + + + + + + From 444701397f6ccb9572551367808a88250032569d Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 25 Jan 2015 22:50:16 +0900 Subject: [PATCH 010/208] Add more tests. (coverage is about 74%) --- .../src/androidTest/AndroidManifest.xml | 2 + .../test/GridViewActivity.java | 11 +- .../test/GridViewActivityTest.java | 21 ++ .../test/ListViewActivity.java | 10 + .../test/ListViewActivityTest.java | 21 ++ .../ListViewScrollFromBottomActivity.java | 24 ++ .../ListViewScrollFromBottomActivityTest.java | 32 ++ .../test/RecyclerViewActivityTest.java | 21 ++ .../test/UiTestUtils.java | 5 +- .../test/ViewPagerTab2Activity.java | 291 ++++++++++++++++++ .../test/ViewPagerTab2ActivityTest.java | 53 ++++ .../test/ViewPagerTab2GridViewFragment.java | 44 +++ .../test/ViewPagerTab2ListViewFragment.java | 44 +++ .../ViewPagerTab2RecyclerViewFragment.java | 47 +++ .../test/ViewPagerTab2ScrollViewFragment.java | 42 +++ .../test/ViewPagerTab2WebViewFragment.java | 43 +++ .../test/ViewPagerTabActivity.java | 14 +- .../test/ViewPagerTabActivityTest.java | 7 +- .../test/WebViewActivityTest.java | 21 ++ .../res/layout/activity_viewpagertab2.xml | 56 ++++ .../res/layout/fragment_gridview.xml | 20 ++ .../layout/fragment_scrollview_noheader.xml | 35 +++ .../res/layout/fragment_webview.xml | 19 ++ 23 files changed, 874 insertions(+), 9 deletions(-) create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivityTest.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2Activity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ActivityTest.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2GridViewFragment.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ListViewFragment.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2RecyclerViewFragment.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ScrollViewFragment.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2WebViewFragment.java create mode 100644 observablescrollview/src/androidTest/res/layout/activity_viewpagertab2.xml create mode 100644 observablescrollview/src/androidTest/res/layout/fragment_gridview.xml create mode 100644 observablescrollview/src/androidTest/res/layout/fragment_scrollview_noheader.xml create mode 100644 observablescrollview/src/androidTest/res/layout/fragment_webview.xml diff --git a/observablescrollview/src/androidTest/AndroidManifest.xml b/observablescrollview/src/androidTest/AndroidManifest.xml index ab6745cf..cc8a8bd5 100644 --- a/observablescrollview/src/androidTest/AndroidManifest.xml +++ b/observablescrollview/src/androidTest/AndroidManifest.xml @@ -21,9 +21,11 @@ + + diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java index e4b3e5fc..111b616f 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java @@ -2,9 +2,9 @@ import android.app.Activity; import android.os.Bundle; +import android.widget.AbsListView; import com.github.ksoichiro.android.observablescrollview.ObservableGridView; -import com.github.ksoichiro.android.observablescrollview.ObservableListView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; import com.github.ksoichiro.android.observablescrollview.ScrollState; @@ -17,6 +17,15 @@ protected void onCreate(Bundle savedInstanceState) { ObservableGridView scrollable = (ObservableGridView) findViewById(R.id.scrollable); scrollable.setScrollViewCallbacks(this); UiTestUtils.setDummyData(this, scrollable); + scrollable.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + } + }); } @Override diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java index cf114c87..7a08f350 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java @@ -2,6 +2,8 @@ import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; +import android.util.DisplayMetrics; +import android.util.TypedValue; import com.github.ksoichiro.android.observablescrollview.ObservableGridView; @@ -34,4 +36,23 @@ public void testSaveAndRestoreInstanceState() throws Throwable { UiTestUtils.saveAndRestoreInstanceState(this, activity); testScroll(); } + + public void testScrollVerticallyTo() throws Throwable { + final DisplayMetrics metrics = activity.getResources().getDisplayMetrics(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.scrollVerticallyTo((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, metrics)); + } + }); + getInstrumentation().waitForIdleSync(); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.scrollVerticallyTo(0); + } + }); + getInstrumentation().waitForIdleSync(); + } } diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java index e91c3373..5d6dc796 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java @@ -2,6 +2,7 @@ import android.app.Activity; import android.os.Bundle; +import android.widget.AbsListView; import com.github.ksoichiro.android.observablescrollview.ObservableListView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; @@ -16,6 +17,15 @@ protected void onCreate(Bundle savedInstanceState) { ObservableListView scrollable = (ObservableListView) findViewById(R.id.scrollable); scrollable.setScrollViewCallbacks(this); UiTestUtils.setDummyData(this, scrollable); + scrollable.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + } + }); } @Override diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java index f17a5012..188fbc4f 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java @@ -2,6 +2,8 @@ import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; +import android.util.DisplayMetrics; +import android.util.TypedValue; import com.github.ksoichiro.android.observablescrollview.ObservableListView; @@ -34,4 +36,23 @@ public void testSaveAndRestoreInstanceState() throws Throwable { UiTestUtils.saveAndRestoreInstanceState(this, activity); testScroll(); } + + public void testScrollVerticallyTo() throws Throwable { + final DisplayMetrics metrics = activity.getResources().getDisplayMetrics(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.scrollVerticallyTo((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, metrics)); + } + }); + getInstrumentation().waitForIdleSync(); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.scrollVerticallyTo(0); + } + }); + getInstrumentation().waitForIdleSync(); + } } diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivity.java new file mode 100644 index 00000000..6a3509b2 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivity.java @@ -0,0 +1,24 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.os.Bundle; + +import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; + +public class ListViewScrollFromBottomActivity extends ListViewActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final ObservableListView scrollable = (ObservableListView) findViewById(R.id.scrollable); + ScrollUtils.addOnGlobalLayoutListener(scrollable, new Runnable() { + @Override + public void run() { + int count = scrollable.getAdapter().getCount() - 1; + int position = count == 0 ? 1 : count > 0 ? count : 0; + scrollable.smoothScrollToPosition(position); + scrollable.setSelection(position); + } + }); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivityTest.java new file mode 100644 index 00000000..db8b465b --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivityTest.java @@ -0,0 +1,32 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; + +import com.github.ksoichiro.android.observablescrollview.ObservableListView; + +public class ListViewScrollFromBottomActivityTest extends ActivityInstrumentationTestCase2 { + + private Activity activity; + private ObservableListView scrollable; + + public ListViewScrollFromBottomActivityTest() { + super(ListViewScrollFromBottomActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableListView) activity.findViewById(R.id.scrollable); + } + + public void testScroll() throws Throwable { + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java index 37f6f396..33335ed0 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java @@ -2,6 +2,8 @@ import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; +import android.util.DisplayMetrics; +import android.util.TypedValue; import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; @@ -35,4 +37,23 @@ public void testSaveAndRestoreInstanceState() throws Throwable { UiTestUtils.saveAndRestoreInstanceState(this, activity); testScroll(); } + + public void testScrollVerticallyTo() throws Throwable { + final DisplayMetrics metrics = activity.getResources().getDisplayMetrics(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.scrollVerticallyTo((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, metrics)); + } + }); + getInstrumentation().waitForIdleSync(); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.scrollVerticallyTo(0); + } + }); + getInstrumentation().waitForIdleSync(); + } } diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java index 623553ed..43ebbf84 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java @@ -18,6 +18,7 @@ public class UiTestUtils { private static final int NUM_OF_ITEMS = 100; private static final int NUM_OF_ITEMS_FEW = 3; + private static final int DRAG_STEP_COUNT = 50; public enum Direction { LEFT, RIGHT, UP, DOWN @@ -53,7 +54,7 @@ public static void swipeHorizontally(InstrumentationTestCase test, View v, Direc float xEnd = xy[0] + ((direction == Direction.LEFT) ? distanceFromEdge : (viewWidth - distanceFromEdge)); float y = xy[1] + (viewHeight / 2.0f); - TouchUtils.drag(test, xStart, xEnd, y, y, 100); + TouchUtils.drag(test, xStart, xEnd, y, y, DRAG_STEP_COUNT); } public static void swipeVertically(InstrumentationTestCase test, View v, Direction direction) { @@ -69,7 +70,7 @@ public static void swipeVertically(InstrumentationTestCase test, View v, Directi float yStart = xy[1] + ((direction == Direction.UP) ? (viewHeight - distanceFromEdge) : distanceFromEdge); float yEnd = xy[1] + ((direction == Direction.UP) ? distanceFromEdge : (viewHeight - distanceFromEdge)); - TouchUtils.drag(test, x, x, yStart, yEnd, 100); + TouchUtils.drag(test, x, x, yStart, yEnd, DRAG_STEP_COUNT); } public static ArrayList getDummyData() { diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2Activity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2Activity.java new file mode 100644 index 00000000..ecde2777 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2Activity.java @@ -0,0 +1,291 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.test; + +import android.content.res.TypedArray; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.Toolbar; +import android.util.TypedValue; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.FrameLayout; + +import com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.github.ksoichiro.android.observablescrollview.Scrollable; +import com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout; +import com.google.samples.apps.iosched.ui.widget.SlidingTabLayout; +import com.nineoldandroids.animation.ValueAnimator; +import com.nineoldandroids.view.ViewHelper; + +public class ViewPagerTab2Activity extends ActionBarActivity implements ObservableScrollViewCallbacks { + + private View mToolbarView; + private TouchInterceptionFrameLayout mInterceptionLayout; + private ViewPager mPager; + private NavigationAdapter mPagerAdapter; + private int mSlop; + private boolean mScrolled; + private ScrollState mLastScrollState; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_viewpagertab2); + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + + mToolbarView = findViewById(R.id.toolbar); + mPagerAdapter = new NavigationAdapter(getSupportFragmentManager()); + mPager = (ViewPager) findViewById(R.id.pager); + mPager.setAdapter(mPagerAdapter); + // Padding for ViewPager must be set outside the ViewPager itself + // because with padding, EdgeEffect of ViewPager become strange. + final int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); + findViewById(R.id.pager_wrapper).setPadding(0, getActionBarSize() + tabHeight, 0, 0); + + SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs); + slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1); + slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent)); + slidingTabLayout.setDistributeEvenly(true); + slidingTabLayout.setViewPager(mPager); + + ViewConfiguration vc = ViewConfiguration.get(this); + mSlop = vc.getScaledTouchSlop(); + mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.container); + mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + if (!mScrolled) { + // This event can be used only when TouchInterceptionFrameLayout + // doesn't handle the consecutive events. + adjustToolbar(scrollState); + } + } + + private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() { + @Override + public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) { + if (!mScrolled && mSlop < Math.abs(diffX) && Math.abs(diffY) < Math.abs(diffX)) { + // Horizontal scroll is maybe handled by ViewPager + return false; + } + + Scrollable scrollable = getCurrentScrollable(); + if (scrollable == null) { + mScrolled = false; + return false; + } + + // If interceptionLayout can move, it should intercept. + // And once it begins to move, horizontal scroll shouldn't work any longer. + int toolbarHeight = mToolbarView.getHeight(); + int translationY = (int) ViewHelper.getTranslationY(mInterceptionLayout); + boolean scrollingUp = 0 < diffY; + boolean scrollingDown = diffY < 0; + if (scrollingUp) { + if (translationY < 0) { + mScrolled = true; + mLastScrollState = ScrollState.UP; + return true; + } + } else if (scrollingDown) { + if (-toolbarHeight < translationY) { + mScrolled = true; + mLastScrollState = ScrollState.DOWN; + return true; + } + } + mScrolled = false; + return false; + } + + @Override + public void onDownMotionEvent(MotionEvent ev) { + } + + @Override + public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) { + float translationY = ScrollUtils.getFloat(ViewHelper.getTranslationY(mInterceptionLayout) + diffY, -mToolbarView.getHeight(), 0); + ViewHelper.setTranslationY(mInterceptionLayout, translationY); + if (translationY < 0) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams(); + lp.height = (int) (-translationY + getScreenHeight()); + mInterceptionLayout.requestLayout(); + } + } + + @Override + public void onUpOrCancelMotionEvent(MotionEvent ev) { + mScrolled = false; + adjustToolbar(mLastScrollState); + } + }; + + public Scrollable getCurrentScrollable() { + Fragment fragment = getCurrentFragment(); + if (fragment == null) { + return null; + } + View view = fragment.getView(); + if (view == null) { + return null; + } + return (Scrollable) view.findViewById(R.id.scroll); + } + + private void adjustToolbar(ScrollState scrollState) { + int toolbarHeight = mToolbarView.getHeight(); + final Scrollable scrollable = getCurrentScrollable(); + if (scrollable == null) { + return; + } + int scrollY = scrollable.getCurrentScrollY(); + if (scrollState == ScrollState.DOWN) { + showToolbar(); + } else if (scrollState == ScrollState.UP) { + if (toolbarHeight <= scrollY) { + hideToolbar(); + } else { + showToolbar(); + } + } else if (!toolbarIsShown() && !toolbarIsHidden()) { + // Toolbar is moving but doesn't know which to move: + // you can change this to hideToolbar() + showToolbar(); + } + } + + private Fragment getCurrentFragment() { + return mPagerAdapter.getItemAt(mPager.getCurrentItem()); + } + + private boolean toolbarIsShown() { + return ViewHelper.getTranslationY(mInterceptionLayout) == 0; + } + + private boolean toolbarIsHidden() { + return ViewHelper.getTranslationY(mInterceptionLayout) == -mToolbarView.getHeight(); + } + + private void showToolbar() { + animateToolbar(0); + } + + private void hideToolbar() { + animateToolbar(-mToolbarView.getHeight()); + } + + private void animateToolbar(final float toY) { + float layoutTranslationY = ViewHelper.getTranslationY(mInterceptionLayout); + if (layoutTranslationY != toY) { + ValueAnimator animator = ValueAnimator.ofFloat(ViewHelper.getTranslationY(mInterceptionLayout), toY).setDuration(200); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float translationY = (float) animation.getAnimatedValue(); + ViewHelper.setTranslationY(mInterceptionLayout, translationY); + if (translationY < 0) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams(); + lp.height = (int) (-translationY + getScreenHeight()); + mInterceptionLayout.requestLayout(); + } + } + }); + animator.start(); + } + } + + private int getActionBarSize() { + TypedValue typedValue = new TypedValue(); + int[] textSizeAttr = new int[]{R.attr.actionBarSize}; + int indexOfAttrTextSize = 0; + TypedArray a = obtainStyledAttributes(typedValue.data, textSizeAttr); + int actionBarSize = a.getDimensionPixelSize(indexOfAttrTextSize, -1); + a.recycle(); + return actionBarSize; + } + + private int getScreenHeight() { + return findViewById(android.R.id.content).getHeight(); + } + + /** + * This adapter provides two types of fragments as an example. + * {@linkplain #createItem(int)} should be modified if you use this example for your app. + */ + private static class NavigationAdapter extends CacheFragmentStatePagerAdapter { + + private static final String[] TITLES = new String[]{"Applepie", "Butter Cookie", "Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb", "Ice Cream Sandwich", "Jelly Bean", "KitKat", "Lollipop"}; + + public NavigationAdapter(FragmentManager fm) { + super(fm); + } + + @Override + protected Fragment createItem(int position) { + Fragment f; + final int pattern = position % 5; + switch (pattern) { + case 0: + f = new ViewPagerTab2ScrollViewFragment(); + break; + case 1: + f = new ViewPagerTab2ListViewFragment(); + break; + case 2: + f = new ViewPagerTab2RecyclerViewFragment(); + break; + case 3: + f = new ViewPagerTab2GridViewFragment(); + break; + case 4: + default: + f = new ViewPagerTab2WebViewFragment(); + break; + } + return f; + } + + @Override + public int getCount() { + return TITLES.length; + } + + @Override + public CharSequence getPageTitle(int position) { + return TITLES[position]; + } + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ActivityTest.java new file mode 100644 index 00000000..e1dc35a6 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ActivityTest.java @@ -0,0 +1,53 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.test.ActivityInstrumentationTestCase2; +import android.view.View; + +public class ViewPagerTab2ActivityTest extends ActivityInstrumentationTestCase2 { + + private ViewPagerTab2Activity activity; + + public ViewPagerTab2ActivityTest() { + super(ViewPagerTab2Activity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + } + + public void testScroll() throws Throwable { + for (int i = 0; i < 5; i++) { + UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.LEFT); + getInstrumentation().waitForIdleSync(); + scroll(); + } + for (int i = 0; i < 5; i++) { + UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.RIGHT); + getInstrumentation().waitForIdleSync(); + scroll(); + } + } + + public void scroll() throws Throwable { + View scrollable = ((View) activity.getCurrentScrollable()).findViewById(R.id.scroll); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + public void testSaveAndRestoreInstanceState() throws Throwable { + for (int i = 0; i < 5; i++) { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + scroll(); + + UiTestUtils.swipeHorizontally(this, activity.findViewById(R.id.pager), UiTestUtils.Direction.LEFT); + getInstrumentation().waitForIdleSync(); + } + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2GridViewFragment.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2GridViewFragment.java new file mode 100644 index 00000000..401752a9 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2GridViewFragment.java @@ -0,0 +1,44 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; + +public class ViewPagerTab2GridViewFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_gridview, container, false); + + Activity parentActivity = getActivity(); + final ObservableGridView gridView = (ObservableGridView) view.findViewById(R.id.scroll); + UiTestUtils.setDummyData(getActivity(), gridView); + gridView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container)); + + if (parentActivity instanceof ObservableScrollViewCallbacks) { + gridView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); + } + return view; + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ListViewFragment.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ListViewFragment.java new file mode 100644 index 00000000..33daf0c8 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ListViewFragment.java @@ -0,0 +1,44 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; + +public class ViewPagerTab2ListViewFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_listview, container, false); + + Activity parentActivity = getActivity(); + final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll); + UiTestUtils.setDummyData(getActivity(), listView); + listView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container)); + + if (parentActivity instanceof ObservableScrollViewCallbacks) { + listView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); + } + return view; + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2RecyclerViewFragment.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2RecyclerViewFragment.java new file mode 100644 index 00000000..1e20ed93 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2RecyclerViewFragment.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.LinearLayoutManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; + +public class ViewPagerTab2RecyclerViewFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_recyclerview, container, false); + + Activity parentActivity = getActivity(); + final ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll); + recyclerView.setLayoutManager(new LinearLayoutManager(parentActivity)); + recyclerView.setHasFixedSize(false); + UiTestUtils.setDummyData(getActivity(), recyclerView); + recyclerView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container)); + + if (parentActivity instanceof ObservableScrollViewCallbacks) { + recyclerView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); + } + return view; + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ScrollViewFragment.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ScrollViewFragment.java new file mode 100644 index 00000000..286e96e5 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ScrollViewFragment.java @@ -0,0 +1,42 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; + +public class ViewPagerTab2ScrollViewFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_scrollview_noheader, container, false); + + final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll); + Activity parentActivity = getActivity(); + scrollView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container)); + if (parentActivity instanceof ObservableScrollViewCallbacks) { + scrollView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); + } + return view; + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2WebViewFragment.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2WebViewFragment.java new file mode 100644 index 00000000..582873f3 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2WebViewFragment.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ObservableWebView; + +public class ViewPagerTab2WebViewFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_webview, container, false); + + final ObservableWebView webView = (ObservableWebView) view.findViewById(R.id.scroll); + webView.loadUrl("file:///android_asset/lipsum.html"); + Activity parentActivity = getActivity(); + webView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.container)); + if (parentActivity instanceof ObservableScrollViewCallbacks) { + webView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); + } + return view; + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java index 475bb518..39153790 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java @@ -101,6 +101,18 @@ public void onUpOrCancelMotionEvent(ScrollState scrollState) { adjustToolbar(scrollState, view); } + public Scrollable getCurrentScrollable() { + Fragment fragment = getCurrentFragment(); + if (fragment == null) { + return null; + } + View view = fragment.getView(); + if (view == null) { + return null; + } + return (Scrollable) view.findViewById(R.id.scroll); + } + private void adjustToolbar(ScrollState scrollState, View view) { int toolbarHeight = mToolbarView.getHeight(); final Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll); @@ -130,7 +142,7 @@ private void adjustToolbar(ScrollState scrollState, View view) { } } - private Fragment getCurrentFragment() { + public Fragment getCurrentFragment() { return mPagerAdapter.getItemAt(mPager.getCurrentItem()); } diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java index 29dd761d..6125d6ff 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java @@ -1,14 +1,11 @@ package com.github.ksoichiro.android.observablescrollview.test; -import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; import android.view.View; -import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; - public class ViewPagerTabActivityTest extends ActivityInstrumentationTestCase2 { - private Activity activity; + private ViewPagerTabActivity activity; public ViewPagerTabActivityTest() { super(ViewPagerTabActivity.class); @@ -35,7 +32,7 @@ public void testScroll() throws Throwable { } public void scroll() throws Throwable { - View scrollable = activity.findViewById(R.id.scroll); + View scrollable = ((View) activity.getCurrentScrollable()).findViewById(R.id.scroll); UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); getInstrumentation().waitForIdleSync(); diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java index c44c1db2..d6cc295e 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java @@ -2,6 +2,8 @@ import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; +import android.util.DisplayMetrics; +import android.util.TypedValue; import com.github.ksoichiro.android.observablescrollview.ObservableWebView; @@ -34,4 +36,23 @@ public void testSaveAndRestoreInstanceState() throws Throwable { UiTestUtils.saveAndRestoreInstanceState(this, activity); testScroll(); } + + public void testScrollVerticallyTo() throws Throwable { + final DisplayMetrics metrics = activity.getResources().getDisplayMetrics(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.scrollVerticallyTo((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, metrics)); + } + }); + getInstrumentation().waitForIdleSync(); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.scrollVerticallyTo(0); + } + }); + getInstrumentation().waitForIdleSync(); + } } diff --git a/observablescrollview/src/androidTest/res/layout/activity_viewpagertab2.xml b/observablescrollview/src/androidTest/res/layout/activity_viewpagertab2.xml new file mode 100644 index 00000000..510ed95c --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_viewpagertab2.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/observablescrollview/src/androidTest/res/layout/fragment_gridview.xml b/observablescrollview/src/androidTest/res/layout/fragment_gridview.xml new file mode 100644 index 00000000..65241a4d --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/fragment_gridview.xml @@ -0,0 +1,20 @@ + + diff --git a/observablescrollview/src/androidTest/res/layout/fragment_scrollview_noheader.xml b/observablescrollview/src/androidTest/res/layout/fragment_scrollview_noheader.xml new file mode 100644 index 00000000..2ac408dd --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/fragment_scrollview_noheader.xml @@ -0,0 +1,35 @@ + + + + + + + + diff --git a/observablescrollview/src/androidTest/res/layout/fragment_webview.xml b/observablescrollview/src/androidTest/res/layout/fragment_webview.xml new file mode 100644 index 00000000..b02d9ec3 --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/fragment_webview.xml @@ -0,0 +1,19 @@ + + From b3dc3274a5f04f12c0c6f8ce4df4598987a5b154 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 26 Jan 2015 19:36:09 +0900 Subject: [PATCH 011/208] Add more tests. (coverage is about 87%) --- .../src/androidTest/AndroidManifest.xml | 1 + .../observablescrollview/SavedStateTest.java | 129 ++++++++++++++++++ .../RecyclerViewScrollFromBottomActivity.java | 23 ++++ ...yclerViewScrollFromBottomActivityTest.java | 33 +++++ .../ObservableGridView.java | 2 +- .../ObservableListView.java | 2 +- .../ObservableRecyclerView.java | 2 +- .../ObservableScrollView.java | 2 +- .../ObservableWebView.java | 2 +- 9 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/SavedStateTest.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivityTest.java diff --git a/observablescrollview/src/androidTest/AndroidManifest.xml b/observablescrollview/src/androidTest/AndroidManifest.xml index cc8a8bd5..7b536b5e 100644 --- a/observablescrollview/src/androidTest/AndroidManifest.xml +++ b/observablescrollview/src/androidTest/AndroidManifest.xml @@ -23,6 +23,7 @@ + diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/SavedStateTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/SavedStateTest.java new file mode 100644 index 00000000..c98d0fa5 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/SavedStateTest.java @@ -0,0 +1,129 @@ +package com.github.ksoichiro.android.observablescrollview; + +import android.os.Parcel; +import android.test.InstrumentationTestCase; +import android.util.SparseIntArray; +import android.view.AbsSavedState; + +public class SavedStateTest extends InstrumentationTestCase { + + public void testGridViewSavedState() throws Throwable { + Parcel parcel = Parcel.obtain(); + ObservableGridView.SavedState state1 = new ObservableGridView.SavedState(AbsSavedState.EMPTY_STATE); + state1.prevFirstVisiblePosition = 1; + state1.prevFirstVisibleChildHeight = 2; + state1.prevScrolledChildrenHeight = 3; + state1.prevScrollY = 4; + state1.scrollY = 5; + state1.childrenHeights = new SparseIntArray(); + state1.childrenHeights.put(0, 10); + state1.childrenHeights.put(1, 20); + state1.childrenHeights.put(2, 30); + state1.writeToParcel(parcel, 0); + + parcel.setDataPosition(0); + + ObservableGridView.SavedState state2 = ObservableGridView.SavedState.CREATOR.createFromParcel(parcel); + assertNotNull(state2); + assertEquals(state1.prevFirstVisiblePosition, state2.prevFirstVisiblePosition); + assertEquals(state1.prevFirstVisibleChildHeight, state2.prevFirstVisibleChildHeight); + assertEquals(state1.prevScrolledChildrenHeight, state2.prevScrolledChildrenHeight); + assertEquals(state1.prevScrollY, state2.prevScrollY); + assertEquals(state1.scrollY, state2.scrollY); + assertNotNull(state1.childrenHeights); + assertEquals(3, state1.childrenHeights.size()); + assertEquals(10, state1.childrenHeights.get(0)); + assertEquals(20, state1.childrenHeights.get(1)); + assertEquals(30, state1.childrenHeights.get(2)); + } + + public void testListViewSavedState() throws Throwable { + Parcel parcel = Parcel.obtain(); + ObservableListView.SavedState state1 = new ObservableListView.SavedState(AbsSavedState.EMPTY_STATE); + state1.prevFirstVisiblePosition = 1; + state1.prevFirstVisibleChildHeight = 2; + state1.prevScrolledChildrenHeight = 3; + state1.prevScrollY = 4; + state1.scrollY = 5; + state1.childrenHeights = new SparseIntArray(); + state1.childrenHeights.put(0, 10); + state1.childrenHeights.put(1, 20); + state1.childrenHeights.put(2, 30); + state1.writeToParcel(parcel, 0); + + parcel.setDataPosition(0); + + ObservableListView.SavedState state2 = ObservableListView.SavedState.CREATOR.createFromParcel(parcel); + assertNotNull(state2); + assertEquals(state1.prevFirstVisiblePosition, state2.prevFirstVisiblePosition); + assertEquals(state1.prevFirstVisibleChildHeight, state2.prevFirstVisibleChildHeight); + assertEquals(state1.prevScrolledChildrenHeight, state2.prevScrolledChildrenHeight); + assertEquals(state1.prevScrollY, state2.prevScrollY); + assertEquals(state1.scrollY, state2.scrollY); + assertNotNull(state1.childrenHeights); + assertEquals(3, state1.childrenHeights.size()); + assertEquals(10, state1.childrenHeights.get(0)); + assertEquals(20, state1.childrenHeights.get(1)); + assertEquals(30, state1.childrenHeights.get(2)); + } + + public void testRecyclerViewSavedState() throws Throwable { + Parcel parcel = Parcel.obtain(); + ObservableRecyclerView.SavedState state1 = new ObservableRecyclerView.SavedState(AbsSavedState.EMPTY_STATE); + state1.prevFirstVisiblePosition = 1; + state1.prevFirstVisibleChildHeight = 2; + state1.prevScrolledChildrenHeight = 3; + state1.prevScrollY = 4; + state1.scrollY = 5; + state1.childrenHeights = new SparseIntArray(); + state1.childrenHeights.put(0, 10); + state1.childrenHeights.put(1, 20); + state1.childrenHeights.put(2, 30); + state1.writeToParcel(parcel, 0); + + parcel.setDataPosition(0); + + ObservableRecyclerView.SavedState state2 = ObservableRecyclerView.SavedState.CREATOR.createFromParcel(parcel); + assertNotNull(state2); + assertEquals(state1.prevFirstVisiblePosition, state2.prevFirstVisiblePosition); + assertEquals(state1.prevFirstVisibleChildHeight, state2.prevFirstVisibleChildHeight); + assertEquals(state1.prevScrolledChildrenHeight, state2.prevScrolledChildrenHeight); + assertEquals(state1.prevScrollY, state2.prevScrollY); + assertEquals(state1.scrollY, state2.scrollY); + assertNotNull(state1.childrenHeights); + assertEquals(3, state1.childrenHeights.size()); + assertEquals(10, state1.childrenHeights.get(0)); + assertEquals(20, state1.childrenHeights.get(1)); + assertEquals(30, state1.childrenHeights.get(2)); + } + + public void testScrollViewSavedState() throws Throwable { + Parcel parcel = Parcel.obtain(); + ObservableScrollView.SavedState state1 = new ObservableScrollView.SavedState(AbsSavedState.EMPTY_STATE); + state1.prevScrollY = 1; + state1.scrollY = 2; + state1.writeToParcel(parcel, 0); + + parcel.setDataPosition(0); + + ObservableScrollView.SavedState state2 = ObservableScrollView.SavedState.CREATOR.createFromParcel(parcel); + assertNotNull(state2); + assertEquals(state1.prevScrollY, state2.prevScrollY); + assertEquals(state1.scrollY, state2.scrollY); + } + + public void testWebViewSavedState() throws Throwable { + Parcel parcel = Parcel.obtain(); + ObservableWebView.SavedState state1 = new ObservableWebView.SavedState(AbsSavedState.EMPTY_STATE); + state1.prevScrollY = 1; + state1.scrollY = 2; + state1.writeToParcel(parcel, 0); + + parcel.setDataPosition(0); + + ObservableWebView.SavedState state2 = ObservableWebView.SavedState.CREATOR.createFromParcel(parcel); + assertNotNull(state2); + assertEquals(state1.prevScrollY, state2.prevScrollY); + assertEquals(state1.scrollY, state2.scrollY); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivity.java new file mode 100644 index 00000000..5bf39218 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivity.java @@ -0,0 +1,23 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.os.Bundle; + +import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; + +public class RecyclerViewScrollFromBottomActivity extends RecyclerViewActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final ObservableRecyclerView scrollable = (ObservableRecyclerView) findViewById(R.id.scrollable); + ScrollUtils.addOnGlobalLayoutListener(scrollable, new Runnable() { + @Override + public void run() { + int count = scrollable.getAdapter().getItemCount() - 1; + int position = count == 0 ? 1 : count > 0 ? count : 0; + scrollable.scrollToPosition(position); + } + }); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivityTest.java new file mode 100644 index 00000000..37eaf4d5 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivityTest.java @@ -0,0 +1,33 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; + +import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; + +public class RecyclerViewScrollFromBottomActivityTest extends ActivityInstrumentationTestCase2 { + + private Activity activity; + private ObservableRecyclerView scrollable; + + public RecyclerViewScrollFromBottomActivityTest() { + super(RecyclerViewScrollFromBottomActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableRecyclerView) activity.findViewById(R.id.scrollable); + } + + public void testScroll() throws Throwable { + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + } +} diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index 0f36183d..02f206c0 100644 --- a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -307,7 +307,7 @@ static class SavedState extends BaseSavedState { /** * Called by onSaveInstanceState. */ - private SavedState(Parcelable superState) { + SavedState(Parcelable superState) { super(superState); } diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java index d9698280..1d20c578 100644 --- a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java +++ b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java @@ -322,7 +322,7 @@ static class SavedState extends BaseSavedState { /** * Called by onSaveInstanceState. */ - private SavedState(Parcelable superState) { + SavedState(Parcelable superState) { super(superState); } diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java index 76ac3618..e0787559 100644 --- a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java +++ b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java @@ -349,7 +349,7 @@ private SavedState() { /** * Called by onSaveInstanceState. */ - private SavedState(Parcelable superState) { + SavedState(Parcelable superState) { this.superState = superState != EMPTY_STATE ? superState : null; } diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java index a76cd7f8..d38c0e2d 100644 --- a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java +++ b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java @@ -216,7 +216,7 @@ static class SavedState extends BaseSavedState { /** * Called by onSaveInstanceState. */ - private SavedState(Parcelable superState) { + SavedState(Parcelable superState) { super(superState); } diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java index 0167e704..3e023f74 100644 --- a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java +++ b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java @@ -214,7 +214,7 @@ static class SavedState extends BaseSavedState { /** * Called by onSaveInstanceState. */ - private SavedState(Parcelable superState) { + SavedState(Parcelable superState) { super(superState); } From eddad371db2c4eaab1b7176fb870f8ec347ed15c Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 27 Jan 2015 23:40:35 +0900 Subject: [PATCH 012/208] Re-activate test on Travis. --- .travis.yml | 16 +++++++++------- observablescrollview/build.gradle | 5 +++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 648bb908..f6af63b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,19 +3,21 @@ android: components: - build-tools-21.1.1 - tools - #- android-19 + - android-19 - android-21 - #- system-image + - system-image - extra-android-support - extra-android-m2repository licenses: - 'android-sdk-license-.+' -#before_script: - #- echo no | android create avd --force -n test -t android-19 --abi default/armeabi-v7a - #- emulator -avd test -no-skin -no-audio -no-window & - #- android-wait-for-emulator +before_script: + - echo no | android create avd --force -n test -t android-19 --abi default/armeabi-v7a + - emulator -avd test -no-skin -no-audio -no-window & + - android-wait-for-emulator script: # Release build type is only for Google Play store currently, # which resolve dependency from Maven Central. # This causes build errors while developing a new feature, so disable release build. - - travis_retry ./gradlew --full-stacktrace -q assembleDebug + - travis_retry ./gradlew --full-stacktrace -q assembleDebug connectedCheck +after_success: + - ./gradlew :observablescrollview:coveralls diff --git a/observablescrollview/build.gradle b/observablescrollview/build.gradle index 9ddd57be..1c3856dd 100644 --- a/observablescrollview/build.gradle +++ b/observablescrollview/build.gradle @@ -3,6 +3,7 @@ buildscript { mavenCentral() } dependencies { + classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.1.0' classpath 'com.android.tools.build:gradle:1.0.0' } } @@ -42,5 +43,9 @@ android { } } +apply plugin: 'com.github.kt3k.coveralls' + +coveralls.jacocoReportPath = 'build/outputs/reports/coverage/debug/report.xml' + // This is from 'https://github.com/chrisbanes/gradle-mvn-push' apply from: 'gradle-mvn-push.gradle' From 8dbbf60c005c71cacda7c242f3ea2eadac7e26c7 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 28 Jan 2015 00:27:39 +0900 Subject: [PATCH 013/208] Add Coveralls badge. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 02329299..18c8add2 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Android-ObservableScrollView === [![Build Status](http://img.shields.io/travis/ksoichiro/Android-ObservableScrollView.svg?style=flat)](https://travis-ci.org/ksoichiro/Android-ObservableScrollView) +[![Coverage Status](https://img.shields.io/coveralls/ksoichiro/Android-ObservableScrollView/master.svg?style=flat)](https://coveralls.io/r/ksoichiro/Android-ObservableScrollView?branch=master) [![Maven Central](http://img.shields.io/maven-central/v/com.github.ksoichiro/android-observablescrollview.svg?style=flat)](https://github.com/ksoichiro/Android-ObservableScrollView/releases/latest) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Android--ObservableScrollView-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1136) From 2191045365e7b59379a1b14d0ccda5b85aa8115e Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 28 Jan 2015 00:29:13 +0900 Subject: [PATCH 014/208] Bumped version to 1.5.0. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 126c9201..d1c9d464 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.5.0-SNAPSHOT +VERSION_NAME=1.5.0 SYNCED_VERSION_NAME=1.4.0 GROUP=com.github.ksoichiro From 0d45d9538df78036a33f44555864c3d76106a807 Mon Sep 17 00:00:00 2001 From: shimizu_yasuhiro Date: Wed, 28 Jan 2015 11:58:11 +0900 Subject: [PATCH 015/208] include topMargin in Scrollable's height calculation --- .../samples/ToolbarControlBaseActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlBaseActivity.java b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlBaseActivity.java index 5acf7412..ab7556c6 100644 --- a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlBaseActivity.java +++ b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlBaseActivity.java @@ -99,7 +99,7 @@ public void onAnimationUpdate(ValueAnimator animation) { ViewHelper.setTranslationY(mToolbar, translationY); ViewHelper.setTranslationY((View) mScrollable, translationY); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) ((View) mScrollable).getLayoutParams(); - lp.height = (int) -translationY + getScreenHeight(); + lp.height = (int) -translationY + getScreenHeight() - lp.topMargin; ((View) mScrollable).requestLayout(); } }); From 59fb5608d00a427468fb445aaef47686e9cd9727 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 28 Jan 2015 21:24:47 +0900 Subject: [PATCH 016/208] Update README and latest synced version. --- README.md | 5 ++++- gradle.properties | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 18c8add2..c8266f9d 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ repositories { } dependencies { - compile 'com.github.ksoichiro:android-observablescrollview:1.4.0' + compile 'com.github.ksoichiro:android-observablescrollview:1.5.0' } ``` @@ -130,6 +130,9 @@ This project is built and tested under the following environment. ## Release notes +* v1.5.0 + * Add a helper class `CacheFragmentStatePagerAdapter` to implement ViewPager pattern. + * Fix that swipe down (over-scroll) causes item click. * v1.4.0 * Add a custom view named `TouchInterceptionFrameLayout` and a new API `setTouchInterceptionViewGroup()` for `Scrollable`. With these class and API, you can move `Scrollable` itself using its scrolling events. diff --git a/gradle.properties b/gradle.properties index d1c9d464..b1611363 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ VERSION_NAME=1.5.0 -SYNCED_VERSION_NAME=1.4.0 +SYNCED_VERSION_NAME=1.5.0 GROUP=com.github.ksoichiro POM_DESCRIPTION=Android library to observe scroll events on scrollable views. From a12440a29f8b30544320e8b0276ccf6cbeb931b0 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 30 Jan 2015 00:40:54 +0900 Subject: [PATCH 017/208] Add an app to the list of apps that uses this library by Erick Chavez. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c8266f9d..655ddfa3 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,7 @@ This project is built and tested under the following environment. ## Apps that uses this library * [Jair Player](https://play.google.com/store/apps/details?id=aj.jair.music) by Akshay Chordiya +* [My Gradle](https://play.google.com/store/apps/details?id=se.project.generic.mygradle) by Erick Chavez Alcarraz If you're using this library in your app and you'd like to list it here, please let me know via [email](soichiro.kashima@gmail.com) or pull requests or issues. From e8deb7d85823fd2fd6805c34c7a62a995e7882a0 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 31 Jan 2015 16:46:02 +0900 Subject: [PATCH 018/208] Update sample app version for Google Play Store. --- observablescrollview-samples/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/observablescrollview-samples/build.gradle b/observablescrollview-samples/build.gradle index cafea876..eaada311 100644 --- a/observablescrollview-samples/build.gradle +++ b/observablescrollview-samples/build.gradle @@ -45,8 +45,8 @@ android { applicationId "com.github.ksoichiro.android.observablescrollview.samples" minSdkVersion 9 targetSdkVersion 21 - versionCode 4 - versionName "1.2.1" + versionCode 5 + versionName "1.3.0" buildConfigField "String", "GIT_HASH", "\"${project.ext.gitHash}\"" } From 49b4538093fbf8076d640966a86e929571a5a86e Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 1 Feb 2015 14:15:27 +0900 Subject: [PATCH 019/208] Limit tasks for Travis CI: remove redundant components and limited target flavors to dev. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f6af63b6..ac0a80ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ android: - tools - android-19 - android-21 - - system-image + - sys-img-armeabi-v7a-android-19 - extra-android-support - extra-android-m2repository licenses: @@ -18,6 +18,6 @@ script: # Release build type is only for Google Play store currently, # which resolve dependency from Maven Central. # This causes build errors while developing a new feature, so disable release build. - - travis_retry ./gradlew --full-stacktrace -q assembleDebug connectedCheck + - travis_retry ./gradlew --full-stacktrace -q assembleDevDebug connectedCheck after_success: - ./gradlew :observablescrollview:coveralls From fd34888ee69285467665ad15cf25aa0787a88363 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 1 Feb 2015 14:51:18 +0900 Subject: [PATCH 020/208] Replace calculation codes to use ScrollUtils in FillGap2 examples. --- .../samples/FillGap2BaseActivity.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2BaseActivity.java b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2BaseActivity.java index 7979d106..880c2eb6 100644 --- a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2BaseActivity.java +++ b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2BaseActivity.java @@ -16,6 +16,7 @@ package com.github.ksoichiro.android.observablescrollview.samples; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; import com.github.ksoichiro.android.observablescrollview.Scrollable; /** @@ -25,11 +26,6 @@ */ public abstract class FillGap2BaseActivity extends FillGapBaseActivity { protected float getHeaderTranslationY(int scrollY) { - final int headerHeight = mHeaderBar.getHeight(); - int headerTranslationY = 0; - if (0 <= -scrollY + mFlexibleSpaceImageHeight - headerHeight) { - headerTranslationY = -scrollY + mFlexibleSpaceImageHeight - headerHeight; - } - return headerTranslationY; + return ScrollUtils.getFloat(-scrollY + mFlexibleSpaceImageHeight - mHeaderBar.getHeight(), 0, Float.MAX_VALUE); } } From da534cab284d4b82e9553d3f28ae57360ad5f436 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 1 Feb 2015 15:05:58 +0900 Subject: [PATCH 021/208] Fix ScrollView content around bottom is not shown in FillGapScrollView and FillGap2ScrollView examples. Related to #56 issue. --- .../src/main/res/layout/activity_fillgapscrollview.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/observablescrollview-samples/src/main/res/layout/activity_fillgapscrollview.xml b/observablescrollview-samples/src/main/res/layout/activity_fillgapscrollview.xml index 024456a8..07a1ab2f 100644 --- a/observablescrollview-samples/src/main/res/layout/activity_fillgapscrollview.xml +++ b/observablescrollview-samples/src/main/res/layout/activity_fillgapscrollview.xml @@ -37,6 +37,7 @@ android:id="@+id/scroll" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_marginTop="@dimen/flexible_space_image_height" android:fillViewport="true" android:overScrollMode="never" android:scrollbars="none"> @@ -45,7 +46,6 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/flexible_space_image_height" android:background="@android:color/white" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" From ef6e1db5d4f009902207a31081fb591679c49fa6 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 1 Feb 2015 16:03:36 +0900 Subject: [PATCH 022/208] Fix that FAB's onClickListener is not called on pre-honeycomb in FlexibleSpaceWithImageListViewActivity. #58 --- ...lexibleSpaceWithImageListViewActivity.java | 24 ++++++++++++++++--- ...ctivity_flexiblespacewithimagelistview.xml | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java index 4d04b235..d94a9d71 100644 --- a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java +++ b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java @@ -17,11 +17,14 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.graphics.Color; +import android.os.Build; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.view.View; import android.widget.AbsListView; +import android.widget.FrameLayout; import android.widget.TextView; +import android.widget.Toast; import com.github.ksoichiro.android.observablescrollview.ObservableListView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; @@ -84,6 +87,12 @@ protected void onCreate(Bundle savedInstanceState) { mTitleView.setText(getTitle()); setTitle(null); mFab = findViewById(R.id.fab); + mFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(FlexibleSpaceWithImageListViewActivity.this, "FAB is clicked", Toast.LENGTH_SHORT).show(); + } + }); mFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard); ViewHelper.setScaleX(mFab, 0); ViewHelper.setScaleY(mFab, 0); @@ -137,11 +146,20 @@ public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) -scrollY + mFlexibleSpaceImageHeight - mFab.getHeight() / 2, mActionBarSize - mFab.getHeight() / 2, maxFabTranslationY); - ViewHelper.setTranslationX(mFab, mOverlayView.getWidth() - mFabMargin - mFab.getWidth()); - ViewHelper.setTranslationY(mFab, fabTranslationY); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + // On pre-honeycomb, ViewHelper.setTranslationX/Y does not set margin, + // which causes FAB's OnClickListener not working. + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFab.getLayoutParams(); + lp.leftMargin = mOverlayView.getWidth() - mFabMargin - mFab.getWidth(); + lp.topMargin = (int) fabTranslationY; + mFab.requestLayout(); + } else { + ViewHelper.setTranslationX(mFab, mOverlayView.getWidth() - mFabMargin - mFab.getWidth()); + ViewHelper.setTranslationY(mFab, fabTranslationY); + } // Show/hide FAB - if (ViewHelper.getTranslationY(mFab) < mFlexibleSpaceShowFabOffset) { + if (fabTranslationY < mFlexibleSpaceShowFabOffset) { hideFab(); } else { showFab(); diff --git a/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagelistview.xml b/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagelistview.xml index 19a73a72..8b519103 100644 --- a/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagelistview.xml +++ b/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagelistview.xml @@ -80,6 +80,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:scaleType="center" + android:layout_gravity="left|top" app:fab_colorNormal="@color/accentLight" app:fab_colorPressed="@color/accent" /> From d41b3018b77d69ab85c6f6ab07ee948bb044cf23 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 2 Feb 2015 23:58:46 +0900 Subject: [PATCH 023/208] Fix that FAB's onClickListener is not called on pre-honeycomb in FlexibleSpaceWithImageScrollViewActivity. --- ...xibleSpaceWithImageScrollViewActivity.java | 24 ++++++++++++++++--- ...ivity_flexiblespacewithimagescrollview.xml | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewActivity.java b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewActivity.java index 33852ebd..8cb55c3d 100644 --- a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewActivity.java +++ b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewActivity.java @@ -17,10 +17,13 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.graphics.Color; +import android.os.Build; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.view.View; +import android.widget.FrameLayout; import android.widget.TextView; +import android.widget.Toast; import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; @@ -71,6 +74,12 @@ protected void onCreate(Bundle savedInstanceState) { mTitleView.setText(getTitle()); setTitle(null); mFab = findViewById(R.id.fab); + mFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(FlexibleSpaceWithImageScrollViewActivity.this, "FAB is clicked", Toast.LENGTH_SHORT).show(); + } + }); mFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard); ViewHelper.setScaleX(mFab, 0); ViewHelper.setScaleY(mFab, 0); @@ -126,11 +135,20 @@ public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) -scrollY + mFlexibleSpaceImageHeight - mFab.getHeight() / 2, mActionBarSize - mFab.getHeight() / 2, maxFabTranslationY); - ViewHelper.setTranslationX(mFab, mOverlayView.getWidth() - mFabMargin - mFab.getWidth()); - ViewHelper.setTranslationY(mFab, fabTranslationY); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + // On pre-honeycomb, ViewHelper.setTranslationX/Y does not set margin, + // which causes FAB's OnClickListener not working. + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFab.getLayoutParams(); + lp.leftMargin = mOverlayView.getWidth() - mFabMargin - mFab.getWidth(); + lp.topMargin = (int) fabTranslationY; + mFab.requestLayout(); + } else { + ViewHelper.setTranslationX(mFab, mOverlayView.getWidth() - mFabMargin - mFab.getWidth()); + ViewHelper.setTranslationY(mFab, fabTranslationY); + } // Show/hide FAB - if (ViewHelper.getTranslationY(mFab) < mFlexibleSpaceShowFabOffset) { + if (fabTranslationY < mFlexibleSpaceShowFabOffset) { hideFab(); } else { showFab(); diff --git a/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagescrollview.xml b/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagescrollview.xml index cc599387..53fbf648 100644 --- a/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagescrollview.xml +++ b/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagescrollview.xml @@ -97,6 +97,7 @@ android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_gravity="left|top" android:scaleType="center" app:fab_colorNormal="@color/accentLight" app:fab_colorPressed="@color/accent" /> From e60a0cd751cb2776bd05ee8afea8b71c21d8feda Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 3 Feb 2015 01:38:05 +0900 Subject: [PATCH 024/208] Fix title scale is wrong when RTL is enabled. #58 --- ...lexibleSpaceWithImageListViewActivity.java | 15 +++++++++++++- ...ctivity_flexiblespacewithimagelistview.xml | 4 +++- .../src/main/res/values-ar/strings.xml | 20 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 observablescrollview-samples/src/main/res/values-ar/strings.xml diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java index d94a9d71..7e285fb1 100644 --- a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java +++ b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java @@ -16,6 +16,8 @@ package com.github.ksoichiro.android.observablescrollview.samples; +import android.annotation.TargetApi; +import android.content.res.Configuration; import android.graphics.Color; import android.os.Build; import android.os.Bundle; @@ -127,7 +129,7 @@ public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) // Scale title text float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA); - ViewHelper.setPivotX(mTitleView, 0); + setPivotXToTitle(); ViewHelper.setPivotY(mTitleView, 0); ViewHelper.setScaleX(mTitleView, scale); ViewHelper.setScaleY(mTitleView, scale); @@ -190,6 +192,17 @@ public void onDownMotionEvent() { public void onUpOrCancelMotionEvent(ScrollState scrollState) { } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private void setPivotXToTitle() { + Configuration config = getResources().getConfiguration(); + if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT + && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + ViewHelper.setPivotX(mTitleView, findViewById(android.R.id.content).getWidth()); + } else { + ViewHelper.setPivotX(mTitleView, 0); + } + } + private void showFab() { if (!mFabIsShown) { ViewPropertyAnimator.animate(mFab).cancel(); diff --git a/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagelistview.xml b/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagelistview.xml index 8b519103..40a55726 100644 --- a/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagelistview.xml +++ b/observablescrollview-samples/src/main/res/layout/activity_flexiblespacewithimagelistview.xml @@ -56,7 +56,9 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingLeft="@dimen/margin_standard"> + android:paddingEnd="@dimen/margin_standard" + android:paddingLeft="@dimen/margin_standard" + android:paddingStart="@dimen/margin_standard"> + + + الفضاء مرن + + From ad9725ab2126a99b09e1b411c7d206756b36950a Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 7 Feb 2015 20:28:19 +0900 Subject: [PATCH 025/208] Remove BuildConfig's custom fields. (Preparing to supporting eclipse) --- observablescrollview-samples/.gitignore | 1 + observablescrollview-samples/build.gradle | 16 +--- .../src/main/AndroidManifest.xml | 92 +++++++++---------- .../samples/AboutActivity.java | 18 +++- .../samples/MainActivity.java | 3 +- observablescrollview-samples/version.gradle | 59 ++++++++++++ 6 files changed, 128 insertions(+), 61 deletions(-) create mode 100644 observablescrollview-samples/version.gradle diff --git a/observablescrollview-samples/.gitignore b/observablescrollview-samples/.gitignore index 796b96d1..915c5f4b 100644 --- a/observablescrollview-samples/.gitignore +++ b/observablescrollview-samples/.gitignore @@ -1 +1,2 @@ /build +/src/version/ diff --git a/observablescrollview-samples/build.gradle b/observablescrollview-samples/build.gradle index eaada311..6c8de2c7 100644 --- a/observablescrollview-samples/build.gradle +++ b/observablescrollview-samples/build.gradle @@ -7,13 +7,6 @@ buildscript { } } -// Get git commit hash for naming the APK file if 'git' is available -try { - project.ext.gitHash = "git rev-parse --short HEAD".execute().text.trim() -} catch (all) { - project.ext.gitHash = "unknown" -} - apply plugin: 'com.android.application' repositories { @@ -37,6 +30,9 @@ dependencies { //compile "com.github.ksoichiro:android-observablescrollview:$VERSION_NAME" } +apply from: 'version.gradle' +project.ext.versionInfo.releaseVersionName = SYNCED_VERSION_NAME + android { compileSdkVersion 21 buildToolsVersion "21.1.1" @@ -47,7 +43,6 @@ android { targetSdkVersion 21 versionCode 5 versionName "1.3.0" - buildConfigField "String", "GIT_HASH", "\"${project.ext.gitHash}\"" } productFlavors { @@ -83,12 +78,9 @@ android { debug { applicationIdSuffix ".debug" versionNameSuffix "-debug" - buildConfigField "String", "LIB_VERSION", "\"${project.ext.gitHash}\"" } release { - buildConfigField "String", "LIB_VERSION", "\"${VERSION_NAME}\"" - minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' @@ -107,7 +99,7 @@ android { applicationVariants.all { variant -> def output = variant.outputs.get(0) File apk = output.outputFile - String newName = output.outputFile.name.replace(".apk", "-${variant.mergedFlavor.versionCode}-${variant.mergedFlavor.versionName}-${project.ext.gitHash}.apk") + String newName = output.outputFile.name.replace(".apk", "-${variant.mergedFlavor.versionCode}-${variant.mergedFlavor.versionName}-${project.ext.versionInfo.build}.apk") .replace("app-", "${variant.mergedFlavor.applicationId}-") output.outputFile = new File(apk.parentFile, newName) } diff --git a/observablescrollview-samples/src/main/AndroidManifest.xml b/observablescrollview-samples/src/main/AndroidManifest.xml index b5818066..6e894767 100644 --- a/observablescrollview-samples/src/main/AndroidManifest.xml +++ b/observablescrollview-samples/src/main/AndroidManifest.xml @@ -42,7 +42,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java index b8f81299..fa656382 100644 --- a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java +++ b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java @@ -16,6 +16,8 @@ package com.github.ksoichiro.android.observablescrollview.samples; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.os.Bundle; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; @@ -39,8 +41,8 @@ protected void onCreate(Bundle savedInstanceState) { ab.setDisplayHomeAsUpEnabled(true); ab.setHomeButtonEnabled(true); } - ((TextView) findViewById(R.id.app_version)).setText(getString(R.string.msg_app_version, BuildConfig.VERSION_NAME, BuildConfig.GIT_HASH)); - ((TextView) findViewById(R.id.lib_version)).setText(getString(R.string.msg_lib_version, BuildConfig.LIB_VERSION)); + ((TextView) findViewById(R.id.app_version)).setText(getString(R.string.msg_app_version, getVersionName(), VersionInfo.BUILD)); + ((TextView) findViewById(R.id.lib_version)).setText(getString(R.string.msg_lib_version, VersionInfo.LIBRARY_VERSION)); initLicenses(); } @@ -106,4 +108,16 @@ private TextView createHtmlText(final String s, final int margin) { private View createDivider(final LayoutInflater inflater) { return inflater.inflate(R.layout.divider, null); } + + private String getVersionName() { + final PackageManager manager = getPackageManager(); + String versionName; + try { + final PackageInfo info = manager.getPackageInfo(getPackageName(), PackageManager.GET_META_DATA); + versionName = info.versionName; + } catch (PackageManager.NameNotFoundException e) { + versionName = ""; + } + return versionName; + } } diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java index 5d65f3ed..50fd880a 100644 --- a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java +++ b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java @@ -38,7 +38,7 @@ public class MainActivity extends ActionBarActivity implements AdapterView.OnItemClickListener { - private static final String CATEGORY_SAMPLES = BuildConfig.APPLICATION_ID; + private static final String CATEGORY_SAMPLES = MainActivity.class.getPackage().getName(); private static final String TAG_CLASS_NAME = "className"; private static final String TAG_DESCRIPTION = "description"; private static final String TAG_INTENT = "intent"; @@ -85,6 +85,7 @@ private List> getData() { List> data = new ArrayList>(); Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.setPackage(getApplicationContext().getPackageName()); mainIntent.addCategory(CATEGORY_SAMPLES); PackageManager pm = getPackageManager(); diff --git a/observablescrollview-samples/version.gradle b/observablescrollview-samples/version.gradle new file mode 100644 index 00000000..9360393e --- /dev/null +++ b/observablescrollview-samples/version.gradle @@ -0,0 +1,59 @@ +// Generate version information to show in the sample app. +// This can be achieved more easily when we use BuildConfig in Gradle and Android Studio, +// but we also support Eclipse so we don't use BuildConfig. + +project.ext.versionInfo = new Expando() +project.ext.versionInfo.srcDir = 'version' + +// Get git commit hash for naming the APK file if 'git' is available +try { + project.ext.versionInfo.build = "git rev-parse --short HEAD".execute().text.trim() +} catch (ignored) { + project.ext.versionInfo.build = "unknown" +} +project.ext.versionInfo.releaseVersionName = project.ext.versionInfo.build + +android.sourceSets.findAll { it in android.buildTypes }.each { + it.java.srcDirs += "src/${project.ext.versionInfo.srcDir}/${it.name}/java" +} + +android.buildTypes.each { buildType -> + task "generateVersionInfo${buildType.name.capitalize()}" << { + def packageName = android.defaultConfig.applicationId + def dir = project.file("src/${project.ext.versionInfo.srcDir}/${buildType.name}/java/${packageName.tr('.', '/')}") + if (dir.exists()) { + dir.listFiles().each { + project.delete(it) + } + } else { + project.mkdir(dir) + } + def libraryVersion = buildType.name == 'release' ? project.ext.versionInfo.releaseVersionName : project.ext.versionInfo.build + def className = 'VersionInfo' + new File(dir, "${className}.java").text = """\ +package ${packageName}; + +// DO NOT EDIT: This file is automatically generated. +public class ${className} { + public static final String LIBRARY_VERSION = "${libraryVersion}"; + public static final String BUILD = "${project.ext.versionInfo.build}"; +} +""" + } +} + +task cleanVersionInfo << { + def dir = project.file("src/${project.ext.versionInfo.srcDir}") + if (dir.exists()) { + dir.listFiles().findAll { it.isDirectory() }.each { + project.delete(it) + } + } +} +clean.dependsOn(tasks['cleanVersionInfo']) + +afterEvaluate { + android.applicationVariants.all { + tasks["generate${it.name.capitalize()}Sources"].dependsOn("generateVersionInfo${it.buildType.name.capitalize()}") + } +} From 71570435815fe38073d97c7c688309cfcd578292 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 8 Feb 2015 17:01:38 +0900 Subject: [PATCH 026/208] IMPORTANT CHANGE: directory structures changed to support build on Eclipse. --- .gitignore | 4 + eclipse.gradle | 273 ++++++++++++++++++ observablescrollview-samples/.classpath | 17 ++ observablescrollview-samples/.project | 34 +++ .../{src/main => }/AndroidManifest.xml | 11 +- observablescrollview-samples/build.gradle | 11 +- .../project.properties | 19 ++ .../main => }/res/color/tab_text_color.xml | 0 .../res/drawable-xhdpi/ic_launcher.png | Bin .../res/drawable-xxhdpi/ic_launcher.png | Bin .../res/drawable-xxxhdpi/ic_launcher.png | Bin .../{src/main => }/res/drawable/example.jpeg | Bin .../drawable/gradient_header_background.xml | 0 .../res/drawable/sliding_header_overlay.xml | 0 .../res/layout-v11/tab_indicator.xml | 0 .../main => }/res/layout/activity_about.xml | 0 .../activity_actionbarcontrolgridview.xml | 0 .../activity_actionbarcontrollistview.xml | 0 .../activity_actionbarcontrolrecyclerview.xml | 0 .../activity_actionbarcontrolscrollview.xml | 0 .../activity_actionbarcontrolwebview.xml | 0 .../res/layout/activity_fillgap3listview.xml | 0 .../layout/activity_fillgap3recyclerview.xml | 0 .../layout/activity_fillgap3scrollview.xml | 0 .../res/layout/activity_fillgaplistview.xml | 0 .../layout/activity_fillgaprecyclerview.xml | 0 .../res/layout/activity_fillgapscrollview.xml | 0 ...ctivity_flexiblespacetoolbarscrollview.xml | 0 ...ctivity_flexiblespacewithimagelistview.xml | 0 ...ivity_flexiblespacewithimagescrollview.xml | 0 .../activity_fragmentactionbarcontrol.xml | 0 .../layout/activity_fragmenttransition.xml | 0 .../layout/activity_handletouchgridview.xml | 0 .../layout/activity_handletouchlistview.xml | 0 .../activity_handletouchrecyclerview.xml | 0 .../layout/activity_handletouchscrollview.xml | 0 .../layout/activity_handletouchwebview.xml | 0 .../main => }/res/layout/activity_main.xml | 0 .../activity_parallaxtoolbarscrollview.xml | 0 .../res/layout/activity_slidingupgridview.xml | 0 .../res/layout/activity_slidinguplistview.xml | 0 .../layout/activity_slidinguprecyclerview.xml | 0 .../layout/activity_slidingupscrollview.xml | 0 .../res/layout/activity_slidingupwebview.xml | 0 .../layout/activity_stickyheaderlistview.xml | 0 .../activity_stickyheaderrecyclerview.xml | 0 .../activity_stickyheaderscrollview.xml | 0 .../layout/activity_stickyheaderwebview.xml | 0 .../activity_toolbarcontrolgridview.xml | 0 .../activity_toolbarcontrollistview.xml | 0 .../activity_toolbarcontrolrecyclerview.xml | 0 .../activity_toolbarcontrolscrollview.xml | 0 .../layout/activity_toolbarcontrolwebview.xml | 0 .../res/layout/activity_viewpagertab.xml | 0 .../res/layout/activity_viewpagertab2.xml | 0 .../layout/activity_viewpagertabfragment.xml | 0 .../{src/main => }/res/layout/divider.xml | 0 .../fragment_actionbarcontrollistview.xml | 0 .../fragment_fragmenttransition_default.xml | 0 .../fragment_fragmenttransition_second.xml | 0 .../res/layout/fragment_gridview.xml | 0 .../res/layout/fragment_listview.xml | 0 .../res/layout/fragment_recyclerview.xml | 0 .../res/layout/fragment_scrollview.xml | 0 .../layout/fragment_scrollview_noheader.xml | 0 .../fragment_viewpagertabfragment_parent.xml | 0 .../main => }/res/layout/fragment_webview.xml | 0 .../main => }/res/layout/gradient_header.xml | 0 .../res/layout/list_item_handletouch.xml | 0 .../main => }/res/layout/list_item_main.xml | 0 .../{src/main => }/res/layout/padding.xml | 0 .../main => }/res/layout/recycler_header.xml | 0 .../main => }/res/layout/tab_indicator.xml | 0 .../{src/main => }/res/menu/menu_main.xml | 0 .../{src/main => }/res/values-ar/strings.xml | 0 .../main => }/res/values-w820dp/dimens.xml | 0 .../{src/main => }/res/values/colors.xml | 0 .../{src/main => }/res/values/dimens.xml | 0 .../{src/main => }/res/values/strings.xml | 0 .../main => }/res/values/strings_license.xml | 0 .../{src/main => }/res/values/styles.xml | 0 observablescrollview/.classpath | 10 + observablescrollview/.project | 34 +++ .../{src/main => }/AndroidManifest.xml | 0 observablescrollview/build.gradle | 12 + observablescrollview/project.properties | 17 ++ observablescrollview/res/.gitkeep | 0 87 files changed, 437 insertions(+), 5 deletions(-) create mode 100644 eclipse.gradle create mode 100644 observablescrollview-samples/.classpath create mode 100644 observablescrollview-samples/.project rename observablescrollview-samples/{src/main => }/AndroidManifest.xml (99%) create mode 100644 observablescrollview-samples/project.properties rename observablescrollview-samples/{src/main => }/res/color/tab_text_color.xml (100%) rename observablescrollview-samples/{src/main => }/res/drawable-xhdpi/ic_launcher.png (100%) rename observablescrollview-samples/{src/main => }/res/drawable-xxhdpi/ic_launcher.png (100%) rename observablescrollview-samples/{src/main => }/res/drawable-xxxhdpi/ic_launcher.png (100%) rename observablescrollview-samples/{src/main => }/res/drawable/example.jpeg (100%) rename observablescrollview-samples/{src/main => }/res/drawable/gradient_header_background.xml (100%) rename observablescrollview-samples/{src/main => }/res/drawable/sliding_header_overlay.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout-v11/tab_indicator.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_about.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_actionbarcontrolgridview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_actionbarcontrollistview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_actionbarcontrolrecyclerview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_actionbarcontrolscrollview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_actionbarcontrolwebview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_fillgap3listview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_fillgap3recyclerview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_fillgap3scrollview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_fillgaplistview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_fillgaprecyclerview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_fillgapscrollview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_flexiblespacetoolbarscrollview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_flexiblespacewithimagelistview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_flexiblespacewithimagescrollview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_fragmentactionbarcontrol.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_fragmenttransition.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_handletouchgridview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_handletouchlistview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_handletouchrecyclerview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_handletouchscrollview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_handletouchwebview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_main.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_parallaxtoolbarscrollview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_slidingupgridview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_slidinguplistview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_slidinguprecyclerview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_slidingupscrollview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_slidingupwebview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_stickyheaderlistview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_stickyheaderrecyclerview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_stickyheaderscrollview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_stickyheaderwebview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_toolbarcontrolgridview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_toolbarcontrollistview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_toolbarcontrolrecyclerview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_toolbarcontrolscrollview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_toolbarcontrolwebview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_viewpagertab.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_viewpagertab2.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/activity_viewpagertabfragment.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/divider.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/fragment_actionbarcontrollistview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/fragment_fragmenttransition_default.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/fragment_fragmenttransition_second.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/fragment_gridview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/fragment_listview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/fragment_recyclerview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/fragment_scrollview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/fragment_scrollview_noheader.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/fragment_viewpagertabfragment_parent.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/fragment_webview.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/gradient_header.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/list_item_handletouch.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/list_item_main.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/padding.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/recycler_header.xml (100%) rename observablescrollview-samples/{src/main => }/res/layout/tab_indicator.xml (100%) rename observablescrollview-samples/{src/main => }/res/menu/menu_main.xml (100%) rename observablescrollview-samples/{src/main => }/res/values-ar/strings.xml (100%) rename observablescrollview-samples/{src/main => }/res/values-w820dp/dimens.xml (100%) rename observablescrollview-samples/{src/main => }/res/values/colors.xml (100%) rename observablescrollview-samples/{src/main => }/res/values/dimens.xml (100%) rename observablescrollview-samples/{src/main => }/res/values/strings.xml (100%) rename observablescrollview-samples/{src/main => }/res/values/strings_license.xml (100%) rename observablescrollview-samples/{src/main => }/res/values/styles.xml (100%) create mode 100644 observablescrollview/.classpath create mode 100644 observablescrollview/.project rename observablescrollview/{src/main => }/AndroidManifest.xml (100%) create mode 100644 observablescrollview/project.properties create mode 100644 observablescrollview/res/.gitkeep diff --git a/.gitignore b/.gitignore index 2cd75b76..73557b8c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ private.properties /.idea/ .DS_Store /build +bin/ +gen/ +libs/ +aarDependencies/ diff --git a/eclipse.gradle b/eclipse.gradle new file mode 100644 index 00000000..4db0f2eb --- /dev/null +++ b/eclipse.gradle @@ -0,0 +1,273 @@ +// Eclipse supporting script based on: +// http://www.nodeclipse.org/projects/gradle/android/aar-for-Eclipse + +project.ext.eclipseSupport = new Expando() +project.ext.eclipseSupport.androidTarget = 'android-21' +project.ext.eclipseSupport.aarDependenciesDir = 'aarDependencies' +project.ext.eclipseSupport.jarDependencies = [] as Set +project.ext.eclipseSupport.aarDependencies = [] as Set + +task cleanEclipseDependencies { + description = 'Used for Eclipse. Cleans dependencies directory.' + doLast { + [ project.file(project.ext.eclipseSupport.aarDependenciesDir), project.file('libs') ].each { + if (it.exists()) { + project.delete(it) + println "Deleted ${it}" + } + } + } +} + +task generateEclipseDependencies(dependsOn: 'cleanEclipseDependencies') { + description = 'Used for Eclipse. Copies all dependencies for library directory.' + doLast { + def aggregateJarFrom = { task -> + println "Aggregating JAR dependencies from ${task.name} configuration" + task.filter { + it.name.endsWith 'jar' + }.each { File jar -> project.ext.eclipseSupport.jarDependencies << jar } + } + + def aggregateAarFrom = { task -> + println "Aggregating AAR dependencies from ${task.name} configuration" + task.filter { File aar -> + aar.name.endsWith('aar') && isRequired(aar) + }.each { File aar -> project.ext.eclipseSupport.aarDependencies << aar } + } + + // Ignoring release and androidTest configurations. + project.parent.subprojects.each { + [it.configurations.compile, it.configurations.debugCompile].each { + aggregateJarFrom it + aggregateAarFrom it + } + } + + def extractJarFrom = { task -> + println "Extracting JAR dependencies from ${task.name} configuration" + task.filter { + it.name.endsWith 'jar' + }.each { File jar -> moveJarIntoLibs(jar) } + } + + def extractAarFrom = { task -> + println "Extracting AAR dependencies from ${task.name} configuration" + task.filter { File aar -> + aar.name.endsWith('aar') && isRequired(aar) + }.each { File aar -> moveAndRenameAar(aar) } + } + + project.parent.subprojects.each { + [it.configurations.compile, it.configurations.debugCompile].each { + extractJarFrom it + extractAarFrom it + } + } + + project.file(project.ext.eclipseSupport.aarDependenciesDir).listFiles().findAll { + it.isDirectory() + }.each { aar -> + generateProjectPropertiesFile(aar) + generateEclipseClasspathFile(aar) + generateEclipseProjectFile(aar) + } + } +} + +boolean isRequired(File aar) { + def name = getDependencyProjectName(aar) + boolean result = false + project.file('project.properties').eachLine { String line -> + if (line.matches("^android.library.reference.[0-9]+=${project.ext.eclipseSupport.aarDependenciesDir}/${name}")) { + result = true + } + } + result +} + +String getDependencyProjectName(File file) { + file.name.lastIndexOf('.').with { it != -1 ? file.name[0.. + copy { + from file + into destDir + } + }) +} + +void moveAndRenameAar(File file) { + println "Added aar ${file}" + def dependencyProjectName = getDependencyProjectName(file) + + // directory excluding the classes.jar + copy { + from zipTree(file) + exclude 'classes.jar' + into "${project.ext.eclipseSupport.aarDependenciesDir}/${dependencyProjectName}" + } + + // Copies the classes.jar into the libs directory of the exploded AAR. + // In Eclipse you can then import this exploded ar as an Android project + // and then reference not only the classes but also the android resources :D + ["${project.ext.eclipseSupport.aarDependenciesDir}/${dependencyProjectName}/libs", "libs"].each { dest -> + def jarFileName = "${dependencyProjectName}.jar" + copyJarIfNewer(dest, file, true, { destDir -> + copy { + from zipTree(file) + include 'classes.jar' + into destDir + rename { String fileName -> + fileName.replace('classes.jar', jarFileName) + } + } + }) + } +} + +void copyJarIfNewer(String libsDir, File dependency, boolean isAarDependency, Closure copyClosure) { + def dependencyFilename = dependency.name + def dependencyProjectName = getDependencyProjectName(dependency) + def dependencyName = getDependencyName(dependencyFilename) + def versionName = getVersionName(dependencyFilename) + boolean isNewer = false + boolean sameDependencyExists = false + def dependencies = isAarDependency ? project.ext.eclipseSupport.aarDependencies : project.ext.eclipseSupport.jarDependencies + dependencies.findAll { File it -> + // Check if there are any dependencies with the same name but different version + getDependencyName(it.name) == dependencyName && getVersionName(it.name) != versionName + }.each { File file -> + println " Same dependency exists: ${dependencyFilename}, ${file.name}" + sameDependencyExists = true + def v1 = getVersionName(dependencyFilename) + def v2 = getVersionName(file.name) + // 'file' may be removed in previous loop + if (file.exists() && v1.isNewerThan(v2)) { + println " Found older dependency. Copy ${dependencyFilename} to all subprojects" + isNewer = true + // Should be replaced to jarFilename jar + project.parent.subprojects.each { subProject2 -> + def subProjectLibDir = subProject2.file('libs') + if (isAarDependency) { + subProjectLibDir.listFiles().findAll { + it.isDirectory() && getDependencyName(it.name) == dependencyName + }.each { File lib -> + println " REMOVED ${lib}" + project.delete(lib) + copy { + from zipTree(dependency) + exclude 'classes.jar' + into "${project.ext.eclipseSupport.aarDependenciesDir}/${dependencyProjectName}" + } + copyClosure(subProjectLibDir) + } + } else { + subProjectLibDir.listFiles().findAll { + !it.isDirectory() && getDependencyName(it.name) == dependencyName + }.each { File lib -> + println " REMOVED ${lib}" + project.delete(lib) + copyClosure(subProjectLibDir) + } + } + } + } + } + if (!sameDependencyExists || isNewer) { + println " Copy new dependency: ${dependencyFilename}" + copyClosure(libsDir) + } +} + +String getBaseName(String filename) { + filename.lastIndexOf('.').with { it != -1 ? filename[0.. + String v1 = delegate + def versions1 = v1.tokenize('.') + def versions2 = v2.tokenize('.') + for (int i = 0; i < Math.min(versions1.size(), versions2.size()); i++) { + int n1 = versions1[i].toInteger() + int n2 = versions2[i].toInteger() + if (n2 < n1) { + return true + } + } + versions2.size() < versions1.size() +} + +void generateProjectPropertiesFile(File aar) { + project.file("${project.ext.eclipseSupport.aarDependenciesDir}/${aar.name}/project.properties").text = """\ +target=${project.ext.eclipseSupport.androidTarget} +android.library=true +""" +} + +void generateEclipseClasspathFile(File aar) { + project.file("${project.ext.eclipseSupport.aarDependenciesDir}/${aar.name}/.classpath").text = """\ + + + + + + + + + +""" +} + +void generateEclipseProjectFile(File aar) { + def name = aar.name + project.file("${project.ext.eclipseSupport.aarDependenciesDir}/${name}/.project").text = """\ + + + ${project.name}-${name} + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + org.springsource.ide.eclipse.gradle.core.nature + org.eclipse.jdt.core.javanature + com.android.ide.eclipse.adt.AndroidNature + + +""" +} diff --git a/observablescrollview-samples/.classpath b/observablescrollview-samples/.classpath new file mode 100644 index 00000000..5a25fe17 --- /dev/null +++ b/observablescrollview-samples/.classpath @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/observablescrollview-samples/.project b/observablescrollview-samples/.project new file mode 100644 index 00000000..7a6e10b8 --- /dev/null +++ b/observablescrollview-samples/.project @@ -0,0 +1,34 @@ + + + observablescrollview-samples + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + org.springsource.ide.eclipse.gradle.core.nature + org.eclipse.jdt.core.javanature + com.android.ide.eclipse.adt.AndroidNature + + diff --git a/observablescrollview-samples/src/main/AndroidManifest.xml b/observablescrollview-samples/AndroidManifest.xml similarity index 99% rename from observablescrollview-samples/src/main/AndroidManifest.xml rename to observablescrollview-samples/AndroidManifest.xml index 6e894767..6f95311e 100644 --- a/observablescrollview-samples/src/main/AndroidManifest.xml +++ b/observablescrollview-samples/AndroidManifest.xml @@ -15,9 +15,14 @@ --> - - + package="com.github.ksoichiro.android.observablescrollview.samples" + android:versionCode="5" + android:versionName="1.3.0"> + + + + + + + + + + + diff --git a/observablescrollview/.project b/observablescrollview/.project new file mode 100644 index 00000000..74fc38a4 --- /dev/null +++ b/observablescrollview/.project @@ -0,0 +1,34 @@ + + + observablescrollview + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + org.springsource.ide.eclipse.gradle.core.nature + org.eclipse.jdt.core.javanature + com.android.ide.eclipse.adt.AndroidNature + + diff --git a/observablescrollview/src/main/AndroidManifest.xml b/observablescrollview/AndroidManifest.xml similarity index 100% rename from observablescrollview/src/main/AndroidManifest.xml rename to observablescrollview/AndroidManifest.xml diff --git a/observablescrollview/build.gradle b/observablescrollview/build.gradle index 1c3856dd..3b8e4820 100644 --- a/observablescrollview/build.gradle +++ b/observablescrollview/build.gradle @@ -41,6 +41,16 @@ android { testCoverageEnabled = true } } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + } + } + + lintOptions { + abortOnError false + } } apply plugin: 'com.github.kt3k.coveralls' @@ -49,3 +59,5 @@ coveralls.jacocoReportPath = 'build/outputs/reports/coverage/debug/report.xml' // This is from 'https://github.com/chrisbanes/gradle-mvn-push' apply from: 'gradle-mvn-push.gradle' + +apply from: '../eclipse.gradle' diff --git a/observablescrollview/project.properties b/observablescrollview/project.properties new file mode 100644 index 00000000..e8098713 --- /dev/null +++ b/observablescrollview/project.properties @@ -0,0 +1,17 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-21 +android.library=true +android.library.reference.1=aarDependencies/recyclerview-v7-21.0.0 +android.library.reference.2=aarDependencies/support-v4-21.0.2 diff --git a/observablescrollview/res/.gitkeep b/observablescrollview/res/.gitkeep new file mode 100644 index 00000000..e69de29b From 5151f88f3d142544ba76679a2f7549be5e808fdc Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 8 Feb 2015 18:07:07 +0900 Subject: [PATCH 027/208] Update docs about Eclipse build. --- README.md | 5 ++- docs/eclipse.md | 113 ++++++++++++++++++++++++------------------------ 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 655ddfa3..a113d432 100644 --- a/README.md +++ b/README.md @@ -66,8 +66,9 @@ dependencies { } ``` -Eclipse is not supported but if you really want to build on Eclipse, [see here](https://github.com/ksoichiro/Android-ObservableScrollView/tree/master/docs/eclipse.md). - +Basically this project supports Android Studio. +If you'd like to use Eclipse, please [see here](https://github.com/ksoichiro/Android-ObservableScrollView/tree/master/docs/eclipse.md). +It's partially supported. ### Add widgets to your layout diff --git a/docs/eclipse.md b/docs/eclipse.md index aa09add0..35b7e5d4 100644 --- a/docs/eclipse.md +++ b/docs/eclipse.md @@ -1,62 +1,61 @@ # Building on Eclipse -This library only supports Android Studio and Gradle. +This library and samples basically support Android Studio and Gradle. Because they have strong power to handle dependencies and ability to configure flexibly, and this library and sample app depend on them. -However, if you really want to build it on Eclipse, here are some hints. -These are not the complete and precise instructions but it might help you. - -If you feel these complicated instructions annoying, then maybe it's time to use the Android Studio. - -## Import library to your project - -1. Install the following components on SDK manager. - * Android 5.0 SDK Platform (Rev.1+) - * Android Support Repository (Rev.9+) - * Android Support Library (Rev.21.0.2+) -1. Import library project of this lib (`observablescrollview` directory) to your workspace. - * Import with "Import Android Code Into Workspace". - * Add `android-support-v7-recyclerview.jar` to build path. - Jar file is located here: - `/path/to/sdk/extra/android/support/v7/recyclerview/libs/android-support-v7-recyclerview.jar` - -## Build the sample app - -After importing library to your workspace, import other dependencies and the sample app. - -1. Import "android-support-v7-appcompat" project to your workspace. - * This project is located in the following path: - `/path/to/sdk/extra/android/support/v7/appcompat` - * See [here](https://developer.android.com/tools/support-library/setup.html#libs-with-res) for details: - * Note that you must modify `target=android-19` to `target=android-21` on `project.properties`. - * Also add `android-support-v4.jar` and `android-support-v7-appcompat.jar` to build path. - They also should be exported. (On "Java Build Path > Order and Export" setting) -1. Import [NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids/) library to your workspace. - * Import "library" directory with "Import Android Code Into Workspace". -1. Import [FloatingActionButton](https://github.com/makovkastar/FloatingActionButton) library to your workspace. - * This lib also uses Android Studio(Gradle), - so import `library` directory with "Import Android Code Into Workspace". - * This depends on "NineOldAndroids" library, so add it as a library. - * Also add `android-support-v4.jar` and `android-support-v7-recyclerview.jar` to build path. - They also should be exported. (On "Java Build Path > Order and Export" setting) -1. Import sample project of this lib (`observablescrollview-samples` directory) to your workspace. - * Import with `Import Android Code Into Workspace`. - * Add `android-support-v4.jar` and `android-support-v7-recyclerview.jar` to build path. - * This depends on "android-support-v7-appcompat", - "FloatingActionButton" and "observablescrollview" libraries, - so add them as a library. - * Sample codes depends on Gradle build mechaninsm, and some codes should be modified: - * On `AndroidManifest.xml`, - * Add `android:versionName` and `android:versionCode` attributes to `` element. - * Replace all `` to ``. - * Add `android:targetSdkVersion="19"` to `` element. - * On `MainActivity`, replace value of `CATEGORY_SAMPLES` to `"com.github.ksoichiro.android.observablescrollview.samples"`. - * On `AboutActivity`, remove the following lines. - * `((TextView) -findViewById(R.id.app_version)).setText(getString(R.string.msg_app_version, -BuildConfig.VERSION_NAME, BuildConfig.GIT_HASH));` - * `((TextView) -findViewById(R.id.lib_version)).setText(getString(R.string.msg_lib_version, -BuildConfig.LIB_VERSION));` +However, some of you might still want to build or debug the project on Eclipse. +If you'd like to do that, please try the following instructions. + +Please note that with these instructions you could bulid project on Eclipse, but test codes, build types ('debug' or 'release') and product flavors are still not supported. + +## Prerequisites + +* [Eclipse IDE for Java Developers 4.4 (Luna) SR1](https://eclipse.org/downloads/packages/eclipse-ide-java-developers/lunasr1a) +* [Eclipse ADT Plugin](http://developer.android.com/sdk/installing/installing-adt.html) +* Oracle JDK 7 +* Android 5.0 SDK Platform (Rev.1+) +* Android Support Repository (Rev.9+) +* Android Support Library (Rev.21.0.2+) + +## Instructions + +### Get the source codes + +``` +$ git clone https://github.com/ksoichiro/Android-ObservableScrollView.git +``` + +### Define ANDROID_HOME environment variable + +If you haven't define the environment variable `ANDROID_HOME` yet, define it to indicate Android SDK root directory. + +### Generate dependency codes for Eclipse + +Before trying to import projects to Eclipse, +execute these command: + +``` +$ ./gradlew clean generateVersionInfoDebug generateEclipseDependencies +``` + +This will generate dependency codes from AAR files using Gradle wrapper and some metadata files (`.classpath`, `.project`, `project.properties`). + +### Import projects to Eclipse and build app + +1. Launch Eclipse. +1. Select `File` > `Import`. +1. Select `General` > `Existing Projects into Workspace` and click `Next`. + * Warning: DO NOT `Android` > `Existing Android Code into Workspace`. +1. Click `Browse` and select project root directory (`Android-ObservableScrollView`). +1. Check `Search` for nested projects. +1. Select all projects and click next. +1. Some warning messages will be generated, but ignore them and wait until build finishes. + +### Run the app + +1. Confirm your device is connected. +1. Right click `observablescrollview-samples` and select `Run As` > `Android Application`. + +That's all! + \ No newline at end of file From cef0f1c53b7c466358fbb276c354e571da638cec Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 25 Feb 2015 20:50:44 +0900 Subject: [PATCH 028/208] Apply gradle-eclipse-aar-plugin for Eclipse build. --- .gitignore | 3 + build.gradle | 5 + eclipse.gradle | 273 ------------------ observablescrollview-samples/.classpath | 17 -- observablescrollview-samples/.project | 34 --- observablescrollview-samples/build.gradle | 2 - .../project.properties | 19 -- observablescrollview/.classpath | 10 - observablescrollview/.project | 34 --- observablescrollview/build.gradle | 2 - observablescrollview/project.properties | 17 -- 11 files changed, 8 insertions(+), 408 deletions(-) delete mode 100644 eclipse.gradle delete mode 100644 observablescrollview-samples/.classpath delete mode 100644 observablescrollview-samples/.project delete mode 100644 observablescrollview-samples/project.properties delete mode 100644 observablescrollview/.classpath delete mode 100644 observablescrollview/.project delete mode 100644 observablescrollview/project.properties diff --git a/.gitignore b/.gitignore index 73557b8c..a1383ac2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ bin/ gen/ libs/ aarDependencies/ +.project +.classpath +project.properties diff --git a/build.gradle b/build.gradle index f694d3ab..d206504e 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,9 @@ buildscript { repositories { mavenCentral() } + dependencies { + classpath 'com.github.ksoichiro:gradle-eclipse-aar-plugin:0.1.0' + } } allprojects { @@ -11,3 +14,5 @@ allprojects { mavenCentral() } } + +apply plugin: 'com.github.ksoichiro.eclipse.aar' diff --git a/eclipse.gradle b/eclipse.gradle deleted file mode 100644 index 4db0f2eb..00000000 --- a/eclipse.gradle +++ /dev/null @@ -1,273 +0,0 @@ -// Eclipse supporting script based on: -// http://www.nodeclipse.org/projects/gradle/android/aar-for-Eclipse - -project.ext.eclipseSupport = new Expando() -project.ext.eclipseSupport.androidTarget = 'android-21' -project.ext.eclipseSupport.aarDependenciesDir = 'aarDependencies' -project.ext.eclipseSupport.jarDependencies = [] as Set -project.ext.eclipseSupport.aarDependencies = [] as Set - -task cleanEclipseDependencies { - description = 'Used for Eclipse. Cleans dependencies directory.' - doLast { - [ project.file(project.ext.eclipseSupport.aarDependenciesDir), project.file('libs') ].each { - if (it.exists()) { - project.delete(it) - println "Deleted ${it}" - } - } - } -} - -task generateEclipseDependencies(dependsOn: 'cleanEclipseDependencies') { - description = 'Used for Eclipse. Copies all dependencies for library directory.' - doLast { - def aggregateJarFrom = { task -> - println "Aggregating JAR dependencies from ${task.name} configuration" - task.filter { - it.name.endsWith 'jar' - }.each { File jar -> project.ext.eclipseSupport.jarDependencies << jar } - } - - def aggregateAarFrom = { task -> - println "Aggregating AAR dependencies from ${task.name} configuration" - task.filter { File aar -> - aar.name.endsWith('aar') && isRequired(aar) - }.each { File aar -> project.ext.eclipseSupport.aarDependencies << aar } - } - - // Ignoring release and androidTest configurations. - project.parent.subprojects.each { - [it.configurations.compile, it.configurations.debugCompile].each { - aggregateJarFrom it - aggregateAarFrom it - } - } - - def extractJarFrom = { task -> - println "Extracting JAR dependencies from ${task.name} configuration" - task.filter { - it.name.endsWith 'jar' - }.each { File jar -> moveJarIntoLibs(jar) } - } - - def extractAarFrom = { task -> - println "Extracting AAR dependencies from ${task.name} configuration" - task.filter { File aar -> - aar.name.endsWith('aar') && isRequired(aar) - }.each { File aar -> moveAndRenameAar(aar) } - } - - project.parent.subprojects.each { - [it.configurations.compile, it.configurations.debugCompile].each { - extractJarFrom it - extractAarFrom it - } - } - - project.file(project.ext.eclipseSupport.aarDependenciesDir).listFiles().findAll { - it.isDirectory() - }.each { aar -> - generateProjectPropertiesFile(aar) - generateEclipseClasspathFile(aar) - generateEclipseProjectFile(aar) - } - } -} - -boolean isRequired(File aar) { - def name = getDependencyProjectName(aar) - boolean result = false - project.file('project.properties').eachLine { String line -> - if (line.matches("^android.library.reference.[0-9]+=${project.ext.eclipseSupport.aarDependenciesDir}/${name}")) { - result = true - } - } - result -} - -String getDependencyProjectName(File file) { - file.name.lastIndexOf('.').with { it != -1 ? file.name[0.. - copy { - from file - into destDir - } - }) -} - -void moveAndRenameAar(File file) { - println "Added aar ${file}" - def dependencyProjectName = getDependencyProjectName(file) - - // directory excluding the classes.jar - copy { - from zipTree(file) - exclude 'classes.jar' - into "${project.ext.eclipseSupport.aarDependenciesDir}/${dependencyProjectName}" - } - - // Copies the classes.jar into the libs directory of the exploded AAR. - // In Eclipse you can then import this exploded ar as an Android project - // and then reference not only the classes but also the android resources :D - ["${project.ext.eclipseSupport.aarDependenciesDir}/${dependencyProjectName}/libs", "libs"].each { dest -> - def jarFileName = "${dependencyProjectName}.jar" - copyJarIfNewer(dest, file, true, { destDir -> - copy { - from zipTree(file) - include 'classes.jar' - into destDir - rename { String fileName -> - fileName.replace('classes.jar', jarFileName) - } - } - }) - } -} - -void copyJarIfNewer(String libsDir, File dependency, boolean isAarDependency, Closure copyClosure) { - def dependencyFilename = dependency.name - def dependencyProjectName = getDependencyProjectName(dependency) - def dependencyName = getDependencyName(dependencyFilename) - def versionName = getVersionName(dependencyFilename) - boolean isNewer = false - boolean sameDependencyExists = false - def dependencies = isAarDependency ? project.ext.eclipseSupport.aarDependencies : project.ext.eclipseSupport.jarDependencies - dependencies.findAll { File it -> - // Check if there are any dependencies with the same name but different version - getDependencyName(it.name) == dependencyName && getVersionName(it.name) != versionName - }.each { File file -> - println " Same dependency exists: ${dependencyFilename}, ${file.name}" - sameDependencyExists = true - def v1 = getVersionName(dependencyFilename) - def v2 = getVersionName(file.name) - // 'file' may be removed in previous loop - if (file.exists() && v1.isNewerThan(v2)) { - println " Found older dependency. Copy ${dependencyFilename} to all subprojects" - isNewer = true - // Should be replaced to jarFilename jar - project.parent.subprojects.each { subProject2 -> - def subProjectLibDir = subProject2.file('libs') - if (isAarDependency) { - subProjectLibDir.listFiles().findAll { - it.isDirectory() && getDependencyName(it.name) == dependencyName - }.each { File lib -> - println " REMOVED ${lib}" - project.delete(lib) - copy { - from zipTree(dependency) - exclude 'classes.jar' - into "${project.ext.eclipseSupport.aarDependenciesDir}/${dependencyProjectName}" - } - copyClosure(subProjectLibDir) - } - } else { - subProjectLibDir.listFiles().findAll { - !it.isDirectory() && getDependencyName(it.name) == dependencyName - }.each { File lib -> - println " REMOVED ${lib}" - project.delete(lib) - copyClosure(subProjectLibDir) - } - } - } - } - } - if (!sameDependencyExists || isNewer) { - println " Copy new dependency: ${dependencyFilename}" - copyClosure(libsDir) - } -} - -String getBaseName(String filename) { - filename.lastIndexOf('.').with { it != -1 ? filename[0.. - String v1 = delegate - def versions1 = v1.tokenize('.') - def versions2 = v2.tokenize('.') - for (int i = 0; i < Math.min(versions1.size(), versions2.size()); i++) { - int n1 = versions1[i].toInteger() - int n2 = versions2[i].toInteger() - if (n2 < n1) { - return true - } - } - versions2.size() < versions1.size() -} - -void generateProjectPropertiesFile(File aar) { - project.file("${project.ext.eclipseSupport.aarDependenciesDir}/${aar.name}/project.properties").text = """\ -target=${project.ext.eclipseSupport.androidTarget} -android.library=true -""" -} - -void generateEclipseClasspathFile(File aar) { - project.file("${project.ext.eclipseSupport.aarDependenciesDir}/${aar.name}/.classpath").text = """\ - - - - - - - - - -""" -} - -void generateEclipseProjectFile(File aar) { - def name = aar.name - project.file("${project.ext.eclipseSupport.aarDependenciesDir}/${name}/.project").text = """\ - - - ${project.name}-${name} - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - org.springsource.ide.eclipse.gradle.core.nature - org.eclipse.jdt.core.javanature - com.android.ide.eclipse.adt.AndroidNature - - -""" -} diff --git a/observablescrollview-samples/.classpath b/observablescrollview-samples/.classpath deleted file mode 100644 index 5a25fe17..00000000 --- a/observablescrollview-samples/.classpath +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/observablescrollview-samples/.project b/observablescrollview-samples/.project deleted file mode 100644 index 7a6e10b8..00000000 --- a/observablescrollview-samples/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - observablescrollview-samples - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - org.springsource.ide.eclipse.gradle.core.nature - org.eclipse.jdt.core.javanature - com.android.ide.eclipse.adt.AndroidNature - - diff --git a/observablescrollview-samples/build.gradle b/observablescrollview-samples/build.gradle index 1c2d652c..53e5c81e 100644 --- a/observablescrollview-samples/build.gradle +++ b/observablescrollview-samples/build.gradle @@ -109,5 +109,3 @@ android { output.outputFile = new File(apk.parentFile, newName) } } - -apply from: '../eclipse.gradle' diff --git a/observablescrollview-samples/project.properties b/observablescrollview-samples/project.properties deleted file mode 100644 index c4fb55af..00000000 --- a/observablescrollview-samples/project.properties +++ /dev/null @@ -1,19 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-21 -android.library.reference.1=../observablescrollview -android.library.reference.2=aarDependencies/support-v4-21.0.2 -android.library.reference.3=aarDependencies/floatingactionbutton-1.0.7 -android.library.reference.4=aarDependencies/recyclerview-v7-21.0.0 -android.library.reference.5=aarDependencies/appcompat-v7-21.0.2 diff --git a/observablescrollview/.classpath b/observablescrollview/.classpath deleted file mode 100644 index 0779d73f..00000000 --- a/observablescrollview/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/observablescrollview/.project b/observablescrollview/.project deleted file mode 100644 index 74fc38a4..00000000 --- a/observablescrollview/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - observablescrollview - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - org.springsource.ide.eclipse.gradle.core.nature - org.eclipse.jdt.core.javanature - com.android.ide.eclipse.adt.AndroidNature - - diff --git a/observablescrollview/build.gradle b/observablescrollview/build.gradle index 3b8e4820..c673061d 100644 --- a/observablescrollview/build.gradle +++ b/observablescrollview/build.gradle @@ -59,5 +59,3 @@ coveralls.jacocoReportPath = 'build/outputs/reports/coverage/debug/report.xml' // This is from 'https://github.com/chrisbanes/gradle-mvn-push' apply from: 'gradle-mvn-push.gradle' - -apply from: '../eclipse.gradle' diff --git a/observablescrollview/project.properties b/observablescrollview/project.properties deleted file mode 100644 index e8098713..00000000 --- a/observablescrollview/project.properties +++ /dev/null @@ -1,17 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-21 -android.library=true -android.library.reference.1=aarDependencies/recyclerview-v7-21.0.0 -android.library.reference.2=aarDependencies/support-v4-21.0.2 From 3633f1408f223509bc1f16d08d55a7819209247a Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 27 Feb 2015 01:00:36 +0900 Subject: [PATCH 029/208] #59 Add "Flexible space with image with ViewPagerTab" example based on ViewPagerTab2Activity. --- .../AndroidManifest.xml | 10 + ...lexiblespacewithimagewithviewpagertab2.xml | 95 +++++++ .../res/values/strings.xml | 1 + ...aceWithImageWithViewPagerTab2Activity.java | 269 ++++++++++++++++++ 4 files changed, 375 insertions(+) create mode 100644 observablescrollview-samples/res/layout/activity_flexiblespacewithimagewithviewpagertab2.xml create mode 100644 observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java diff --git a/observablescrollview-samples/AndroidManifest.xml b/observablescrollview-samples/AndroidManifest.xml index 6f95311e..513ad4c5 100644 --- a/observablescrollview-samples/AndroidManifest.xml +++ b/observablescrollview-samples/AndroidManifest.xml @@ -196,6 +196,16 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/observablescrollview-samples/res/values/strings.xml b/observablescrollview-samples/res/values/strings.xml index ad39bf57..335bd608 100644 --- a/observablescrollview-samples/res/values/strings.xml +++ b/observablescrollview-samples/res/values/strings.xml @@ -25,6 +25,7 @@ ScrollView & Action Bar WebView & Action Bar Flexible Space + Flexible Space Flexible Space Another implementation of Fill Gap & ListView Another implementation of Fill Gap & RecyclerView diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java new file mode 100644 index 00000000..9f1e958d --- /dev/null +++ b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java @@ -0,0 +1,269 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.annotation.TargetApi; +import android.content.res.Configuration; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPager; +import android.support.v7.widget.Toolbar; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.github.ksoichiro.android.observablescrollview.Scrollable; +import com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout; +import com.google.samples.apps.iosched.ui.widget.SlidingTabLayout; +import com.nineoldandroids.view.ViewHelper; + +/** + * This uses TouchInterceptionFrameLayout to move Fragments. + *

+ * SlidingTabLayout and SlidingTabStrip are from google/iosched: + * https://github.com/google/iosched + */ +public class FlexibleSpaceWithImageWithViewPagerTab2Activity extends BaseActivity implements ObservableScrollViewCallbacks { + + private static final float MAX_TEXT_SCALE_DELTA = 0.3f; + + private View mImageView; + private View mOverlayView; + private TextView mTitleView; + private TouchInterceptionFrameLayout mInterceptionLayout; + private ViewPager mPager; + private NavigationAdapter mPagerAdapter; + private int mSlop; + private int mFlexibleSpaceHeight; + private int mTabHeight; + private boolean mScrolled; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_flexiblespacewithimagewithviewpagertab2); + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + + ViewCompat.setElevation(findViewById(R.id.header), getResources().getDimension(R.dimen.toolbar_elevation)); + mPagerAdapter = new NavigationAdapter(getSupportFragmentManager()); + mPager = (ViewPager) findViewById(R.id.pager); + mPager.setAdapter(mPagerAdapter); + mImageView = findViewById(R.id.image); + mOverlayView = findViewById(R.id.overlay); + // Padding for ViewPager must be set outside the ViewPager itself + // because with padding, EdgeEffect of ViewPager become strange. + mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + mTabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); + findViewById(R.id.pager_wrapper).setPadding(0, mFlexibleSpaceHeight, 0, 0); + mTitleView = (TextView) findViewById(R.id.title); + mTitleView.setText(getTitle()); + setTitle(null); + + SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs); + slidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1); + slidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent)); + slidingTabLayout.setDistributeEvenly(true); + slidingTabLayout.setViewPager(mPager); + ((FrameLayout.LayoutParams) slidingTabLayout.getLayoutParams()).topMargin = mFlexibleSpaceHeight - mTabHeight; + + ViewConfiguration vc = ViewConfiguration.get(this); + mSlop = vc.getScaledTouchSlop(); + mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.container); + mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener); + ScrollUtils.addOnGlobalLayoutListener(mInterceptionLayout, new Runnable() { + @Override + public void run() { + updateFlexibleSpace(); + } + }); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + + private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() { + @Override + public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) { + if (!mScrolled && mSlop < Math.abs(diffX) && Math.abs(diffY) < Math.abs(diffX)) { + // Horizontal scroll is maybe handled by ViewPager + return false; + } + + Scrollable scrollable = getCurrentScrollable(); + if (scrollable == null) { + mScrolled = false; + return false; + } + + // If interceptionLayout can move, it should intercept. + // And once it begins to move, horizontal scroll shouldn't work any longer. + int flexibleSpace = mFlexibleSpaceHeight - mTabHeight; + int translationY = (int) ViewHelper.getTranslationY(mInterceptionLayout); + boolean scrollingUp = 0 < diffY; + boolean scrollingDown = diffY < 0; + if (scrollingUp) { + if (translationY < 0) { + mScrolled = true; + return true; + } + } else if (scrollingDown) { + if (-flexibleSpace < translationY) { + mScrolled = true; + return true; + } + } + mScrolled = false; + return false; + } + + @Override + public void onDownMotionEvent(MotionEvent ev) { + } + + @Override + public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) { + int flexibleSpace = mFlexibleSpaceHeight - mTabHeight; + float translationY = ScrollUtils.getFloat(ViewHelper.getTranslationY(mInterceptionLayout) + diffY, -flexibleSpace, 0); + updateFlexibleSpace(translationY); + if (translationY < 0) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams(); + lp.height = (int) (-translationY + getScreenHeight()); + mInterceptionLayout.requestLayout(); + } + } + + @Override + public void onUpOrCancelMotionEvent(MotionEvent ev) { + mScrolled = false; + } + }; + + private Scrollable getCurrentScrollable() { + Fragment fragment = getCurrentFragment(); + if (fragment == null) { + return null; + } + View view = fragment.getView(); + if (view == null) { + return null; + } + return (Scrollable) view.findViewById(R.id.scroll); + } + + private void updateFlexibleSpace() { + updateFlexibleSpace(ViewHelper.getTranslationY(mInterceptionLayout)); + } + + private void updateFlexibleSpace(float translationY) { + ViewHelper.setTranslationY(mInterceptionLayout, translationY); + int minOverlayTransitionY = getActionBarSize() - mOverlayView.getHeight(); + ViewHelper.setTranslationY(mImageView, ScrollUtils.getFloat(-translationY / 2, minOverlayTransitionY, 0)); + + // Change alpha of overlay + float flexibleRange = mFlexibleSpaceHeight - getActionBarSize(); + ViewHelper.setAlpha(mOverlayView, ScrollUtils.getFloat(-translationY / flexibleRange, 0, 1)); + + // Scale title text + float scale = 1 + ScrollUtils.getFloat((flexibleRange + translationY - mTabHeight) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA); + setPivotXToTitle(); + ViewHelper.setPivotY(mTitleView, 0); + ViewHelper.setScaleX(mTitleView, scale); + ViewHelper.setScaleY(mTitleView, scale); + } + + private Fragment getCurrentFragment() { + return mPagerAdapter.getItemAt(mPager.getCurrentItem()); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private void setPivotXToTitle() { + Configuration config = getResources().getConfiguration(); + if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT + && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + ViewHelper.setPivotX(mTitleView, findViewById(android.R.id.content).getWidth()); + } else { + ViewHelper.setPivotX(mTitleView, 0); + } + } + + /** + * This adapter provides two types of fragments as an example. + * {@linkplain #createItem(int)} should be modified if you use this example for your app. + */ + private static class NavigationAdapter extends CacheFragmentStatePagerAdapter { + + private static final String[] TITLES = new String[]{"Applepie", "Butter Cookie", "Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb", "Ice Cream Sandwich", "Jelly Bean", "KitKat", "Lollipop"}; + + public NavigationAdapter(FragmentManager fm) { + super(fm); + } + + @Override + protected Fragment createItem(int position) { + Fragment f; + final int pattern = position % 5; + switch (pattern) { + case 0: + f = new ViewPagerTab2ScrollViewFragment(); + break; + case 1: + f = new ViewPagerTab2ListViewFragment(); + break; + case 2: + f = new ViewPagerTab2RecyclerViewFragment(); + break; + case 3: + f = new ViewPagerTab2GridViewFragment(); + break; + case 4: + default: + f = new ViewPagerTab2WebViewFragment(); + break; + } + return f; + } + + @Override + public int getCount() { + return TITLES.length; + } + + @Override + public CharSequence getPageTitle(int position) { + return TITLES[position]; + } + } +} From 80c317538a64f584409c4f24fa996952756378a5 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 28 Feb 2015 21:07:07 +0900 Subject: [PATCH 030/208] Add API level badge. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a113d432..2ab012d8 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ Android-ObservableScrollView [![Build Status](http://img.shields.io/travis/ksoichiro/Android-ObservableScrollView.svg?style=flat)](https://travis-ci.org/ksoichiro/Android-ObservableScrollView) [![Coverage Status](https://img.shields.io/coveralls/ksoichiro/Android-ObservableScrollView/master.svg?style=flat)](https://coveralls.io/r/ksoichiro/Android-ObservableScrollView?branch=master) [![Maven Central](http://img.shields.io/maven-central/v/com.github.ksoichiro/android-observablescrollview.svg?style=flat)](https://github.com/ksoichiro/Android-ObservableScrollView/releases/latest) +[![API](https://img.shields.io/badge/API-9%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=9) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Android--ObservableScrollView-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1136) Android library to observe scroll events on scrollable views. From 0b5f4d2e3fa381a429263736813fe5b909d3ef08 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 1 Mar 2015 10:25:12 +0900 Subject: [PATCH 031/208] Add TouchInterceptionFrameLayout's test. --- .../src/androidTest/AndroidManifest.xml | 1 + .../test/TouchInterceptionActivityTest.java | 40 ++++++++ .../test/TouchIntereceptionActivity.java | 93 +++++++++++++++++++ .../res/layout/activity_touchinterception.xml | 75 +++++++++++++++ .../src/androidTest/res/values/dimens.xml | 3 + 5 files changed, 212 insertions(+) create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionActivityTest.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchIntereceptionActivity.java create mode 100644 observablescrollview/src/androidTest/res/layout/activity_touchinterception.xml diff --git a/observablescrollview/src/androidTest/AndroidManifest.xml b/observablescrollview/src/androidTest/AndroidManifest.xml index 7b536b5e..fa7bb38a 100644 --- a/observablescrollview/src/androidTest/AndroidManifest.xml +++ b/observablescrollview/src/androidTest/AndroidManifest.xml @@ -25,6 +25,7 @@ + diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionActivityTest.java new file mode 100644 index 00000000..44dd1172 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionActivityTest.java @@ -0,0 +1,40 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; + +public class TouchInterceptionActivityTest extends ActivityInstrumentationTestCase2 { + + private Activity activity; + private ObservableScrollView scrollable; + + public TouchInterceptionActivityTest() { + super(TouchIntereceptionActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable); + } + + public void testScroll() throws Throwable { + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + public void testSaveAndRestoreInstanceState() throws Throwable { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + testScroll(); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchIntereceptionActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchIntereceptionActivity.java new file mode 100644 index 00000000..8cd4cc66 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchIntereceptionActivity.java @@ -0,0 +1,93 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.view.MotionEvent; +import android.widget.FrameLayout; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.Scrollable; +import com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout; +import com.nineoldandroids.view.ViewHelper; + +public class TouchIntereceptionActivity extends Activity implements ObservableScrollViewCallbacks { + + private TouchInterceptionFrameLayout mInterceptionLayout; + private Scrollable mScrollable; + + private int mIntersectionHeight; + private int mHeaderBarHeight; + + private float mScrollYOnDownMotion; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_touchinterception); + mScrollable = (Scrollable) findViewById(R.id.scrollable); + mScrollable.setScrollViewCallbacks(this); + + mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height); + mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height); + + mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper); + mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + + private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() { + @Override + public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) { + final int minInterceptionLayoutY = -mIntersectionHeight; + return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout) + || (moving && mScrollable.getCurrentScrollY() - diffY < 0); + } + + @Override + public void onDownMotionEvent(MotionEvent ev) { + mScrollYOnDownMotion = mScrollable.getCurrentScrollY(); + } + + @Override + public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) { + float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY; + if (translationY < -mIntersectionHeight) { + translationY = -mIntersectionHeight; + } else if (getScreenHeight() - mHeaderBarHeight < translationY) { + translationY = getScreenHeight() - mHeaderBarHeight; + } + + slideTo(translationY, true); + } + + @Override + public void onUpOrCancelMotionEvent(MotionEvent ev) { + } + }; + + private void slideTo(float translationY, final boolean animated) { + ViewHelper.setTranslationY(mInterceptionLayout, translationY); + + if (translationY < 0) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams(); + lp.height = (int) -translationY + getScreenHeight(); + mInterceptionLayout.requestLayout(); + } + } + + private int getScreenHeight() { + return findViewById(android.R.id.content).getHeight(); + } +} diff --git a/observablescrollview/src/androidTest/res/layout/activity_touchinterception.xml b/observablescrollview/src/androidTest/res/layout/activity_touchinterception.xml new file mode 100644 index 00000000..9bc0cf6f --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_touchinterception.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/observablescrollview/src/androidTest/res/values/dimens.xml b/observablescrollview/src/androidTest/res/values/dimens.xml index f16a5655..286c5be1 100644 --- a/observablescrollview/src/androidTest/res/values/dimens.xml +++ b/observablescrollview/src/androidTest/res/values/dimens.xml @@ -15,4 +15,7 @@ --> 48dp + 72dp + 56dp + 16dp From 9cbc1fdacd8d9b9bedd82348c2cfdb702ab8cecd Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 1 Mar 2015 12:55:25 +0900 Subject: [PATCH 032/208] Move assets directory in sample app for Eclipse. --- .../{src/main => }/assets/handletouch.html | 0 observablescrollview-samples/{src/main => }/assets/lipsum.html | 0 observablescrollview-samples/build.gradle | 1 + 3 files changed, 1 insertion(+) rename observablescrollview-samples/{src/main => }/assets/handletouch.html (100%) rename observablescrollview-samples/{src/main => }/assets/lipsum.html (100%) diff --git a/observablescrollview-samples/src/main/assets/handletouch.html b/observablescrollview-samples/assets/handletouch.html similarity index 100% rename from observablescrollview-samples/src/main/assets/handletouch.html rename to observablescrollview-samples/assets/handletouch.html diff --git a/observablescrollview-samples/src/main/assets/lipsum.html b/observablescrollview-samples/assets/lipsum.html similarity index 100% rename from observablescrollview-samples/src/main/assets/lipsum.html rename to observablescrollview-samples/assets/lipsum.html diff --git a/observablescrollview-samples/build.gradle b/observablescrollview-samples/build.gradle index 53e5c81e..8f537aed 100644 --- a/observablescrollview-samples/build.gradle +++ b/observablescrollview-samples/build.gradle @@ -93,6 +93,7 @@ android { main { manifest.srcFile 'AndroidManifest.xml' res.srcDirs = ['res'] + assets.srcDirs = ['assets'] } } From 3c65189f6d852e57b727cfec59da882f15c7cc6f Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 1 Mar 2015 15:33:49 +0900 Subject: [PATCH 033/208] Add TouchInterceptionFrameLayout's test. --- .../src/androidTest/AndroidManifest.xml | 6 +- .../TouchInterceptionGridViewActivity.java | 97 +++++++++++++++++ ...TouchInterceptionGridViewActivityTest.java | 44 ++++++++ .../TouchInterceptionListViewActivity.java | 97 +++++++++++++++++ ...TouchInterceptionListViewActivityTest.java | 44 ++++++++ ...TouchInterceptionRecyclerViewActivity.java | 102 ++++++++++++++++++ ...hInterceptionRecyclerViewActivityTest.java | 45 ++++++++ ... TouchInterceptionScrollViewActivity.java} | 6 +- ...chInterceptionScrollViewActivityTest.java} | 10 +- .../TouchInterceptionWebViewActivity.java | 97 +++++++++++++++++ .../TouchInterceptionWebViewActivityTest.java | 44 ++++++++ .../activity_touchinterception_gridview.xml | 67 ++++++++++++ .../activity_touchinterception_listview.xml | 67 ++++++++++++ ...ctivity_touchinterception_recyclerview.xml | 66 ++++++++++++ ...activity_touchinterception_scrollview.xml} | 0 .../activity_touchinterception_webview.xml | 66 ++++++++++++ 16 files changed, 852 insertions(+), 6 deletions(-) create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivityTest.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivityTest.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivityTest.java rename observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/{TouchIntereceptionActivity.java => TouchInterceptionScrollViewActivity.java} (92%) rename observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/{TouchInterceptionActivityTest.java => TouchInterceptionScrollViewActivityTest.java} (75%) create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivity.java create mode 100644 observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivityTest.java create mode 100644 observablescrollview/src/androidTest/res/layout/activity_touchinterception_gridview.xml create mode 100644 observablescrollview/src/androidTest/res/layout/activity_touchinterception_listview.xml create mode 100644 observablescrollview/src/androidTest/res/layout/activity_touchinterception_recyclerview.xml rename observablescrollview/src/androidTest/res/layout/{activity_touchinterception.xml => activity_touchinterception_scrollview.xml} (100%) create mode 100644 observablescrollview/src/androidTest/res/layout/activity_touchinterception_webview.xml diff --git a/observablescrollview/src/androidTest/AndroidManifest.xml b/observablescrollview/src/androidTest/AndroidManifest.xml index fa7bb38a..0bbcb891 100644 --- a/observablescrollview/src/androidTest/AndroidManifest.xml +++ b/observablescrollview/src/androidTest/AndroidManifest.xml @@ -25,7 +25,11 @@ - + + + + + diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivity.java new file mode 100644 index 00000000..5a0891c8 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivity.java @@ -0,0 +1,97 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.view.MotionEvent; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.Scrollable; +import com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout; +import com.nineoldandroids.view.ViewHelper; + +public class TouchInterceptionGridViewActivity extends Activity implements ObservableScrollViewCallbacks { + + private TouchInterceptionFrameLayout mInterceptionLayout; + private Scrollable mScrollable; + + private int mIntersectionHeight; + private int mHeaderBarHeight; + + private float mScrollYOnDownMotion; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_touchinterception_gridview); + ((TextView) findViewById(R.id.title)).setText(getClass().getSimpleName()); + mScrollable = (Scrollable) findViewById(R.id.scrollable); + mScrollable.setScrollViewCallbacks(this); + UiTestUtils.setDummyData(this, (ObservableGridView) mScrollable); + + mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height); + mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height); + + mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper); + mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + + private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() { + @Override + public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) { + final int minInterceptionLayoutY = -mIntersectionHeight; + return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout) + || (moving && mScrollable.getCurrentScrollY() - diffY < 0); + } + + @Override + public void onDownMotionEvent(MotionEvent ev) { + mScrollYOnDownMotion = mScrollable.getCurrentScrollY(); + } + + @Override + public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) { + float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY; + if (translationY < -mIntersectionHeight) { + translationY = -mIntersectionHeight; + } else if (getScreenHeight() - mHeaderBarHeight < translationY) { + translationY = getScreenHeight() - mHeaderBarHeight; + } + + slideTo(translationY, true); + } + + @Override + public void onUpOrCancelMotionEvent(MotionEvent ev) { + } + }; + + private void slideTo(float translationY, final boolean animated) { + ViewHelper.setTranslationY(mInterceptionLayout, translationY); + + if (translationY < 0) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams(); + lp.height = (int) -translationY + getScreenHeight(); + mInterceptionLayout.requestLayout(); + } + } + + private int getScreenHeight() { + return findViewById(android.R.id.content).getHeight(); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivityTest.java new file mode 100644 index 00000000..381f2479 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivityTest.java @@ -0,0 +1,44 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; +import android.test.TouchUtils; + +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; + +public class TouchInterceptionGridViewActivityTest extends ActivityInstrumentationTestCase2 { + + private Activity activity; + private ObservableGridView scrollable; + + public TouchInterceptionGridViewActivityTest() { + super(TouchInterceptionGridViewActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable); + } + + public void testScroll() throws Throwable { + TouchUtils.touchAndCancelView(this, scrollable); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + public void testSaveAndRestoreInstanceState() throws Throwable { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + testScroll(); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivity.java new file mode 100644 index 00000000..3b800748 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivity.java @@ -0,0 +1,97 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.view.MotionEvent; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.Scrollable; +import com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout; +import com.nineoldandroids.view.ViewHelper; + +public class TouchInterceptionListViewActivity extends Activity implements ObservableScrollViewCallbacks { + + private TouchInterceptionFrameLayout mInterceptionLayout; + private Scrollable mScrollable; + + private int mIntersectionHeight; + private int mHeaderBarHeight; + + private float mScrollYOnDownMotion; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_touchinterception_listview); + ((TextView) findViewById(R.id.title)).setText(getClass().getSimpleName()); + mScrollable = (Scrollable) findViewById(R.id.scrollable); + mScrollable.setScrollViewCallbacks(this); + UiTestUtils.setDummyData(this, (ObservableListView) mScrollable); + + mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height); + mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height); + + mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper); + mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + + private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() { + @Override + public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) { + final int minInterceptionLayoutY = -mIntersectionHeight; + return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout) + || (moving && mScrollable.getCurrentScrollY() - diffY < 0); + } + + @Override + public void onDownMotionEvent(MotionEvent ev) { + mScrollYOnDownMotion = mScrollable.getCurrentScrollY(); + } + + @Override + public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) { + float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY; + if (translationY < -mIntersectionHeight) { + translationY = -mIntersectionHeight; + } else if (getScreenHeight() - mHeaderBarHeight < translationY) { + translationY = getScreenHeight() - mHeaderBarHeight; + } + + slideTo(translationY, true); + } + + @Override + public void onUpOrCancelMotionEvent(MotionEvent ev) { + } + }; + + private void slideTo(float translationY, final boolean animated) { + ViewHelper.setTranslationY(mInterceptionLayout, translationY); + + if (translationY < 0) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams(); + lp.height = (int) -translationY + getScreenHeight(); + mInterceptionLayout.requestLayout(); + } + } + + private int getScreenHeight() { + return findViewById(android.R.id.content).getHeight(); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivityTest.java new file mode 100644 index 00000000..430b7759 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivityTest.java @@ -0,0 +1,44 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; +import android.test.TouchUtils; + +import com.github.ksoichiro.android.observablescrollview.ObservableListView; + +public class TouchInterceptionListViewActivityTest extends ActivityInstrumentationTestCase2 { + + private Activity activity; + private ObservableListView scrollable; + + public TouchInterceptionListViewActivityTest() { + super(TouchInterceptionListViewActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableListView) activity.findViewById(R.id.scrollable); + } + + public void testScroll() throws Throwable { + TouchUtils.touchAndCancelView(this, scrollable); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + public void testSaveAndRestoreInstanceState() throws Throwable { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + testScroll(); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivity.java new file mode 100644 index 00000000..a7b80620 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivity.java @@ -0,0 +1,102 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.view.MotionEvent; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.Scrollable; +import com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout; +import com.nineoldandroids.view.ViewHelper; + +public class TouchInterceptionRecyclerViewActivity extends Activity implements ObservableScrollViewCallbacks { + + private TouchInterceptionFrameLayout mInterceptionLayout; + private Scrollable mScrollable; + + private int mIntersectionHeight; + private int mHeaderBarHeight; + + private float mScrollYOnDownMotion; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_touchinterception_recyclerview); + ((TextView) findViewById(R.id.title)).setText(getClass().getSimpleName()); + mScrollable = (Scrollable) findViewById(R.id.scrollable); + mScrollable.setScrollViewCallbacks(this); + ObservableRecyclerView recyclerView = (ObservableRecyclerView) mScrollable; + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setHasFixedSize(true); + recyclerView.setScrollViewCallbacks(this); + UiTestUtils.setDummyData(this, recyclerView); + + mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height); + mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height); + + mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper); + mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + + private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() { + @Override + public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) { + final int minInterceptionLayoutY = -mIntersectionHeight; + return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout) + || (moving && mScrollable.getCurrentScrollY() - diffY < 0); + } + + @Override + public void onDownMotionEvent(MotionEvent ev) { + mScrollYOnDownMotion = mScrollable.getCurrentScrollY(); + } + + @Override + public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) { + float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY; + if (translationY < -mIntersectionHeight) { + translationY = -mIntersectionHeight; + } else if (getScreenHeight() - mHeaderBarHeight < translationY) { + translationY = getScreenHeight() - mHeaderBarHeight; + } + + slideTo(translationY, true); + } + + @Override + public void onUpOrCancelMotionEvent(MotionEvent ev) { + } + }; + + private void slideTo(float translationY, final boolean animated) { + ViewHelper.setTranslationY(mInterceptionLayout, translationY); + + if (translationY < 0) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams(); + lp.height = (int) -translationY + getScreenHeight(); + mInterceptionLayout.requestLayout(); + } + } + + private int getScreenHeight() { + return findViewById(android.R.id.content).getHeight(); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivityTest.java new file mode 100644 index 00000000..caf54346 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivityTest.java @@ -0,0 +1,45 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; +import android.test.TouchUtils; + +import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; + +public class TouchInterceptionRecyclerViewActivityTest extends ActivityInstrumentationTestCase2 { + + private Activity activity; + private ObservableRecyclerView scrollable; + + public TouchInterceptionRecyclerViewActivityTest() { + super(TouchInterceptionRecyclerViewActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableRecyclerView) activity.findViewById(R.id.scrollable); + } + + public void testScroll() throws Throwable { + TouchUtils.touchAndCancelView(this, scrollable); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + public void testSaveAndRestoreInstanceState() throws Throwable { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + testScroll(); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchIntereceptionActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivity.java similarity index 92% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchIntereceptionActivity.java rename to observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivity.java index 8cd4cc66..f2b9c5cb 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchIntereceptionActivity.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivity.java @@ -4,6 +4,7 @@ import android.os.Bundle; import android.view.MotionEvent; import android.widget.FrameLayout; +import android.widget.TextView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; import com.github.ksoichiro.android.observablescrollview.ScrollState; @@ -11,7 +12,7 @@ import com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout; import com.nineoldandroids.view.ViewHelper; -public class TouchIntereceptionActivity extends Activity implements ObservableScrollViewCallbacks { +public class TouchInterceptionScrollViewActivity extends Activity implements ObservableScrollViewCallbacks { private TouchInterceptionFrameLayout mInterceptionLayout; private Scrollable mScrollable; @@ -24,7 +25,8 @@ public class TouchIntereceptionActivity extends Activity implements ObservableSc @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_touchinterception); + setContentView(R.layout.activity_touchinterception_scrollview); + ((TextView) findViewById(R.id.title)).setText(getClass().getSimpleName()); mScrollable = (Scrollable) findViewById(R.id.scrollable); mScrollable.setScrollViewCallbacks(this); diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java similarity index 75% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionActivityTest.java rename to observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java index 44dd1172..1c56fd40 100644 --- a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionActivityTest.java +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java @@ -2,16 +2,17 @@ import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; +import android.test.TouchUtils; import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; -public class TouchInterceptionActivityTest extends ActivityInstrumentationTestCase2 { +public class TouchInterceptionScrollViewActivityTest extends ActivityInstrumentationTestCase2 { private Activity activity; private ObservableScrollView scrollable; - public TouchInterceptionActivityTest() { - super(TouchIntereceptionActivity.class); + public TouchInterceptionScrollViewActivityTest() { + super(TouchInterceptionScrollViewActivity.class); } @Override @@ -23,6 +24,9 @@ protected void setUp() throws Exception { } public void testScroll() throws Throwable { + TouchUtils.touchAndCancelView(this, scrollable); + getInstrumentation().waitForIdleSync(); + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); getInstrumentation().waitForIdleSync(); diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivity.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivity.java new file mode 100644 index 00000000..023905b1 --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivity.java @@ -0,0 +1,97 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.view.MotionEvent; +import android.webkit.WebView; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.Scrollable; +import com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout; +import com.nineoldandroids.view.ViewHelper; + +public class TouchInterceptionWebViewActivity extends Activity implements ObservableScrollViewCallbacks { + + private TouchInterceptionFrameLayout mInterceptionLayout; + private Scrollable mScrollable; + + private int mIntersectionHeight; + private int mHeaderBarHeight; + + private float mScrollYOnDownMotion; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_touchinterception_webview); + ((TextView) findViewById(R.id.title)).setText(getClass().getSimpleName()); + mScrollable = (Scrollable) findViewById(R.id.scrollable); + mScrollable.setScrollViewCallbacks(this); + ((WebView) mScrollable).loadUrl("file:///android_asset/lipsum.html"); + + mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height); + mHeaderBarHeight = getResources().getDimensionPixelSize(R.dimen.header_bar_height); + + mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper); + mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + + private TouchInterceptionFrameLayout.TouchInterceptionListener mInterceptionListener = new TouchInterceptionFrameLayout.TouchInterceptionListener() { + @Override + public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) { + final int minInterceptionLayoutY = -mIntersectionHeight; + return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout) + || (moving && mScrollable.getCurrentScrollY() - diffY < 0); + } + + @Override + public void onDownMotionEvent(MotionEvent ev) { + mScrollYOnDownMotion = mScrollable.getCurrentScrollY(); + } + + @Override + public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) { + float translationY = ViewHelper.getTranslationY(mInterceptionLayout) - mScrollYOnDownMotion + diffY; + if (translationY < -mIntersectionHeight) { + translationY = -mIntersectionHeight; + } else if (getScreenHeight() - mHeaderBarHeight < translationY) { + translationY = getScreenHeight() - mHeaderBarHeight; + } + + slideTo(translationY, true); + } + + @Override + public void onUpOrCancelMotionEvent(MotionEvent ev) { + } + }; + + private void slideTo(float translationY, final boolean animated) { + ViewHelper.setTranslationY(mInterceptionLayout, translationY); + + if (translationY < 0) { + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams(); + lp.height = (int) -translationY + getScreenHeight(); + mInterceptionLayout.requestLayout(); + } + } + + private int getScreenHeight() { + return findViewById(android.R.id.content).getHeight(); + } +} diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivityTest.java b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivityTest.java new file mode 100644 index 00000000..d1f24e4f --- /dev/null +++ b/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivityTest.java @@ -0,0 +1,44 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.test.ActivityInstrumentationTestCase2; +import android.test.TouchUtils; + +import com.github.ksoichiro.android.observablescrollview.ObservableWebView; + +public class TouchInterceptionWebViewActivityTest extends ActivityInstrumentationTestCase2 { + + private Activity activity; + private ObservableWebView scrollable; + + public TouchInterceptionWebViewActivityTest() { + super(TouchInterceptionWebViewActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableWebView) activity.findViewById(R.id.scrollable); + } + + public void testScroll() throws Throwable { + TouchUtils.touchAndCancelView(this, scrollable); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + public void testSaveAndRestoreInstanceState() throws Throwable { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + testScroll(); + } +} diff --git a/observablescrollview/src/androidTest/res/layout/activity_touchinterception_gridview.xml b/observablescrollview/src/androidTest/res/layout/activity_touchinterception_gridview.xml new file mode 100644 index 00000000..5077575f --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_touchinterception_gridview.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + diff --git a/observablescrollview/src/androidTest/res/layout/activity_touchinterception_listview.xml b/observablescrollview/src/androidTest/res/layout/activity_touchinterception_listview.xml new file mode 100644 index 00000000..be1d4026 --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_touchinterception_listview.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + diff --git a/observablescrollview/src/androidTest/res/layout/activity_touchinterception_recyclerview.xml b/observablescrollview/src/androidTest/res/layout/activity_touchinterception_recyclerview.xml new file mode 100644 index 00000000..3303b4c5 --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_touchinterception_recyclerview.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + diff --git a/observablescrollview/src/androidTest/res/layout/activity_touchinterception.xml b/observablescrollview/src/androidTest/res/layout/activity_touchinterception_scrollview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_touchinterception.xml rename to observablescrollview/src/androidTest/res/layout/activity_touchinterception_scrollview.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_touchinterception_webview.xml b/observablescrollview/src/androidTest/res/layout/activity_touchinterception_webview.xml new file mode 100644 index 00000000..6f697882 --- /dev/null +++ b/observablescrollview/src/androidTest/res/layout/activity_touchinterception_webview.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + From 8139aa04f335f2855357d608c7653f67e25644b1 Mon Sep 17 00:00:00 2001 From: mandybess Date: Fri, 6 Mar 2015 12:59:22 -0500 Subject: [PATCH 034/208] added a sample for flexible space with image for recycler views --- .../AndroidManifest.xml | 10 + ...ity_flexiblespacewithimagerecyclerview.xml | 66 +++++++ .../res/values/strings.xml | 1 + ...bleSpaceWithImageRecyclerViewActivity.java | 176 ++++++++++++++++++ 4 files changed, 253 insertions(+) create mode 100644 observablescrollview-samples/res/layout/activity_flexiblespacewithimagerecyclerview.xml create mode 100644 observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java diff --git a/observablescrollview-samples/AndroidManifest.xml b/observablescrollview-samples/AndroidManifest.xml index 513ad4c5..845aafa0 100644 --- a/observablescrollview-samples/AndroidManifest.xml +++ b/observablescrollview-samples/AndroidManifest.xml @@ -196,6 +196,16 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/observablescrollview-samples/res/values/strings.xml b/observablescrollview-samples/res/values/strings.xml index 335bd608..9ba24c08 100644 --- a/observablescrollview-samples/res/values/strings.xml +++ b/observablescrollview-samples/res/values/strings.xml @@ -33,6 +33,7 @@ Fill Gap & ListView Fill Gap & RecyclerView Fill Gap & ScrollView + Flexible Space Flexible Space ListView on Fragment & Action Bar ActionBar & Toolbar manipulation with Fragment diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java new file mode 100644 index 00000000..4a20e464 --- /dev/null +++ b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java @@ -0,0 +1,176 @@ +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.annotation.TargetApi; +import android.content.res.Configuration; +import android.graphics.Color; +import android.os.Build; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.nineoldandroids.view.ViewHelper; + +public class FlexibleSpaceWithImageRecyclerViewActivity extends BaseActivity implements ObservableScrollViewCallbacks { + + private static final float MAX_TEXT_SCALE_DELTA = 0.3f; + private static final boolean TOOLBAR_IS_STICKY = false; + + private View mToolbar; + private View mImageView; + private View mOverlayView; + private View mRecyclerViewBackground; + private ObservableRecyclerView mRecyclerView; + private TextView mTitleView; + private int mActionBarSize; + private int mFlexibleSpaceImageHeight; + private int mToolbarColor; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_flexiblespacewithimagerecyclerview); + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + + mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + mActionBarSize = getActionBarSize(); + mToolbarColor = getResources().getColor(R.color.primary); + + mRecyclerView = (ObservableRecyclerView) findViewById(R.id.recycler); + mRecyclerView.setScrollViewCallbacks(this); + mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + mRecyclerView.setHasFixedSize(false); + final View headerView = LayoutInflater.from(this).inflate(R.layout.recycler_header, null); + headerView.post(new Runnable() { + @Override + public void run() { + headerView.getLayoutParams().height = mFlexibleSpaceImageHeight; + } + }); + setDummyDataWithHeader(mRecyclerView, headerView); + + mToolbar = findViewById(R.id.toolbar); + if (!TOOLBAR_IS_STICKY) { + mToolbar.setBackgroundColor(Color.TRANSPARENT); + } + mImageView = findViewById(R.id.image); + mOverlayView = findViewById(R.id.overlay); + + mTitleView = (TextView) findViewById(R.id.title); + mTitleView.setText(getTitle()); + setTitle(null); + + // mRecyclerViewBackground makes RecyclerView's background except header view. + mRecyclerViewBackground = findViewById(R.id.list_background); + final View contentView = getWindow().getDecorView().findViewById(android.R.id.content); + contentView.post(new Runnable() { + @Override + public void run() { + // mRecylcerViewBackground's should fill its parent vertically + // but the height of the content view is 0 on 'onCreate'. + // So we should get it with post(). + mRecyclerViewBackground.getLayoutParams().height = contentView.getHeight(); + } + }); + + //since you cannot programatically add a headerview to a recyclerview we added an empty view as the header + // in the adapter and then are shifting the views OnCreateView to compensate + final float scale = 1 + MAX_TEXT_SCALE_DELTA; + mRecyclerViewBackground.post(new Runnable() { + @Override + public void run() { + ViewHelper.setTranslationY(mRecyclerViewBackground, mFlexibleSpaceImageHeight); + } + }); + ViewHelper.setTranslationY(mOverlayView, mFlexibleSpaceImageHeight); + mTitleView.post(new Runnable() { + @Override + public void run() { + ViewHelper.setTranslationY(mTitleView, (int) (mFlexibleSpaceImageHeight - mTitleView.getHeight() * scale)); + ViewHelper.setPivotX(mTitleView, 0); + ViewHelper.setPivotY(mTitleView, 0); + ViewHelper.setScaleX(mTitleView, scale); + ViewHelper.setScaleY(mTitleView, scale); + } + }); + + + } + + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + // Translate overlay and image + float flexibleRange = mFlexibleSpaceImageHeight - mActionBarSize; + int minOverlayTransitionY = mActionBarSize - mOverlayView.getHeight(); + ViewHelper.setTranslationY(mOverlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0)); + ViewHelper.setTranslationY(mImageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0)); + + // Translate list background + ViewHelper.setTranslationY(mRecyclerViewBackground, Math.max(0, -scrollY + mFlexibleSpaceImageHeight)); + + // Change alpha of overlay + ViewHelper.setAlpha(mOverlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1)); + + // Scale title text + float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA); + setPivotXToTitle(); + ViewHelper.setPivotY(mTitleView, 0); + ViewHelper.setScaleX(mTitleView, scale); + ViewHelper.setScaleY(mTitleView, scale); + + // Translate title text + int maxTitleTranslationY = (int) (mFlexibleSpaceImageHeight - mTitleView.getHeight() * scale); + int titleTranslationY = maxTitleTranslationY - scrollY; + if (TOOLBAR_IS_STICKY) { + titleTranslationY = Math.max(0, titleTranslationY); + } + ViewHelper.setTranslationY(mTitleView, titleTranslationY); + + + if (TOOLBAR_IS_STICKY) { + // Change alpha of toolbar background + if (-scrollY + mFlexibleSpaceImageHeight <= mActionBarSize) { + mToolbar.setBackgroundColor(ScrollUtils.getColorWithAlpha(1, mToolbarColor)); + } else { + mToolbar.setBackgroundColor(ScrollUtils.getColorWithAlpha(0, mToolbarColor)); + } + } else { + // Translate Toolbar + if (scrollY < mFlexibleSpaceImageHeight) { + ViewHelper.setTranslationY(mToolbar, 0); + } else { + ViewHelper.setTranslationY(mToolbar, -scrollY); + } + } + + } + + @Override + public void onDownMotionEvent() { + + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private void setPivotXToTitle() { + Configuration config = getResources().getConfiguration(); + if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT + && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + ViewHelper.setPivotX(mTitleView, findViewById(android.R.id.content).getWidth()); + } else { + ViewHelper.setPivotX(mTitleView, 0); + } + } +} From a000e66d5f6c21ab845e694f80b1a4875d7434fc Mon Sep 17 00:00:00 2001 From: kyanro Date: Sat, 7 Mar 2015 12:07:50 +0900 Subject: [PATCH 035/208] use first child of line in height calculation in ObservableGridView --- .../ObservableGridView.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index 02f206c0..7b759726 100644 --- a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -17,6 +17,7 @@ package com.github.ksoichiro.android.observablescrollview; import android.content.Context; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; @@ -233,13 +234,30 @@ private void init() { super.setOnScrollListener(mScrollListener); } + private int getNumColumnsCompat() { + if (Build.VERSION.SDK_INT >= 11) { + return getNumColumns(); + } else { + int columns = 0; + if (getChildCount() > 0) { + int width = getChildAt(0).getMeasuredWidth(); + if (width > 0) { + columns = getWidth() / width; + } + } + return columns > 0 ? columns : AUTO_FIT; + } + } + private void onScrollChanged() { if (mCallbacks != null) { if (getChildCount() > 0) { int firstVisiblePosition = getFirstVisiblePosition(); for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) { if (mChildrenHeights.indexOfKey(i) < 0 || getChildAt(j).getHeight() != mChildrenHeights.get(i)) { - mChildrenHeights.put(i, getChildAt(j).getHeight()); + if (i % getNumColumnsCompat() == 0) { + mChildrenHeights.put(i, getChildAt(j).getHeight()); + } } } From 328cf11f6d7ae1a1dbf8278e08fb7115c6c4e2fd Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 14 Mar 2015 22:45:36 +0700 Subject: [PATCH 036/208] [WIP] Added a note to explain how to build the sample app and how to use the library. --- observablescrollview-samples/README.md | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 observablescrollview-samples/README.md diff --git a/observablescrollview-samples/README.md b/observablescrollview-samples/README.md new file mode 100644 index 00000000..885c3f91 --- /dev/null +++ b/observablescrollview-samples/README.md @@ -0,0 +1,33 @@ +# Samples + +This sample project demonstrates how the Android-ObservableScrollView works. + +This document's goal is to lead you to run the sample app and help understanding how to use this library. + +Please note that this document is still work in progress. +Although I've built the app on Android Studio, Eclipse, Gradle on Mac and Gradle on Linux of Travis CI, there might be some implicit dependencies which I haven't noticed and you couldn't build it correctly. +Therefore I'd greatly appreciate it if you report it to me. + +## How to build + +### on Android Studio + +TODO + +### on Eclipse + +TODO + +### on Gradle + +Windows: + +```sh +> gradlew installDevDebug +``` + +Linux/Mac: + +```sh +$ ./gradlew installDevDebug +``` \ No newline at end of file From 0875676994f86489c722f9ca8c280baa7be9aef4 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 15 Mar 2015 22:07:05 +0700 Subject: [PATCH 037/208] Modified README. --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 2ab012d8..a0ea0d02 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,22 @@ If you are a wercker user, you can download the latest build artifact. Clone this repository and build the app using Gradle wrapper. +Mac / Linux / Git Bash, Cygwin on Windows: + ```sh $ git clone https://github.com/ksoichiro/Android-ObservableScrollView.git $ cd Android-ObservableScrollView $ ./gradlew installDevDebug ``` +Windows: + +```sh +> git clone https://github.com/ksoichiro/Android-ObservableScrollView.git +> cd Android-ObservableScrollView +> gradlew installDevDebug +``` + ## Usage ### Add to your dependencies From 32c78d528a813689475d97f81588257a195e9481 Mon Sep 17 00:00:00 2001 From: kevintan Date: Mon, 16 Mar 2015 12:45:41 +0800 Subject: [PATCH 038/208] dont intercept when floating action button is clicked, fixes fab touch issue for larger screens --- .../samples/SlidingUpBaseActivity.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java index 19630427..9656fdcf 100644 --- a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java +++ b/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java @@ -24,6 +24,7 @@ import android.view.View; import android.widget.FrameLayout; import android.widget.TextView; +import android.widget.Toast; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; import com.github.ksoichiro.android.observablescrollview.ScrollState; @@ -79,6 +80,12 @@ public abstract class SlidingUpBaseActivity extends BaseAc private boolean mHeaderColorChangedToBottom; private boolean mHeaderIsAtBottom; private boolean mHeaderIsNotAtBottom; + private View.OnClickListener fabClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(SlidingUpBaseActivity.this, "floating action button clicked", Toast.LENGTH_SHORT).show(); + } + }; @Override protected void onCreate(Bundle savedInstanceState) { @@ -119,6 +126,7 @@ public void onClick(View v) { mScrollable = createScrollable(); mFab = findViewById(R.id.fab); + mFab.setOnClickListener(fabClickListener); mFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard); mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.scroll_wrapper); @@ -179,8 +187,17 @@ public void onUpOrCancelMotionEvent(ScrollState scrollState) { @Override public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) { final int minInterceptionLayoutY = -mIntersectionHeight; - return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout) - || (moving && mScrollable.getCurrentScrollY() - diffY < 0); + + //slight fix for untappable floating action button for larger screens + Rect fabRect = new Rect(); + mFab.getHitRect(fabRect); + //if the user's touch is within the floating action button's touch area, dont intercept + if (fabRect.contains((int)ev.getX(), (int)ev.getY())) { + return false; + }else{ + return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout) + || (moving && mScrollable.getCurrentScrollY() - diffY < 0); + } } @Override From f8a767cf86035577f84aca36cb7ed2f0605b4d6c Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 17 Mar 2015 23:18:30 +0900 Subject: [PATCH 039/208] Modified README. --- README.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a0ea0d02..f2b1cefd 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,15 @@ It's easy to interact with the Toolbar introduced in Android 5.0 Lollipop and m ## Samples +You can install the sample app with the following methods. + ### Google Play store [![Get it on Google Play](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.github.ksoichiro.android.observablescrollview.samples2) +Please note that the app on the Play Store is not always the latest version. +If you'd like to install the latest one, install it manually with Gradle. + ### wercker If you are a wercker user, you can download the latest build artifact. @@ -53,7 +58,7 @@ $ cd Android-ObservableScrollView $ ./gradlew installDevDebug ``` -Windows: +Windows (Command prompt): ```sh > git clone https://github.com/ksoichiro/Android-ObservableScrollView.git @@ -61,6 +66,10 @@ Windows: > gradlew installDevDebug ``` +### Install manually with IDE + +See README in the `observablescrollview-samples` directory. + ## Usage ### Add to your dependencies @@ -83,7 +92,13 @@ It's partially supported. ### Add widgets to your layout -Use one of the `ObservableListView`, `ObservableScrollView`, `ObservableWebView`, `ObservableRecyclerView`, `ObservableGridView` in your XML layout file. +Use one of the following widgets in your XML layout file. + +* `ObservableListView` +* `ObservableScrollView` +* `ObservableWebView` +* `ObservableRecyclerView` +* `ObservableGridView` ### Control scroll events with callbacks @@ -209,5 +224,4 @@ Please check the [contributing guideline](https://github.com/ksoichiro/Android-O distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. - + limitations under the License. \ No newline at end of file From 0b387a6b88d221d0e7a4e3f4f57ef4132c351653 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 18 Mar 2015 19:55:53 +0900 Subject: [PATCH 040/208] Modified README. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f2b1cefd..c7b6d304 100644 --- a/README.md +++ b/README.md @@ -224,4 +224,5 @@ Please check the [contributing guideline](https://github.com/ksoichiro/Android-O distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. + From 63bc769085f630974fd4daafc0028fb518129e0f Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 18 Mar 2015 20:32:03 +0900 Subject: [PATCH 041/208] Fixed wrong highlight in eclipse docs. --- docs/eclipse.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/eclipse.md b/docs/eclipse.md index 35b7e5d4..deebcff4 100644 --- a/docs/eclipse.md +++ b/docs/eclipse.md @@ -48,7 +48,7 @@ This will generate dependency codes from AAR files using Gradle wrapper and some 1. Select `General` > `Existing Projects into Workspace` and click `Next`. * Warning: DO NOT `Android` > `Existing Android Code into Workspace`. 1. Click `Browse` and select project root directory (`Android-ObservableScrollView`). -1. Check `Search` for nested projects. +1. Check `Search for nested projects`. 1. Select all projects and click next. 1. Some warning messages will be generated, but ignore them and wait until build finishes. From f946954396aa5a2b000b388cb1bb6031782ab89e Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 18 Mar 2015 21:51:04 +0900 Subject: [PATCH 042/208] IMPORTANT CHANGE: renamed sub-projects to shorten paths of each files for Windows environment. --- .travis.yml | 2 +- README.md | 34 ++++++++++-------- build.gradle | 7 +++- {observablescrollview => library}/.gitignore | 0 .../AndroidManifest.xml | 0 .../build.gradle | 0 .../gradle-mvn-push.gradle | 0 .../gradle.properties | 0 .../res/.gitkeep | 0 .../src/androidTest/AndroidManifest.xml | 0 .../src/androidTest}/assets/lipsum.html | 0 .../observablescrollview/SavedStateTest.java | 0 .../test/GridViewActivity.java | 0 .../test/GridViewActivityTest.java | 0 .../test/ListViewActivity.java | 0 .../test/ListViewActivityTest.java | 0 .../ListViewScrollFromBottomActivity.java | 0 .../ListViewScrollFromBottomActivityTest.java | 0 .../test/RecyclerViewActivity.java | 0 .../test/RecyclerViewActivityTest.java | 0 .../RecyclerViewScrollFromBottomActivity.java | 0 ...yclerViewScrollFromBottomActivityTest.java | 0 .../test/ScrollUtilsTest.java | 0 .../test/ScrollViewActivity.java | 0 .../test/ScrollViewActivityTest.java | 0 .../test/SimpleHeaderRecyclerAdapter.java | 0 .../test/SimpleRecyclerAdapter.java | 0 .../TouchInterceptionGridViewActivity.java | 0 ...TouchInterceptionGridViewActivityTest.java | 0 .../TouchInterceptionListViewActivity.java | 0 ...TouchInterceptionListViewActivityTest.java | 0 ...TouchInterceptionRecyclerViewActivity.java | 0 ...hInterceptionRecyclerViewActivityTest.java | 0 .../TouchInterceptionScrollViewActivity.java | 0 ...uchInterceptionScrollViewActivityTest.java | 0 .../TouchInterceptionWebViewActivity.java | 0 .../TouchInterceptionWebViewActivityTest.java | 0 .../test/UiTestUtils.java | 0 .../test/ViewPagerTab2Activity.java | 0 .../test/ViewPagerTab2ActivityTest.java | 0 .../test/ViewPagerTab2GridViewFragment.java | 0 .../test/ViewPagerTab2ListViewFragment.java | 0 .../ViewPagerTab2RecyclerViewFragment.java | 0 .../test/ViewPagerTab2ScrollViewFragment.java | 0 .../test/ViewPagerTab2WebViewFragment.java | 0 .../test/ViewPagerTabActivity.java | 0 .../test/ViewPagerTabActivityTest.java | 0 .../test/ViewPagerTabListViewFragment.java | 0 .../ViewPagerTabRecyclerViewFragment.java | 0 .../test/ViewPagerTabScrollViewFragment.java | 0 .../test/WebViewActivity.java | 0 .../test/WebViewActivityTest.java | 0 .../iosched/ui/widget/SlidingTabLayout.java | 0 .../iosched/ui/widget/SlidingTabStrip.java | 0 .../androidTest}/res/color/tab_text_color.xml | 0 .../res/layout/activity_gridview.xml | 0 .../res/layout/activity_listview.xml | 0 .../res/layout/activity_recyclerview.xml | 0 .../res/layout/activity_scrollview.xml | 0 .../activity_touchinterception_gridview.xml | 0 .../activity_touchinterception_listview.xml | 0 ...ctivity_touchinterception_recyclerview.xml | 0 .../activity_touchinterception_scrollview.xml | 0 .../activity_touchinterception_webview.xml | 0 .../res/layout/activity_viewpagertab.xml | 0 .../res/layout/activity_viewpagertab2.xml | 0 .../res/layout/activity_webview.xml | 0 .../res/layout/fragment_gridview.xml | 0 .../res/layout/fragment_listview.xml | 0 .../res/layout/fragment_recyclerview.xml | 0 .../res/layout/fragment_scrollview.xml | 0 .../layout/fragment_scrollview_noheader.xml | 0 .../res/layout/fragment_webview.xml | 0 .../src/androidTest}/res/layout/padding.xml | 0 .../androidTest}/res/layout/tab_indicator.xml | 0 .../src/androidTest/res/values/colors.xml | 0 .../src/androidTest/res/values/dimens.xml | 0 .../src/androidTest/res/values/strings.xml | 0 .../src/androidTest}/res/values/styles.xml | 0 .../CacheFragmentStatePagerAdapter.java | 0 .../ObservableGridView.java | 0 .../ObservableListView.java | 0 .../ObservableRecyclerView.java | 0 .../ObservableScrollView.java | 0 .../ObservableScrollViewCallbacks.java | 0 .../ObservableWebView.java | 0 .../observablescrollview/ScrollState.java | 0 .../observablescrollview/ScrollUtils.java | 0 .../observablescrollview/Scrollable.java | 0 .../TouchInterceptionFrameLayout.java | 0 .../.gitignore | 0 .../AndroidManifest.xml | 0 .../README.md | 0 .../assets/handletouch.html | 0 .../assets/lipsum.html | 0 .../build.gradle | 2 +- .../ic_launcher-web.png | Bin .../images}/demo1.gif | Bin .../images}/demo10.gif | Bin .../images}/demo11.gif | Bin .../images}/demo12.gif | Bin .../images}/demo13.gif | Bin .../images}/demo2.gif | Bin .../images}/demo3.gif | Bin .../images}/demo4.gif | Bin .../images}/demo5.gif | Bin .../images}/demo6.gif | Bin .../images}/demo7.gif | Bin .../images}/demo8.gif | Bin .../images}/demo9.gif | Bin .../proguard-rules.pro | 0 .../res/color/tab_text_color.xml | 0 .../res/drawable-xhdpi/ic_launcher.png | Bin .../res/drawable-xxhdpi/ic_launcher.png | Bin .../res/drawable-xxxhdpi/ic_launcher.png | Bin .../res/drawable/example.jpeg | Bin .../drawable/gradient_header_background.xml | 0 .../res/drawable/sliding_header_overlay.xml | 0 .../res/layout-v11/tab_indicator.xml | 0 .../res/layout/activity_about.xml | 0 .../activity_actionbarcontrolgridview.xml | 0 .../activity_actionbarcontrollistview.xml | 0 .../activity_actionbarcontrolrecyclerview.xml | 0 .../activity_actionbarcontrolscrollview.xml | 0 .../activity_actionbarcontrolwebview.xml | 0 .../res/layout/activity_fillgap3listview.xml | 0 .../layout/activity_fillgap3recyclerview.xml | 0 .../layout/activity_fillgap3scrollview.xml | 0 .../res/layout/activity_fillgaplistview.xml | 0 .../layout/activity_fillgaprecyclerview.xml | 0 .../res/layout/activity_fillgapscrollview.xml | 0 ...ctivity_flexiblespacetoolbarscrollview.xml | 0 ...ctivity_flexiblespacewithimagelistview.xml | 0 ...ity_flexiblespacewithimagerecyclerview.xml | 0 ...ivity_flexiblespacewithimagescrollview.xml | 0 ...lexiblespacewithimagewithviewpagertab2.xml | 0 .../activity_fragmentactionbarcontrol.xml | 0 .../layout/activity_fragmenttransition.xml | 0 .../layout/activity_handletouchgridview.xml | 0 .../layout/activity_handletouchlistview.xml | 0 .../activity_handletouchrecyclerview.xml | 0 .../layout/activity_handletouchscrollview.xml | 0 .../layout/activity_handletouchwebview.xml | 0 .../res/layout/activity_main.xml | 0 .../activity_parallaxtoolbarscrollview.xml | 0 .../res/layout/activity_slidingupgridview.xml | 0 .../res/layout/activity_slidinguplistview.xml | 0 .../layout/activity_slidinguprecyclerview.xml | 0 .../layout/activity_slidingupscrollview.xml | 0 .../res/layout/activity_slidingupwebview.xml | 0 .../layout/activity_stickyheaderlistview.xml | 0 .../activity_stickyheaderrecyclerview.xml | 0 .../activity_stickyheaderscrollview.xml | 0 .../layout/activity_stickyheaderwebview.xml | 0 .../activity_toolbarcontrolgridview.xml | 0 .../activity_toolbarcontrollistview.xml | 0 .../activity_toolbarcontrolrecyclerview.xml | 0 .../activity_toolbarcontrolscrollview.xml | 0 .../layout/activity_toolbarcontrolwebview.xml | 0 .../res/layout/activity_viewpagertab.xml | 0 .../res/layout/activity_viewpagertab2.xml | 0 .../layout/activity_viewpagertabfragment.xml | 0 .../res/layout/divider.xml | 0 .../fragment_actionbarcontrollistview.xml | 0 .../fragment_fragmenttransition_default.xml | 0 .../fragment_fragmenttransition_second.xml | 0 .../res/layout/fragment_gridview.xml | 0 .../res/layout/fragment_listview.xml | 0 .../res/layout/fragment_recyclerview.xml | 0 .../res/layout/fragment_scrollview.xml | 0 .../layout/fragment_scrollview_noheader.xml | 0 .../fragment_viewpagertabfragment_parent.xml | 0 .../res/layout/fragment_webview.xml | 0 .../res/layout/gradient_header.xml | 0 .../res/layout/list_item_handletouch.xml | 0 .../res/layout/list_item_main.xml | 0 .../res/layout/padding.xml | 0 .../res/layout/recycler_header.xml | 0 .../res/layout/tab_indicator.xml | 0 .../res/menu/menu_main.xml | 0 .../res/values-ar/strings.xml | 0 .../res/values-w820dp/dimens.xml | 0 .../res/values/colors.xml | 0 .../res/values/dimens.xml | 0 .../res/values/strings.xml | 0 .../res/values/strings_license.xml | 0 .../res/values/styles.xml | 0 .../github/ksoichiro/app/ApplicationTest.java | 0 .../debug/res/drawable-xhdpi/ic_launcher.png | Bin .../debug/res/drawable-xxhdpi/ic_launcher.png | Bin .../res/drawable-xxxhdpi/ic_launcher.png | Bin .../samples/AboutActivity.java | 0 .../ActionBarControlGridViewActivity.java | 0 .../ActionBarControlListViewActivity.java | 0 .../ActionBarControlRecyclerViewActivity.java | 0 .../ActionBarControlScrollViewActivity.java | 0 .../ActionBarControlWebViewActivity.java | 0 .../samples/BaseActivity.java | 0 .../samples/BaseFragment.java | 0 .../samples/FillGap2BaseActivity.java | 0 .../samples/FillGap2ListViewActivity.java | 0 .../samples/FillGap2RecyclerViewActivity.java | 0 .../samples/FillGap2ScrollViewActivity.java | 0 .../samples/FillGap3BaseActivity.java | 0 .../samples/FillGap3ListViewActivity.java | 0 .../samples/FillGap3RecyclerViewActivity.java | 0 .../samples/FillGap3ScrollViewActivity.java | 0 .../samples/FillGapBaseActivity.java | 0 .../samples/FillGapListViewActivity.java | 0 .../samples/FillGapRecyclerViewActivity.java | 0 .../samples/FillGapScrollViewActivity.java | 0 ...lexibleSpaceToolbarScrollViewActivity.java | 0 ...lexibleSpaceWithImageListViewActivity.java | 0 ...bleSpaceWithImageRecyclerViewActivity.java | 0 ...xibleSpaceWithImageScrollViewActivity.java | 0 ...aceWithImageWithViewPagerTab2Activity.java | 0 ...gmentActionBarControlListViewActivity.java | 0 ...gmentActionBarControlListViewFragment.java | 0 .../samples/FragmentTransitionActivity.java | 0 .../FragmentTransitionDefaultFragment.java | 0 .../FragmentTransitionSecondFragment.java | 0 .../samples/HandleTouchGridViewActivity.java | 0 .../samples/HandleTouchListViewActivity.java | 0 .../HandleTouchRecyclerViewActivity.java | 0 .../HandleTouchScrollViewActivity.java | 0 .../samples/HandleTouchWebViewActivity.java | 0 .../samples/MainActivity.java | 0 .../ParallaxToolbarScrollViewActivity.java | 0 .../ScrollFromBottomListViewActivity.java | 0 .../ScrollFromBottomRecyclerViewActivity.java | 0 .../samples/SimpleHeaderRecyclerAdapter.java | 0 .../samples/SimpleRecyclerAdapter.java | 0 .../samples/SlidingUpBaseActivity.java | 0 .../samples/SlidingUpGridViewActivity.java | 0 .../samples/SlidingUpListViewActivity.java | 0 .../SlidingUpRecyclerViewActivity.java | 0 .../samples/SlidingUpScrollViewActivity.java | 0 .../samples/SlidingUpWebViewActivity.java | 0 .../samples/StickyHeaderListViewActivity.java | 0 .../StickyHeaderRecyclerViewActivity.java | 0 .../StickyHeaderScrollViewActivity.java | 0 .../samples/StickyHeaderWebViewActivity.java | 0 .../samples/ToolbarControlBaseActivity.java | 0 .../ToolbarControlGridViewActivity.java | 0 .../ToolbarControlListViewActivity.java | 0 .../ToolbarControlRecyclerViewActivity.java | 0 .../ToolbarControlScrollViewActivity.java | 0 .../ToolbarControlWebViewActivity.java | 0 .../samples/ViewPagerTab2Activity.java | 0 .../ViewPagerTab2GridViewFragment.java | 0 .../ViewPagerTab2ListViewFragment.java | 0 .../ViewPagerTab2RecyclerViewFragment.java | 0 .../ViewPagerTab2ScrollViewFragment.java | 0 .../samples/ViewPagerTab2WebViewFragment.java | 0 .../samples/ViewPagerTabActivity.java | 0 .../samples/ViewPagerTabFragmentActivity.java | 0 .../ViewPagerTabFragmentGridViewFragment.java | 0 .../ViewPagerTabFragmentListViewFragment.java | 0 .../ViewPagerTabFragmentParentFragment.java | 0 ...wPagerTabFragmentRecyclerViewFragment.java | 0 ...iewPagerTabFragmentScrollViewFragment.java | 0 .../ViewPagerTabFragmentWebViewFragment.java | 0 .../samples/ViewPagerTabListViewActivity.java | 0 .../samples/ViewPagerTabListViewFragment.java | 0 .../ViewPagerTabRecyclerViewFragment.java | 0 .../ViewPagerTabScrollViewActivity.java | 0 .../ViewPagerTabScrollViewFragment.java | 0 .../iosched/ui/widget/SlidingTabLayout.java | 0 .../iosched/ui/widget/SlidingTabStrip.java | 0 .../version.gradle | 0 settings.gradle | 2 +- wercker.yml | 4 +-- 272 files changed, 30 insertions(+), 21 deletions(-) rename {observablescrollview => library}/.gitignore (100%) rename {observablescrollview => library}/AndroidManifest.xml (100%) rename {observablescrollview => library}/build.gradle (100%) rename {observablescrollview => library}/gradle-mvn-push.gradle (100%) rename {observablescrollview => library}/gradle.properties (100%) rename {observablescrollview => library}/res/.gitkeep (100%) rename {observablescrollview => library}/src/androidTest/AndroidManifest.xml (100%) rename {observablescrollview-samples => library/src/androidTest}/assets/lipsum.html (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/SavedStateTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollUtilsTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleHeaderRecyclerAdapter.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleRecyclerAdapter.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2Activity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2GridViewFragment.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ListViewFragment.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2RecyclerViewFragment.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ScrollViewFragment.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2WebViewFragment.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabListViewFragment.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabRecyclerViewFragment.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabScrollViewFragment.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivity.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java (100%) rename {observablescrollview => library}/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java (100%) rename {observablescrollview-samples/src/main => library/src/androidTest}/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java (100%) rename {observablescrollview-samples => library/src/androidTest}/res/color/tab_text_color.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/activity_gridview.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/activity_listview.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/activity_recyclerview.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/activity_scrollview.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/activity_touchinterception_gridview.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/activity_touchinterception_listview.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/activity_touchinterception_recyclerview.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/activity_touchinterception_scrollview.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/activity_touchinterception_webview.xml (100%) rename {observablescrollview-samples => library/src/androidTest}/res/layout/activity_viewpagertab.xml (100%) rename {observablescrollview-samples => library/src/androidTest}/res/layout/activity_viewpagertab2.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/activity_webview.xml (100%) rename {observablescrollview-samples => library/src/androidTest}/res/layout/fragment_gridview.xml (100%) rename {observablescrollview-samples => library/src/androidTest}/res/layout/fragment_listview.xml (100%) rename {observablescrollview-samples => library/src/androidTest}/res/layout/fragment_recyclerview.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/fragment_scrollview.xml (100%) rename {observablescrollview => library}/src/androidTest/res/layout/fragment_scrollview_noheader.xml (100%) rename {observablescrollview-samples => library/src/androidTest}/res/layout/fragment_webview.xml (100%) rename {observablescrollview-samples => library/src/androidTest}/res/layout/padding.xml (100%) rename {observablescrollview-samples => library/src/androidTest}/res/layout/tab_indicator.xml (100%) rename {observablescrollview => library}/src/androidTest/res/values/colors.xml (100%) rename {observablescrollview => library}/src/androidTest/res/values/dimens.xml (100%) rename {observablescrollview => library}/src/androidTest/res/values/strings.xml (100%) rename {observablescrollview-samples => library/src/androidTest}/res/values/styles.xml (100%) rename {observablescrollview => library}/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java (100%) rename {observablescrollview => library}/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java (100%) rename {observablescrollview => library}/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java (100%) rename {observablescrollview => library}/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java (100%) rename {observablescrollview => library}/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java (100%) rename {observablescrollview => library}/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java (100%) rename {observablescrollview => library}/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java (100%) rename {observablescrollview => library}/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollState.java (100%) rename {observablescrollview => library}/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java (100%) rename {observablescrollview => library}/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java (100%) rename {observablescrollview => library}/src/main/java/com/github/ksoichiro/android/observablescrollview/TouchInterceptionFrameLayout.java (100%) rename {observablescrollview-samples => samples}/.gitignore (100%) rename {observablescrollview-samples => samples}/AndroidManifest.xml (100%) rename {observablescrollview-samples => samples}/README.md (100%) rename {observablescrollview-samples => samples}/assets/handletouch.html (100%) rename {observablescrollview/src/androidTest => samples}/assets/lipsum.html (100%) rename {observablescrollview-samples => samples}/build.gradle (98%) rename {observablescrollview-samples => samples}/ic_launcher-web.png (100%) rename {observablescrollview-samples => samples/images}/demo1.gif (100%) rename {observablescrollview-samples => samples/images}/demo10.gif (100%) rename {observablescrollview-samples => samples/images}/demo11.gif (100%) rename {observablescrollview-samples => samples/images}/demo12.gif (100%) rename {observablescrollview-samples => samples/images}/demo13.gif (100%) rename {observablescrollview-samples => samples/images}/demo2.gif (100%) rename {observablescrollview-samples => samples/images}/demo3.gif (100%) rename {observablescrollview-samples => samples/images}/demo4.gif (100%) rename {observablescrollview-samples => samples/images}/demo5.gif (100%) rename {observablescrollview-samples => samples/images}/demo6.gif (100%) rename {observablescrollview-samples => samples/images}/demo7.gif (100%) rename {observablescrollview-samples => samples/images}/demo8.gif (100%) rename {observablescrollview-samples => samples/images}/demo9.gif (100%) rename {observablescrollview-samples => samples}/proguard-rules.pro (100%) rename {observablescrollview/src/androidTest => samples}/res/color/tab_text_color.xml (100%) rename {observablescrollview-samples => samples}/res/drawable-xhdpi/ic_launcher.png (100%) rename {observablescrollview-samples => samples}/res/drawable-xxhdpi/ic_launcher.png (100%) rename {observablescrollview-samples => samples}/res/drawable-xxxhdpi/ic_launcher.png (100%) rename {observablescrollview-samples => samples}/res/drawable/example.jpeg (100%) rename {observablescrollview-samples => samples}/res/drawable/gradient_header_background.xml (100%) rename {observablescrollview-samples => samples}/res/drawable/sliding_header_overlay.xml (100%) rename {observablescrollview-samples => samples}/res/layout-v11/tab_indicator.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_about.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_actionbarcontrolgridview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_actionbarcontrollistview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_actionbarcontrolrecyclerview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_actionbarcontrolscrollview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_actionbarcontrolwebview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_fillgap3listview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_fillgap3recyclerview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_fillgap3scrollview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_fillgaplistview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_fillgaprecyclerview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_fillgapscrollview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_flexiblespacetoolbarscrollview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_flexiblespacewithimagelistview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_flexiblespacewithimagerecyclerview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_flexiblespacewithimagescrollview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_flexiblespacewithimagewithviewpagertab2.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_fragmentactionbarcontrol.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_fragmenttransition.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_handletouchgridview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_handletouchlistview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_handletouchrecyclerview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_handletouchscrollview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_handletouchwebview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_main.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_parallaxtoolbarscrollview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_slidingupgridview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_slidinguplistview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_slidinguprecyclerview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_slidingupscrollview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_slidingupwebview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_stickyheaderlistview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_stickyheaderrecyclerview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_stickyheaderscrollview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_stickyheaderwebview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_toolbarcontrolgridview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_toolbarcontrollistview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_toolbarcontrolrecyclerview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_toolbarcontrolscrollview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_toolbarcontrolwebview.xml (100%) rename {observablescrollview/src/androidTest => samples}/res/layout/activity_viewpagertab.xml (100%) rename {observablescrollview/src/androidTest => samples}/res/layout/activity_viewpagertab2.xml (100%) rename {observablescrollview-samples => samples}/res/layout/activity_viewpagertabfragment.xml (100%) rename {observablescrollview-samples => samples}/res/layout/divider.xml (100%) rename {observablescrollview-samples => samples}/res/layout/fragment_actionbarcontrollistview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/fragment_fragmenttransition_default.xml (100%) rename {observablescrollview-samples => samples}/res/layout/fragment_fragmenttransition_second.xml (100%) rename {observablescrollview/src/androidTest => samples}/res/layout/fragment_gridview.xml (100%) rename {observablescrollview/src/androidTest => samples}/res/layout/fragment_listview.xml (100%) rename {observablescrollview/src/androidTest => samples}/res/layout/fragment_recyclerview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/fragment_scrollview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/fragment_scrollview_noheader.xml (100%) rename {observablescrollview-samples => samples}/res/layout/fragment_viewpagertabfragment_parent.xml (100%) rename {observablescrollview/src/androidTest => samples}/res/layout/fragment_webview.xml (100%) rename {observablescrollview-samples => samples}/res/layout/gradient_header.xml (100%) rename {observablescrollview-samples => samples}/res/layout/list_item_handletouch.xml (100%) rename {observablescrollview-samples => samples}/res/layout/list_item_main.xml (100%) rename {observablescrollview/src/androidTest => samples}/res/layout/padding.xml (100%) rename {observablescrollview-samples => samples}/res/layout/recycler_header.xml (100%) rename {observablescrollview/src/androidTest => samples}/res/layout/tab_indicator.xml (100%) rename {observablescrollview-samples => samples}/res/menu/menu_main.xml (100%) rename {observablescrollview-samples => samples}/res/values-ar/strings.xml (100%) rename {observablescrollview-samples => samples}/res/values-w820dp/dimens.xml (100%) rename {observablescrollview-samples => samples}/res/values/colors.xml (100%) rename {observablescrollview-samples => samples}/res/values/dimens.xml (100%) rename {observablescrollview-samples => samples}/res/values/strings.xml (100%) rename {observablescrollview-samples => samples}/res/values/strings_license.xml (100%) rename {observablescrollview/src/androidTest => samples}/res/values/styles.xml (100%) rename {observablescrollview-samples => samples}/src/androidTest/java/com/github/ksoichiro/app/ApplicationTest.java (100%) rename {observablescrollview-samples => samples}/src/debug/res/drawable-xhdpi/ic_launcher.png (100%) rename {observablescrollview-samples => samples}/src/debug/res/drawable-xxhdpi/ic_launcher.png (100%) rename {observablescrollview-samples => samples}/src/debug/res/drawable-xxxhdpi/ic_launcher.png (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlGridViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlRecyclerViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlWebViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2BaseActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2RecyclerViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3BaseActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3RecyclerViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapRecyclerViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionDefaultFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchGridViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchRecyclerViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchWebViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomRecyclerViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleHeaderRecyclerAdapter.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleRecyclerAdapter.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpGridViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpRecyclerViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpWebViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderRecyclerViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderWebViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlBaseActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlGridViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlRecyclerViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlWebViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2Activity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2GridViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ListViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2RecyclerViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ScrollViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2WebViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentGridViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentListViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentParentFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentRecyclerViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentScrollViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentWebViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabRecyclerViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewFragment.java (100%) rename {observablescrollview-samples => samples}/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java (100%) rename {observablescrollview/src/androidTest => samples/src/main}/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java (100%) rename {observablescrollview-samples => samples}/version.gradle (100%) diff --git a/.travis.yml b/.travis.yml index ac0a80ec..369fc300 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,4 +20,4 @@ script: # This causes build errors while developing a new feature, so disable release build. - travis_retry ./gradlew --full-stacktrace -q assembleDevDebug connectedCheck after_success: - - ./gradlew :observablescrollview:coveralls + - ./gradlew :library:coveralls diff --git a/README.md b/README.md index c7b6d304..d3761ec5 100644 --- a/README.md +++ b/README.md @@ -10,22 +10,22 @@ Android-ObservableScrollView Android library to observe scroll events on scrollable views. It's easy to interact with the Toolbar introduced in Android 5.0 Lollipop and may be helpful to implement look and feel of Material Design apps. -![](observablescrollview-samples/demo12.gif) -![](observablescrollview-samples/demo10.gif) -![](observablescrollview-samples/demo11.gif) -![](observablescrollview-samples/demo13.gif) +![](samples/images/demo12.gif) +![](samples/images/demo10.gif) +![](samples/images/demo11.gif) +![](samples/images/demo13.gif) -![](observablescrollview-samples/demo1.gif) -![](observablescrollview-samples/demo2.gif) -![](observablescrollview-samples/demo3.gif) -![](observablescrollview-samples/demo4.gif) +![](samples/images/demo1.gif) +![](samples/images/demo2.gif) +![](samples/images/demo3.gif) +![](samples/images/demo4.gif) -![](observablescrollview-samples/demo5.gif) -![](observablescrollview-samples/demo6.gif) -![](observablescrollview-samples/demo7.gif) -![](observablescrollview-samples/demo8.gif) +![](samples/images/demo5.gif) +![](samples/images/demo6.gif) +![](samples/images/demo7.gif) +![](samples/images/demo8.gif) -![](observablescrollview-samples/demo9.gif) +![](samples/images/demo9.gif) ## Samples @@ -68,7 +68,7 @@ Windows (Command prompt): ### Install manually with IDE -See README in the `observablescrollview-samples` directory. +See README in the `samples` directory. ## Usage @@ -129,7 +129,7 @@ Example: } ``` -See [sample app's Activity codes](https://github.com/ksoichiro/Android-ObservableScrollView/tree/master/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples) for more details. +See [sample app's Activity codes](https://github.com/ksoichiro/Android-ObservableScrollView/tree/master/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples) for more details. ## Supported widgets @@ -194,6 +194,10 @@ If you're using this library in your app and you'd like to list it here, please let me know via [email](soichiro.kashima@gmail.com) or pull requests or issues. +## Frequently asked questions + + + ## Contributions Any contributions are welcome! diff --git a/build.gradle b/build.gradle index d206504e..6e616911 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.github.ksoichiro:gradle-eclipse-aar-plugin:0.1.0' + classpath 'com.github.ksoichiro:gradle-eclipse-aar-plugin:0.1.1' } } @@ -16,3 +16,8 @@ allprojects { } apply plugin: 'com.github.ksoichiro.eclipse.aar' + +eclipseAar { + projectNamePrefix = 'observablescrollview-' + cleanLibsDirectoryEnabled = true +} diff --git a/observablescrollview/.gitignore b/library/.gitignore similarity index 100% rename from observablescrollview/.gitignore rename to library/.gitignore diff --git a/observablescrollview/AndroidManifest.xml b/library/AndroidManifest.xml similarity index 100% rename from observablescrollview/AndroidManifest.xml rename to library/AndroidManifest.xml diff --git a/observablescrollview/build.gradle b/library/build.gradle similarity index 100% rename from observablescrollview/build.gradle rename to library/build.gradle diff --git a/observablescrollview/gradle-mvn-push.gradle b/library/gradle-mvn-push.gradle similarity index 100% rename from observablescrollview/gradle-mvn-push.gradle rename to library/gradle-mvn-push.gradle diff --git a/observablescrollview/gradle.properties b/library/gradle.properties similarity index 100% rename from observablescrollview/gradle.properties rename to library/gradle.properties diff --git a/observablescrollview/res/.gitkeep b/library/res/.gitkeep similarity index 100% rename from observablescrollview/res/.gitkeep rename to library/res/.gitkeep diff --git a/observablescrollview/src/androidTest/AndroidManifest.xml b/library/src/androidTest/AndroidManifest.xml similarity index 100% rename from observablescrollview/src/androidTest/AndroidManifest.xml rename to library/src/androidTest/AndroidManifest.xml diff --git a/observablescrollview-samples/assets/lipsum.html b/library/src/androidTest/assets/lipsum.html similarity index 100% rename from observablescrollview-samples/assets/lipsum.html rename to library/src/androidTest/assets/lipsum.html diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/SavedStateTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/SavedStateTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/SavedStateTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/SavedStateTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewScrollFromBottomActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewScrollFromBottomActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollUtilsTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollUtilsTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollUtilsTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollUtilsTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleHeaderRecyclerAdapter.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleHeaderRecyclerAdapter.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleHeaderRecyclerAdapter.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleHeaderRecyclerAdapter.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleRecyclerAdapter.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleRecyclerAdapter.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleRecyclerAdapter.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/SimpleRecyclerAdapter.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionGridViewActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionListViewActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionRecyclerViewActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionWebViewActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/UiTestUtils.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2Activity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2Activity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2Activity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2Activity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2GridViewFragment.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2GridViewFragment.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2GridViewFragment.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2GridViewFragment.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ListViewFragment.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ListViewFragment.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ListViewFragment.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ListViewFragment.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2RecyclerViewFragment.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2RecyclerViewFragment.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2RecyclerViewFragment.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2RecyclerViewFragment.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ScrollViewFragment.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ScrollViewFragment.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ScrollViewFragment.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2ScrollViewFragment.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2WebViewFragment.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2WebViewFragment.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2WebViewFragment.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTab2WebViewFragment.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabListViewFragment.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabListViewFragment.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabListViewFragment.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabListViewFragment.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabRecyclerViewFragment.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabRecyclerViewFragment.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabRecyclerViewFragment.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabRecyclerViewFragment.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabScrollViewFragment.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabScrollViewFragment.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabScrollViewFragment.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ViewPagerTabScrollViewFragment.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivity.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivity.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivity.java diff --git a/observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java rename to library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java diff --git a/observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java b/library/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java rename to library/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java diff --git a/observablescrollview-samples/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java b/library/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java rename to library/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java diff --git a/observablescrollview-samples/res/color/tab_text_color.xml b/library/src/androidTest/res/color/tab_text_color.xml similarity index 100% rename from observablescrollview-samples/res/color/tab_text_color.xml rename to library/src/androidTest/res/color/tab_text_color.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_gridview.xml b/library/src/androidTest/res/layout/activity_gridview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_gridview.xml rename to library/src/androidTest/res/layout/activity_gridview.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_listview.xml b/library/src/androidTest/res/layout/activity_listview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_listview.xml rename to library/src/androidTest/res/layout/activity_listview.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_recyclerview.xml b/library/src/androidTest/res/layout/activity_recyclerview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_recyclerview.xml rename to library/src/androidTest/res/layout/activity_recyclerview.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_scrollview.xml b/library/src/androidTest/res/layout/activity_scrollview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_scrollview.xml rename to library/src/androidTest/res/layout/activity_scrollview.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_touchinterception_gridview.xml b/library/src/androidTest/res/layout/activity_touchinterception_gridview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_touchinterception_gridview.xml rename to library/src/androidTest/res/layout/activity_touchinterception_gridview.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_touchinterception_listview.xml b/library/src/androidTest/res/layout/activity_touchinterception_listview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_touchinterception_listview.xml rename to library/src/androidTest/res/layout/activity_touchinterception_listview.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_touchinterception_recyclerview.xml b/library/src/androidTest/res/layout/activity_touchinterception_recyclerview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_touchinterception_recyclerview.xml rename to library/src/androidTest/res/layout/activity_touchinterception_recyclerview.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_touchinterception_scrollview.xml b/library/src/androidTest/res/layout/activity_touchinterception_scrollview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_touchinterception_scrollview.xml rename to library/src/androidTest/res/layout/activity_touchinterception_scrollview.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_touchinterception_webview.xml b/library/src/androidTest/res/layout/activity_touchinterception_webview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_touchinterception_webview.xml rename to library/src/androidTest/res/layout/activity_touchinterception_webview.xml diff --git a/observablescrollview-samples/res/layout/activity_viewpagertab.xml b/library/src/androidTest/res/layout/activity_viewpagertab.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_viewpagertab.xml rename to library/src/androidTest/res/layout/activity_viewpagertab.xml diff --git a/observablescrollview-samples/res/layout/activity_viewpagertab2.xml b/library/src/androidTest/res/layout/activity_viewpagertab2.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_viewpagertab2.xml rename to library/src/androidTest/res/layout/activity_viewpagertab2.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_webview.xml b/library/src/androidTest/res/layout/activity_webview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_webview.xml rename to library/src/androidTest/res/layout/activity_webview.xml diff --git a/observablescrollview-samples/res/layout/fragment_gridview.xml b/library/src/androidTest/res/layout/fragment_gridview.xml similarity index 100% rename from observablescrollview-samples/res/layout/fragment_gridview.xml rename to library/src/androidTest/res/layout/fragment_gridview.xml diff --git a/observablescrollview-samples/res/layout/fragment_listview.xml b/library/src/androidTest/res/layout/fragment_listview.xml similarity index 100% rename from observablescrollview-samples/res/layout/fragment_listview.xml rename to library/src/androidTest/res/layout/fragment_listview.xml diff --git a/observablescrollview-samples/res/layout/fragment_recyclerview.xml b/library/src/androidTest/res/layout/fragment_recyclerview.xml similarity index 100% rename from observablescrollview-samples/res/layout/fragment_recyclerview.xml rename to library/src/androidTest/res/layout/fragment_recyclerview.xml diff --git a/observablescrollview/src/androidTest/res/layout/fragment_scrollview.xml b/library/src/androidTest/res/layout/fragment_scrollview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/fragment_scrollview.xml rename to library/src/androidTest/res/layout/fragment_scrollview.xml diff --git a/observablescrollview/src/androidTest/res/layout/fragment_scrollview_noheader.xml b/library/src/androidTest/res/layout/fragment_scrollview_noheader.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/fragment_scrollview_noheader.xml rename to library/src/androidTest/res/layout/fragment_scrollview_noheader.xml diff --git a/observablescrollview-samples/res/layout/fragment_webview.xml b/library/src/androidTest/res/layout/fragment_webview.xml similarity index 100% rename from observablescrollview-samples/res/layout/fragment_webview.xml rename to library/src/androidTest/res/layout/fragment_webview.xml diff --git a/observablescrollview-samples/res/layout/padding.xml b/library/src/androidTest/res/layout/padding.xml similarity index 100% rename from observablescrollview-samples/res/layout/padding.xml rename to library/src/androidTest/res/layout/padding.xml diff --git a/observablescrollview-samples/res/layout/tab_indicator.xml b/library/src/androidTest/res/layout/tab_indicator.xml similarity index 100% rename from observablescrollview-samples/res/layout/tab_indicator.xml rename to library/src/androidTest/res/layout/tab_indicator.xml diff --git a/observablescrollview/src/androidTest/res/values/colors.xml b/library/src/androidTest/res/values/colors.xml similarity index 100% rename from observablescrollview/src/androidTest/res/values/colors.xml rename to library/src/androidTest/res/values/colors.xml diff --git a/observablescrollview/src/androidTest/res/values/dimens.xml b/library/src/androidTest/res/values/dimens.xml similarity index 100% rename from observablescrollview/src/androidTest/res/values/dimens.xml rename to library/src/androidTest/res/values/dimens.xml diff --git a/observablescrollview/src/androidTest/res/values/strings.xml b/library/src/androidTest/res/values/strings.xml similarity index 100% rename from observablescrollview/src/androidTest/res/values/strings.xml rename to library/src/androidTest/res/values/strings.xml diff --git a/observablescrollview-samples/res/values/styles.xml b/library/src/androidTest/res/values/styles.xml similarity index 100% rename from observablescrollview-samples/res/values/styles.xml rename to library/src/androidTest/res/values/styles.xml diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java similarity index 100% rename from observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java rename to library/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java similarity index 100% rename from observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java rename to library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java similarity index 100% rename from observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java rename to library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java similarity index 100% rename from observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java rename to library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java similarity index 100% rename from observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java rename to library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java similarity index 100% rename from observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java rename to library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java similarity index 100% rename from observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java rename to library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollState.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollState.java similarity index 100% rename from observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollState.java rename to library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollState.java diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java similarity index 100% rename from observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java rename to library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java similarity index 100% rename from observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java rename to library/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java diff --git a/observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/TouchInterceptionFrameLayout.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/TouchInterceptionFrameLayout.java similarity index 100% rename from observablescrollview/src/main/java/com/github/ksoichiro/android/observablescrollview/TouchInterceptionFrameLayout.java rename to library/src/main/java/com/github/ksoichiro/android/observablescrollview/TouchInterceptionFrameLayout.java diff --git a/observablescrollview-samples/.gitignore b/samples/.gitignore similarity index 100% rename from observablescrollview-samples/.gitignore rename to samples/.gitignore diff --git a/observablescrollview-samples/AndroidManifest.xml b/samples/AndroidManifest.xml similarity index 100% rename from observablescrollview-samples/AndroidManifest.xml rename to samples/AndroidManifest.xml diff --git a/observablescrollview-samples/README.md b/samples/README.md similarity index 100% rename from observablescrollview-samples/README.md rename to samples/README.md diff --git a/observablescrollview-samples/assets/handletouch.html b/samples/assets/handletouch.html similarity index 100% rename from observablescrollview-samples/assets/handletouch.html rename to samples/assets/handletouch.html diff --git a/observablescrollview/src/androidTest/assets/lipsum.html b/samples/assets/lipsum.html similarity index 100% rename from observablescrollview/src/androidTest/assets/lipsum.html rename to samples/assets/lipsum.html diff --git a/observablescrollview-samples/build.gradle b/samples/build.gradle similarity index 98% rename from observablescrollview-samples/build.gradle rename to samples/build.gradle index 8f537aed..c8f3f029 100644 --- a/observablescrollview-samples/build.gradle +++ b/samples/build.gradle @@ -22,7 +22,7 @@ dependencies { compile 'com.android.support:appcompat-v7:21.0.2' compile 'com.nineoldandroids:library:2.4.0' compile 'com.melnykov:floatingactionbutton:1.0.7' - debugCompile project(':observablescrollview') + debugCompile project(':library') // Release build uses the synced latest version releaseCompile "com.github.ksoichiro:android-observablescrollview:${SYNCED_VERSION_NAME}" diff --git a/observablescrollview-samples/ic_launcher-web.png b/samples/ic_launcher-web.png similarity index 100% rename from observablescrollview-samples/ic_launcher-web.png rename to samples/ic_launcher-web.png diff --git a/observablescrollview-samples/demo1.gif b/samples/images/demo1.gif similarity index 100% rename from observablescrollview-samples/demo1.gif rename to samples/images/demo1.gif diff --git a/observablescrollview-samples/demo10.gif b/samples/images/demo10.gif similarity index 100% rename from observablescrollview-samples/demo10.gif rename to samples/images/demo10.gif diff --git a/observablescrollview-samples/demo11.gif b/samples/images/demo11.gif similarity index 100% rename from observablescrollview-samples/demo11.gif rename to samples/images/demo11.gif diff --git a/observablescrollview-samples/demo12.gif b/samples/images/demo12.gif similarity index 100% rename from observablescrollview-samples/demo12.gif rename to samples/images/demo12.gif diff --git a/observablescrollview-samples/demo13.gif b/samples/images/demo13.gif similarity index 100% rename from observablescrollview-samples/demo13.gif rename to samples/images/demo13.gif diff --git a/observablescrollview-samples/demo2.gif b/samples/images/demo2.gif similarity index 100% rename from observablescrollview-samples/demo2.gif rename to samples/images/demo2.gif diff --git a/observablescrollview-samples/demo3.gif b/samples/images/demo3.gif similarity index 100% rename from observablescrollview-samples/demo3.gif rename to samples/images/demo3.gif diff --git a/observablescrollview-samples/demo4.gif b/samples/images/demo4.gif similarity index 100% rename from observablescrollview-samples/demo4.gif rename to samples/images/demo4.gif diff --git a/observablescrollview-samples/demo5.gif b/samples/images/demo5.gif similarity index 100% rename from observablescrollview-samples/demo5.gif rename to samples/images/demo5.gif diff --git a/observablescrollview-samples/demo6.gif b/samples/images/demo6.gif similarity index 100% rename from observablescrollview-samples/demo6.gif rename to samples/images/demo6.gif diff --git a/observablescrollview-samples/demo7.gif b/samples/images/demo7.gif similarity index 100% rename from observablescrollview-samples/demo7.gif rename to samples/images/demo7.gif diff --git a/observablescrollview-samples/demo8.gif b/samples/images/demo8.gif similarity index 100% rename from observablescrollview-samples/demo8.gif rename to samples/images/demo8.gif diff --git a/observablescrollview-samples/demo9.gif b/samples/images/demo9.gif similarity index 100% rename from observablescrollview-samples/demo9.gif rename to samples/images/demo9.gif diff --git a/observablescrollview-samples/proguard-rules.pro b/samples/proguard-rules.pro similarity index 100% rename from observablescrollview-samples/proguard-rules.pro rename to samples/proguard-rules.pro diff --git a/observablescrollview/src/androidTest/res/color/tab_text_color.xml b/samples/res/color/tab_text_color.xml similarity index 100% rename from observablescrollview/src/androidTest/res/color/tab_text_color.xml rename to samples/res/color/tab_text_color.xml diff --git a/observablescrollview-samples/res/drawable-xhdpi/ic_launcher.png b/samples/res/drawable-xhdpi/ic_launcher.png similarity index 100% rename from observablescrollview-samples/res/drawable-xhdpi/ic_launcher.png rename to samples/res/drawable-xhdpi/ic_launcher.png diff --git a/observablescrollview-samples/res/drawable-xxhdpi/ic_launcher.png b/samples/res/drawable-xxhdpi/ic_launcher.png similarity index 100% rename from observablescrollview-samples/res/drawable-xxhdpi/ic_launcher.png rename to samples/res/drawable-xxhdpi/ic_launcher.png diff --git a/observablescrollview-samples/res/drawable-xxxhdpi/ic_launcher.png b/samples/res/drawable-xxxhdpi/ic_launcher.png similarity index 100% rename from observablescrollview-samples/res/drawable-xxxhdpi/ic_launcher.png rename to samples/res/drawable-xxxhdpi/ic_launcher.png diff --git a/observablescrollview-samples/res/drawable/example.jpeg b/samples/res/drawable/example.jpeg similarity index 100% rename from observablescrollview-samples/res/drawable/example.jpeg rename to samples/res/drawable/example.jpeg diff --git a/observablescrollview-samples/res/drawable/gradient_header_background.xml b/samples/res/drawable/gradient_header_background.xml similarity index 100% rename from observablescrollview-samples/res/drawable/gradient_header_background.xml rename to samples/res/drawable/gradient_header_background.xml diff --git a/observablescrollview-samples/res/drawable/sliding_header_overlay.xml b/samples/res/drawable/sliding_header_overlay.xml similarity index 100% rename from observablescrollview-samples/res/drawable/sliding_header_overlay.xml rename to samples/res/drawable/sliding_header_overlay.xml diff --git a/observablescrollview-samples/res/layout-v11/tab_indicator.xml b/samples/res/layout-v11/tab_indicator.xml similarity index 100% rename from observablescrollview-samples/res/layout-v11/tab_indicator.xml rename to samples/res/layout-v11/tab_indicator.xml diff --git a/observablescrollview-samples/res/layout/activity_about.xml b/samples/res/layout/activity_about.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_about.xml rename to samples/res/layout/activity_about.xml diff --git a/observablescrollview-samples/res/layout/activity_actionbarcontrolgridview.xml b/samples/res/layout/activity_actionbarcontrolgridview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_actionbarcontrolgridview.xml rename to samples/res/layout/activity_actionbarcontrolgridview.xml diff --git a/observablescrollview-samples/res/layout/activity_actionbarcontrollistview.xml b/samples/res/layout/activity_actionbarcontrollistview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_actionbarcontrollistview.xml rename to samples/res/layout/activity_actionbarcontrollistview.xml diff --git a/observablescrollview-samples/res/layout/activity_actionbarcontrolrecyclerview.xml b/samples/res/layout/activity_actionbarcontrolrecyclerview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_actionbarcontrolrecyclerview.xml rename to samples/res/layout/activity_actionbarcontrolrecyclerview.xml diff --git a/observablescrollview-samples/res/layout/activity_actionbarcontrolscrollview.xml b/samples/res/layout/activity_actionbarcontrolscrollview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_actionbarcontrolscrollview.xml rename to samples/res/layout/activity_actionbarcontrolscrollview.xml diff --git a/observablescrollview-samples/res/layout/activity_actionbarcontrolwebview.xml b/samples/res/layout/activity_actionbarcontrolwebview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_actionbarcontrolwebview.xml rename to samples/res/layout/activity_actionbarcontrolwebview.xml diff --git a/observablescrollview-samples/res/layout/activity_fillgap3listview.xml b/samples/res/layout/activity_fillgap3listview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_fillgap3listview.xml rename to samples/res/layout/activity_fillgap3listview.xml diff --git a/observablescrollview-samples/res/layout/activity_fillgap3recyclerview.xml b/samples/res/layout/activity_fillgap3recyclerview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_fillgap3recyclerview.xml rename to samples/res/layout/activity_fillgap3recyclerview.xml diff --git a/observablescrollview-samples/res/layout/activity_fillgap3scrollview.xml b/samples/res/layout/activity_fillgap3scrollview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_fillgap3scrollview.xml rename to samples/res/layout/activity_fillgap3scrollview.xml diff --git a/observablescrollview-samples/res/layout/activity_fillgaplistview.xml b/samples/res/layout/activity_fillgaplistview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_fillgaplistview.xml rename to samples/res/layout/activity_fillgaplistview.xml diff --git a/observablescrollview-samples/res/layout/activity_fillgaprecyclerview.xml b/samples/res/layout/activity_fillgaprecyclerview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_fillgaprecyclerview.xml rename to samples/res/layout/activity_fillgaprecyclerview.xml diff --git a/observablescrollview-samples/res/layout/activity_fillgapscrollview.xml b/samples/res/layout/activity_fillgapscrollview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_fillgapscrollview.xml rename to samples/res/layout/activity_fillgapscrollview.xml diff --git a/observablescrollview-samples/res/layout/activity_flexiblespacetoolbarscrollview.xml b/samples/res/layout/activity_flexiblespacetoolbarscrollview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_flexiblespacetoolbarscrollview.xml rename to samples/res/layout/activity_flexiblespacetoolbarscrollview.xml diff --git a/observablescrollview-samples/res/layout/activity_flexiblespacewithimagelistview.xml b/samples/res/layout/activity_flexiblespacewithimagelistview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_flexiblespacewithimagelistview.xml rename to samples/res/layout/activity_flexiblespacewithimagelistview.xml diff --git a/observablescrollview-samples/res/layout/activity_flexiblespacewithimagerecyclerview.xml b/samples/res/layout/activity_flexiblespacewithimagerecyclerview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_flexiblespacewithimagerecyclerview.xml rename to samples/res/layout/activity_flexiblespacewithimagerecyclerview.xml diff --git a/observablescrollview-samples/res/layout/activity_flexiblespacewithimagescrollview.xml b/samples/res/layout/activity_flexiblespacewithimagescrollview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_flexiblespacewithimagescrollview.xml rename to samples/res/layout/activity_flexiblespacewithimagescrollview.xml diff --git a/observablescrollview-samples/res/layout/activity_flexiblespacewithimagewithviewpagertab2.xml b/samples/res/layout/activity_flexiblespacewithimagewithviewpagertab2.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_flexiblespacewithimagewithviewpagertab2.xml rename to samples/res/layout/activity_flexiblespacewithimagewithviewpagertab2.xml diff --git a/observablescrollview-samples/res/layout/activity_fragmentactionbarcontrol.xml b/samples/res/layout/activity_fragmentactionbarcontrol.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_fragmentactionbarcontrol.xml rename to samples/res/layout/activity_fragmentactionbarcontrol.xml diff --git a/observablescrollview-samples/res/layout/activity_fragmenttransition.xml b/samples/res/layout/activity_fragmenttransition.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_fragmenttransition.xml rename to samples/res/layout/activity_fragmenttransition.xml diff --git a/observablescrollview-samples/res/layout/activity_handletouchgridview.xml b/samples/res/layout/activity_handletouchgridview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_handletouchgridview.xml rename to samples/res/layout/activity_handletouchgridview.xml diff --git a/observablescrollview-samples/res/layout/activity_handletouchlistview.xml b/samples/res/layout/activity_handletouchlistview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_handletouchlistview.xml rename to samples/res/layout/activity_handletouchlistview.xml diff --git a/observablescrollview-samples/res/layout/activity_handletouchrecyclerview.xml b/samples/res/layout/activity_handletouchrecyclerview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_handletouchrecyclerview.xml rename to samples/res/layout/activity_handletouchrecyclerview.xml diff --git a/observablescrollview-samples/res/layout/activity_handletouchscrollview.xml b/samples/res/layout/activity_handletouchscrollview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_handletouchscrollview.xml rename to samples/res/layout/activity_handletouchscrollview.xml diff --git a/observablescrollview-samples/res/layout/activity_handletouchwebview.xml b/samples/res/layout/activity_handletouchwebview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_handletouchwebview.xml rename to samples/res/layout/activity_handletouchwebview.xml diff --git a/observablescrollview-samples/res/layout/activity_main.xml b/samples/res/layout/activity_main.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_main.xml rename to samples/res/layout/activity_main.xml diff --git a/observablescrollview-samples/res/layout/activity_parallaxtoolbarscrollview.xml b/samples/res/layout/activity_parallaxtoolbarscrollview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_parallaxtoolbarscrollview.xml rename to samples/res/layout/activity_parallaxtoolbarscrollview.xml diff --git a/observablescrollview-samples/res/layout/activity_slidingupgridview.xml b/samples/res/layout/activity_slidingupgridview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_slidingupgridview.xml rename to samples/res/layout/activity_slidingupgridview.xml diff --git a/observablescrollview-samples/res/layout/activity_slidinguplistview.xml b/samples/res/layout/activity_slidinguplistview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_slidinguplistview.xml rename to samples/res/layout/activity_slidinguplistview.xml diff --git a/observablescrollview-samples/res/layout/activity_slidinguprecyclerview.xml b/samples/res/layout/activity_slidinguprecyclerview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_slidinguprecyclerview.xml rename to samples/res/layout/activity_slidinguprecyclerview.xml diff --git a/observablescrollview-samples/res/layout/activity_slidingupscrollview.xml b/samples/res/layout/activity_slidingupscrollview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_slidingupscrollview.xml rename to samples/res/layout/activity_slidingupscrollview.xml diff --git a/observablescrollview-samples/res/layout/activity_slidingupwebview.xml b/samples/res/layout/activity_slidingupwebview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_slidingupwebview.xml rename to samples/res/layout/activity_slidingupwebview.xml diff --git a/observablescrollview-samples/res/layout/activity_stickyheaderlistview.xml b/samples/res/layout/activity_stickyheaderlistview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_stickyheaderlistview.xml rename to samples/res/layout/activity_stickyheaderlistview.xml diff --git a/observablescrollview-samples/res/layout/activity_stickyheaderrecyclerview.xml b/samples/res/layout/activity_stickyheaderrecyclerview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_stickyheaderrecyclerview.xml rename to samples/res/layout/activity_stickyheaderrecyclerview.xml diff --git a/observablescrollview-samples/res/layout/activity_stickyheaderscrollview.xml b/samples/res/layout/activity_stickyheaderscrollview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_stickyheaderscrollview.xml rename to samples/res/layout/activity_stickyheaderscrollview.xml diff --git a/observablescrollview-samples/res/layout/activity_stickyheaderwebview.xml b/samples/res/layout/activity_stickyheaderwebview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_stickyheaderwebview.xml rename to samples/res/layout/activity_stickyheaderwebview.xml diff --git a/observablescrollview-samples/res/layout/activity_toolbarcontrolgridview.xml b/samples/res/layout/activity_toolbarcontrolgridview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_toolbarcontrolgridview.xml rename to samples/res/layout/activity_toolbarcontrolgridview.xml diff --git a/observablescrollview-samples/res/layout/activity_toolbarcontrollistview.xml b/samples/res/layout/activity_toolbarcontrollistview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_toolbarcontrollistview.xml rename to samples/res/layout/activity_toolbarcontrollistview.xml diff --git a/observablescrollview-samples/res/layout/activity_toolbarcontrolrecyclerview.xml b/samples/res/layout/activity_toolbarcontrolrecyclerview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_toolbarcontrolrecyclerview.xml rename to samples/res/layout/activity_toolbarcontrolrecyclerview.xml diff --git a/observablescrollview-samples/res/layout/activity_toolbarcontrolscrollview.xml b/samples/res/layout/activity_toolbarcontrolscrollview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_toolbarcontrolscrollview.xml rename to samples/res/layout/activity_toolbarcontrolscrollview.xml diff --git a/observablescrollview-samples/res/layout/activity_toolbarcontrolwebview.xml b/samples/res/layout/activity_toolbarcontrolwebview.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_toolbarcontrolwebview.xml rename to samples/res/layout/activity_toolbarcontrolwebview.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_viewpagertab.xml b/samples/res/layout/activity_viewpagertab.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_viewpagertab.xml rename to samples/res/layout/activity_viewpagertab.xml diff --git a/observablescrollview/src/androidTest/res/layout/activity_viewpagertab2.xml b/samples/res/layout/activity_viewpagertab2.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/activity_viewpagertab2.xml rename to samples/res/layout/activity_viewpagertab2.xml diff --git a/observablescrollview-samples/res/layout/activity_viewpagertabfragment.xml b/samples/res/layout/activity_viewpagertabfragment.xml similarity index 100% rename from observablescrollview-samples/res/layout/activity_viewpagertabfragment.xml rename to samples/res/layout/activity_viewpagertabfragment.xml diff --git a/observablescrollview-samples/res/layout/divider.xml b/samples/res/layout/divider.xml similarity index 100% rename from observablescrollview-samples/res/layout/divider.xml rename to samples/res/layout/divider.xml diff --git a/observablescrollview-samples/res/layout/fragment_actionbarcontrollistview.xml b/samples/res/layout/fragment_actionbarcontrollistview.xml similarity index 100% rename from observablescrollview-samples/res/layout/fragment_actionbarcontrollistview.xml rename to samples/res/layout/fragment_actionbarcontrollistview.xml diff --git a/observablescrollview-samples/res/layout/fragment_fragmenttransition_default.xml b/samples/res/layout/fragment_fragmenttransition_default.xml similarity index 100% rename from observablescrollview-samples/res/layout/fragment_fragmenttransition_default.xml rename to samples/res/layout/fragment_fragmenttransition_default.xml diff --git a/observablescrollview-samples/res/layout/fragment_fragmenttransition_second.xml b/samples/res/layout/fragment_fragmenttransition_second.xml similarity index 100% rename from observablescrollview-samples/res/layout/fragment_fragmenttransition_second.xml rename to samples/res/layout/fragment_fragmenttransition_second.xml diff --git a/observablescrollview/src/androidTest/res/layout/fragment_gridview.xml b/samples/res/layout/fragment_gridview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/fragment_gridview.xml rename to samples/res/layout/fragment_gridview.xml diff --git a/observablescrollview/src/androidTest/res/layout/fragment_listview.xml b/samples/res/layout/fragment_listview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/fragment_listview.xml rename to samples/res/layout/fragment_listview.xml diff --git a/observablescrollview/src/androidTest/res/layout/fragment_recyclerview.xml b/samples/res/layout/fragment_recyclerview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/fragment_recyclerview.xml rename to samples/res/layout/fragment_recyclerview.xml diff --git a/observablescrollview-samples/res/layout/fragment_scrollview.xml b/samples/res/layout/fragment_scrollview.xml similarity index 100% rename from observablescrollview-samples/res/layout/fragment_scrollview.xml rename to samples/res/layout/fragment_scrollview.xml diff --git a/observablescrollview-samples/res/layout/fragment_scrollview_noheader.xml b/samples/res/layout/fragment_scrollview_noheader.xml similarity index 100% rename from observablescrollview-samples/res/layout/fragment_scrollview_noheader.xml rename to samples/res/layout/fragment_scrollview_noheader.xml diff --git a/observablescrollview-samples/res/layout/fragment_viewpagertabfragment_parent.xml b/samples/res/layout/fragment_viewpagertabfragment_parent.xml similarity index 100% rename from observablescrollview-samples/res/layout/fragment_viewpagertabfragment_parent.xml rename to samples/res/layout/fragment_viewpagertabfragment_parent.xml diff --git a/observablescrollview/src/androidTest/res/layout/fragment_webview.xml b/samples/res/layout/fragment_webview.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/fragment_webview.xml rename to samples/res/layout/fragment_webview.xml diff --git a/observablescrollview-samples/res/layout/gradient_header.xml b/samples/res/layout/gradient_header.xml similarity index 100% rename from observablescrollview-samples/res/layout/gradient_header.xml rename to samples/res/layout/gradient_header.xml diff --git a/observablescrollview-samples/res/layout/list_item_handletouch.xml b/samples/res/layout/list_item_handletouch.xml similarity index 100% rename from observablescrollview-samples/res/layout/list_item_handletouch.xml rename to samples/res/layout/list_item_handletouch.xml diff --git a/observablescrollview-samples/res/layout/list_item_main.xml b/samples/res/layout/list_item_main.xml similarity index 100% rename from observablescrollview-samples/res/layout/list_item_main.xml rename to samples/res/layout/list_item_main.xml diff --git a/observablescrollview/src/androidTest/res/layout/padding.xml b/samples/res/layout/padding.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/padding.xml rename to samples/res/layout/padding.xml diff --git a/observablescrollview-samples/res/layout/recycler_header.xml b/samples/res/layout/recycler_header.xml similarity index 100% rename from observablescrollview-samples/res/layout/recycler_header.xml rename to samples/res/layout/recycler_header.xml diff --git a/observablescrollview/src/androidTest/res/layout/tab_indicator.xml b/samples/res/layout/tab_indicator.xml similarity index 100% rename from observablescrollview/src/androidTest/res/layout/tab_indicator.xml rename to samples/res/layout/tab_indicator.xml diff --git a/observablescrollview-samples/res/menu/menu_main.xml b/samples/res/menu/menu_main.xml similarity index 100% rename from observablescrollview-samples/res/menu/menu_main.xml rename to samples/res/menu/menu_main.xml diff --git a/observablescrollview-samples/res/values-ar/strings.xml b/samples/res/values-ar/strings.xml similarity index 100% rename from observablescrollview-samples/res/values-ar/strings.xml rename to samples/res/values-ar/strings.xml diff --git a/observablescrollview-samples/res/values-w820dp/dimens.xml b/samples/res/values-w820dp/dimens.xml similarity index 100% rename from observablescrollview-samples/res/values-w820dp/dimens.xml rename to samples/res/values-w820dp/dimens.xml diff --git a/observablescrollview-samples/res/values/colors.xml b/samples/res/values/colors.xml similarity index 100% rename from observablescrollview-samples/res/values/colors.xml rename to samples/res/values/colors.xml diff --git a/observablescrollview-samples/res/values/dimens.xml b/samples/res/values/dimens.xml similarity index 100% rename from observablescrollview-samples/res/values/dimens.xml rename to samples/res/values/dimens.xml diff --git a/observablescrollview-samples/res/values/strings.xml b/samples/res/values/strings.xml similarity index 100% rename from observablescrollview-samples/res/values/strings.xml rename to samples/res/values/strings.xml diff --git a/observablescrollview-samples/res/values/strings_license.xml b/samples/res/values/strings_license.xml similarity index 100% rename from observablescrollview-samples/res/values/strings_license.xml rename to samples/res/values/strings_license.xml diff --git a/observablescrollview/src/androidTest/res/values/styles.xml b/samples/res/values/styles.xml similarity index 100% rename from observablescrollview/src/androidTest/res/values/styles.xml rename to samples/res/values/styles.xml diff --git a/observablescrollview-samples/src/androidTest/java/com/github/ksoichiro/app/ApplicationTest.java b/samples/src/androidTest/java/com/github/ksoichiro/app/ApplicationTest.java similarity index 100% rename from observablescrollview-samples/src/androidTest/java/com/github/ksoichiro/app/ApplicationTest.java rename to samples/src/androidTest/java/com/github/ksoichiro/app/ApplicationTest.java diff --git a/observablescrollview-samples/src/debug/res/drawable-xhdpi/ic_launcher.png b/samples/src/debug/res/drawable-xhdpi/ic_launcher.png similarity index 100% rename from observablescrollview-samples/src/debug/res/drawable-xhdpi/ic_launcher.png rename to samples/src/debug/res/drawable-xhdpi/ic_launcher.png diff --git a/observablescrollview-samples/src/debug/res/drawable-xxhdpi/ic_launcher.png b/samples/src/debug/res/drawable-xxhdpi/ic_launcher.png similarity index 100% rename from observablescrollview-samples/src/debug/res/drawable-xxhdpi/ic_launcher.png rename to samples/src/debug/res/drawable-xxhdpi/ic_launcher.png diff --git a/observablescrollview-samples/src/debug/res/drawable-xxxhdpi/ic_launcher.png b/samples/src/debug/res/drawable-xxxhdpi/ic_launcher.png similarity index 100% rename from observablescrollview-samples/src/debug/res/drawable-xxxhdpi/ic_launcher.png rename to samples/src/debug/res/drawable-xxxhdpi/ic_launcher.png diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlGridViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlGridViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlGridViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlGridViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlRecyclerViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlRecyclerViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlRecyclerViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlWebViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlWebViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlWebViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2BaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2BaseActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2BaseActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2BaseActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2RecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2RecyclerViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2RecyclerViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2RecyclerViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3BaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3BaseActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3BaseActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3BaseActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3RecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3RecyclerViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3RecyclerViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3RecyclerViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapRecyclerViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapRecyclerViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapRecyclerViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionDefaultFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionDefaultFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionDefaultFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionDefaultFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchGridViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchGridViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchGridViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchGridViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchRecyclerViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchRecyclerViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchRecyclerViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchWebViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchWebViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/HandleTouchWebViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomRecyclerViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomRecyclerViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ScrollFromBottomRecyclerViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleHeaderRecyclerAdapter.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleHeaderRecyclerAdapter.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleHeaderRecyclerAdapter.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleHeaderRecyclerAdapter.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleRecyclerAdapter.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleRecyclerAdapter.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleRecyclerAdapter.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SimpleRecyclerAdapter.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpGridViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpGridViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpGridViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpGridViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpRecyclerViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpRecyclerViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpRecyclerViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpWebViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpWebViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpWebViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderRecyclerViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderRecyclerViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderRecyclerViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderWebViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderWebViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/StickyHeaderWebViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlBaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlBaseActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlBaseActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlBaseActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlGridViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlGridViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlGridViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlGridViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlRecyclerViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlRecyclerViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlRecyclerViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlWebViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlWebViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlWebViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2Activity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2Activity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2Activity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2Activity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2GridViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2GridViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2GridViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2GridViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ListViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ListViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ListViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2RecyclerViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2RecyclerViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2RecyclerViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2RecyclerViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ScrollViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ScrollViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ScrollViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2ScrollViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2WebViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2WebViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2WebViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTab2WebViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentGridViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentGridViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentGridViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentGridViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentListViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentListViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentListViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentParentFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentParentFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentParentFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentParentFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentRecyclerViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentRecyclerViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentRecyclerViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentRecyclerViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentScrollViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentScrollViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentScrollViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentScrollViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentWebViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentWebViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentWebViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentWebViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabRecyclerViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabRecyclerViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabRecyclerViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabRecyclerViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java diff --git a/observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewFragment.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewFragment.java rename to samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewFragment.java diff --git a/observablescrollview-samples/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java b/samples/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java similarity index 100% rename from observablescrollview-samples/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java rename to samples/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java diff --git a/observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java b/samples/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java similarity index 100% rename from observablescrollview/src/androidTest/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java rename to samples/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java diff --git a/observablescrollview-samples/version.gradle b/samples/version.gradle similarity index 100% rename from observablescrollview-samples/version.gradle rename to samples/version.gradle diff --git a/settings.gradle b/settings.gradle index 63bd0ca3..69de25de 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':observablescrollview', ':observablescrollview-samples' +include ':library', ':samples' diff --git a/wercker.yml b/wercker.yml index 3f6a915f..7765eb2d 100644 --- a/wercker.yml +++ b/wercker.yml @@ -23,6 +23,6 @@ build: - script: name: inspect build result code: | - ls -la ./observablescrollview-samples/build/outputs/apk - cp ./observablescrollview-samples/build/outputs/apk/*.apk $WERCKER_REPORT_ARTIFACTS_DIR + ls -la ./samples/build/outputs/apk + cp ./samples/build/outputs/apk/*.apk $WERCKER_REPORT_ARTIFACTS_DIR rm -f $WERCKER_REPORT_ARTIFACTS_DIR/*-unaligned.apk From 873ff86e41c31249e4ff73576554db03e66cc1a1 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 18 Mar 2015 22:02:55 +0900 Subject: [PATCH 043/208] Removed "-q" flag of Gradle task for Travis CI because it often stops outputs and results in error. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 369fc300..97309768 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,6 @@ script: # Release build type is only for Google Play store currently, # which resolve dependency from Maven Central. # This causes build errors while developing a new feature, so disable release build. - - travis_retry ./gradlew --full-stacktrace -q assembleDevDebug connectedCheck + - travis_retry ./gradlew --full-stacktrace assembleDevDebug connectedCheck after_success: - ./gradlew :library:coveralls From adebba96f1f96b05ef0a6b3fac76622ebd70de9d Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 18 Mar 2015 23:06:34 +0900 Subject: [PATCH 044/208] Added FAQ. --- README.md | 4 ++-- docs/faq.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 docs/faq.md diff --git a/README.md b/README.md index d3761ec5..b2fb0944 100644 --- a/README.md +++ b/README.md @@ -196,12 +196,12 @@ please let me know via [email](soichiro.kashima@gmail.com) or pull requests or i ## Frequently asked questions - +[Please see here](docs/faq.md). ## Contributions Any contributions are welcome! -Please check the [contributing guideline](https://github.com/ksoichiro/Android-ObservableScrollView/tree/master/CONTRIBUTING.md) before submitting a new issue. +Please check the [FAQ](docs/faq.md) and [contributing guideline](https://github.com/ksoichiro/Android-ObservableScrollView/tree/master/CONTRIBUTING.md) before submitting a new issue. ## Developed By diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 00000000..839212f1 --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,59 @@ +# Frequently asked questions + +These are frequently asked questions from GitHub issues, emails I received from users, etc. + +## Q. When do you implement the new sample? I'm waiting for it so long. + +### A. Sorry and please help me if you could. + +First of all, I'm so grateful to all of you that you're interested in this project. +And of course I'd like to respond to all of your request! +But unfortunately, I don't have enough time to do that... +If you're interested in implementing new samples or fixing bugs, please help me. (Pull requests are welcome!) + +## Q. Does this library support Eclipse? + +### A. Yes, it does partially. + +Please see [here](eclipse.md) for details. + +## Q. Doesn't work! + +### A. Please describe your issue as much as possible. + +As I wrote in [the contribution guideline](../CONTRIBUTING.md), +the library itself only provides the scroll information, +and creating awesome scrolling effects depends deeply on how you use it: layout, offset calculation to animate views, etc. + +Therefore, if you find an issue, please describe not only the issue itself but also the related information like Activity name that has problems, operation to produce the issue, modified code (if any), etc. + +## Q. Paths are too long to build! + +### A. Move your projects upper to shorten the paths. + +On Windows environment, if you locate the project directory like `C:\Documents and Settings\user\workspace\Android-ObservableScrollView\`, +Android Studio causes errors and fail to build the project because some of the paths in the output files are too long. + +If you see this kind of problem, please move the project directory to upper directory to shorten the paths. + +## Q. Sample codes are too complex! + +### A. Sorry, please help me refactor them... + +Yes, I know they're too complex as samples. + +I just aimed to show you that you can realize these kind of effects when you use this library. +I'd very appreciate it if you help me refactor them. + +## Q. Updates of the library in master branch seems not be synced to Maven Central. + +### A. Sorry, please wait for the next release. + +I need to do following tasks to release the new version to the Maven Central, and it takes time, so please wait for the next release. +If you're in a hurry, please send me an email. I'll release it as soon as possible. + +1. Test the library, at least the tests on Travis CI should pass. +1. Check the compatibility for the past versions. +1. Release SNAPSHOT version to the Sonatype snapshot repository. +1. Release to the Sonatype repository. If it's successfully released, it will be synced to Maven Central in a couple of hours. +1. Update README to prompt to use the latest version. From 5bce0e5bdd24abcca8b3a5a8bf0132c6fd0e2df8 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 18 Mar 2015 23:31:06 +0900 Subject: [PATCH 045/208] Released 1.5.1-SNAPSHOT. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b1611363..5fab10e0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.5.0 +VERSION_NAME=1.5.1-SNAPSHOT SYNCED_VERSION_NAME=1.5.0 GROUP=com.github.ksoichiro From 7b0ab48872facc204e5da56ba0b6c05d051acbdf Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 18 Mar 2015 23:56:31 +0900 Subject: [PATCH 046/208] Moved res directory for Eclipse. --- library/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/library/build.gradle b/library/build.gradle index c673061d..20f59fb2 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -45,6 +45,7 @@ android { sourceSets { main { manifest.srcFile 'AndroidManifest.xml' + res.srcDirs = ['res'] } } From c2c8d6d18f50ba3f3779db6c2e2c9748f2b6ac39 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 18 Mar 2015 23:58:38 +0900 Subject: [PATCH 047/208] Formatted for some codes. --- .../samples/SlidingUpBaseActivity.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java index 9656fdcf..06d897ac 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java @@ -188,13 +188,13 @@ public void onUpOrCancelMotionEvent(ScrollState scrollState) { public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY) { final int minInterceptionLayoutY = -mIntersectionHeight; - //slight fix for untappable floating action button for larger screens + // slight fix for untappable floating action button for larger screens Rect fabRect = new Rect(); mFab.getHitRect(fabRect); - //if the user's touch is within the floating action button's touch area, dont intercept - if (fabRect.contains((int)ev.getX(), (int)ev.getY())) { + // if the user's touch is within the floating action button's touch area, don't intercept + if (fabRect.contains((int) ev.getX(), (int) ev.getY())) { return false; - }else{ + } else { return minInterceptionLayoutY < (int) ViewHelper.getY(mInterceptionLayout) || (moving && mScrollable.getCurrentScrollY() - diffY < 0); } From 9fb76de734b1b527195ebcb248412bd7a3a1ab0c Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Thu, 19 Mar 2015 00:16:00 +0900 Subject: [PATCH 048/208] Removed redundant "orientation" attributes in sample layout XML. --- .../res/layout/activity_touchinterception_gridview.xml | 3 +-- .../res/layout/activity_touchinterception_listview.xml | 3 +-- .../res/layout/activity_touchinterception_recyclerview.xml | 3 +-- .../res/layout/activity_touchinterception_scrollview.xml | 3 +-- .../res/layout/activity_touchinterception_webview.xml | 3 +-- samples/res/layout/activity_fillgap3listview.xml | 3 +-- samples/res/layout/activity_fillgap3recyclerview.xml | 3 +-- samples/res/layout/activity_fillgap3scrollview.xml | 3 +-- samples/res/layout/activity_fillgaplistview.xml | 3 +-- samples/res/layout/activity_fillgaprecyclerview.xml | 3 +-- samples/res/layout/activity_fillgapscrollview.xml | 3 +-- .../res/layout/activity_flexiblespacetoolbarscrollview.xml | 5 +---- .../res/layout/activity_flexiblespacewithimagelistview.xml | 3 +-- .../layout/activity_flexiblespacewithimagerecyclerview.xml | 3 +-- .../res/layout/activity_flexiblespacewithimagescrollview.xml | 3 +-- .../activity_flexiblespacewithimagewithviewpagertab2.xml | 3 +-- samples/res/layout/activity_parallaxtoolbarscrollview.xml | 3 +-- samples/res/layout/activity_slidingupgridview.xml | 3 +-- samples/res/layout/activity_slidinguplistview.xml | 3 +-- samples/res/layout/activity_slidinguprecyclerview.xml | 3 +-- samples/res/layout/activity_slidingupscrollview.xml | 3 +-- samples/res/layout/activity_slidingupwebview.xml | 3 +-- samples/res/layout/activity_stickyheaderlistview.xml | 3 +-- samples/res/layout/activity_stickyheaderrecyclerview.xml | 3 +-- samples/res/layout/activity_stickyheaderscrollview.xml | 3 +-- samples/res/layout/activity_stickyheaderwebview.xml | 3 +-- 26 files changed, 26 insertions(+), 54 deletions(-) diff --git a/library/src/androidTest/res/layout/activity_touchinterception_gridview.xml b/library/src/androidTest/res/layout/activity_touchinterception_gridview.xml index 5077575f..043e6175 100644 --- a/library/src/androidTest/res/layout/activity_touchinterception_gridview.xml +++ b/library/src/androidTest/res/layout/activity_touchinterception_gridview.xml @@ -16,8 +16,7 @@ + android:clipChildren="false"> + android:clipChildren="false"> + android:clipChildren="false"> + android:clipChildren="false"> + android:clipChildren="false"> + android:clipChildren="false"> + android:clipChildren="false"> + android:clipChildren="false"> + android:clipChildren="false"> + android:clipChildren="false"> + android:clipChildren="false"> + android:layout_height="match_parent"> + android:layout_height="match_parent"> + android:layout_height="match_parent"> + android:layout_height="match_parent"> + android:background="?attr/colorPrimary"> + android:layout_height="match_parent"> + android:clipChildren="false"> + + + + + + + + + + + + diff --git a/samples/res/values/strings.xml b/samples/res/values/strings.xml index 9ba24c08..e20f3a7e 100644 --- a/samples/res/values/strings.xml +++ b/samples/res/values/strings.xml @@ -42,6 +42,7 @@ Handling touch with RecyclerView Handling touch with ScrollView Handling touch with WebView + Parallax listView & toolbar Parallax scrollView & toolbar ListView, scrolling from bottom RecyclerView, scrolling from bottom diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java new file mode 100644 index 00000000..e036f5be --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java @@ -0,0 +1,103 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.AbsListView; + +import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.nineoldandroids.view.ViewHelper; + +public class ParallaxToolbarListViewActivity extends BaseActivity implements ObservableScrollViewCallbacks { + + private View mImageView; + private View mToolbarView; + private View mListBackgroundView; + private ObservableListView mListView; + private int mParallaxImageHeight; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_parallaxtoolbarlistview) ; + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + + mImageView = findViewById(R.id.image); + mToolbarView = findViewById(R.id.toolbar); + mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(0, getResources().getColor(R.color.primary))); + + mParallaxImageHeight = getResources().getDimensionPixelSize(R.dimen.parallax_image_height); + + mListView = (ObservableListView) findViewById(R.id.list); + mListView.setScrollViewCallbacks(this); + // Set padding view for ListView. This is the flexible space. + View paddingView = new View(this); + AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, + mParallaxImageHeight); + paddingView.setLayoutParams(lp); + + // This is required to disable header's list selector effect + paddingView.setClickable(true); + + mListView.addHeaderView(paddingView); + setDummyData(mListView); + + // mListBackgroundView makes ListView's background except header view. + mListBackgroundView = findViewById(R.id.list_background); + final View contentView = getWindow().getDecorView().findViewById(android.R.id.content); + contentView.post(new Runnable() { + @Override + public void run() { + // mListBackgroundView's should fill its parent vertically + // but the height of the content view is 0 on 'onCreate'. + // So we should get it with post(). + mListBackgroundView.getLayoutParams().height = contentView.getHeight(); + } + }); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + onScrollChanged(mListView.getCurrentScrollY(), false, false); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + int baseColor = getResources().getColor(R.color.primary); + float alpha = 1 - (float) Math.max(0, mParallaxImageHeight - scrollY) / mParallaxImageHeight; + mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, baseColor)); + ViewHelper.setTranslationY(mImageView, -scrollY / 2); + + // Translate list background + ViewHelper.setTranslationY(mListBackgroundView, Math.max(0, -scrollY + mParallaxImageHeight)); + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } +} From a552cb111a695ea69d63a152d2b0d51f6ac818ae Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Sat, 11 Apr 2015 11:51:29 +0800 Subject: [PATCH 050/208] Added a Flexible Space example with Toolbar & WebView --- samples/AndroidManifest.xml | 10 ++ .../activity_flexiblespacetoolbarwebview.xml | 95 +++++++++++++ samples/res/values/strings.xml | 1 + .../FlexibleSpaceToolbarWebViewActivity.java | 130 ++++++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 samples/res/layout/activity_flexiblespacetoolbarwebview.xml create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java diff --git a/samples/AndroidManifest.xml b/samples/AndroidManifest.xml index da039c99..51db11a2 100644 --- a/samples/AndroidManifest.xml +++ b/samples/AndroidManifest.xml @@ -226,6 +226,16 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/res/values/strings.xml b/samples/res/values/strings.xml index e20f3a7e..1ab4179c 100644 --- a/samples/res/values/strings.xml +++ b/samples/res/values/strings.xml @@ -35,6 +35,7 @@ Fill Gap & ScrollView Flexible Space Flexible Space + Flexible Space ListView on Fragment & Action Bar ActionBar & Toolbar manipulation with Fragment Handling touch with GridView diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java new file mode 100644 index 00000000..972c2c49 --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java @@ -0,0 +1,130 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.webkit.WebView; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.nineoldandroids.view.ViewHelper; + +public class FlexibleSpaceToolbarWebViewActivity extends BaseActivity implements ObservableScrollViewCallbacks { + + private View mFlexibleSpaceView; + private View mToolbarView; + private TextView mTitleView; + private int mFlexibleSpaceHeight; + private WebView mWebView; + private View mWebViewContainer; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_flexiblespacetoolbarwebview); + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + mFlexibleSpaceView = findViewById(R.id.flexible_space); + mTitleView = (TextView) findViewById(R.id.title); + mTitleView.setText(getTitle()); + setTitle(null); + mToolbarView = findViewById(R.id.toolbar); + + mWebView = (WebView) findViewById(R.id.webView); + mWebViewContainer = findViewById(R.id.webViewContainer); + + final ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll); + scrollView.setScrollViewCallbacks(this); + + mWebView.loadUrl("file:///android_asset/lipsum.html"); + + mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_height); + int flexibleSpaceAndToolbarHeight = mFlexibleSpaceHeight + getActionBarSize(); + + final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mWebView.getLayoutParams(); + layoutParams.topMargin = flexibleSpaceAndToolbarHeight; + mWebView.setLayoutParams(layoutParams); + + mFlexibleSpaceView.getLayoutParams().height = flexibleSpaceAndToolbarHeight; + + ScrollUtils.addOnGlobalLayoutListener(mTitleView, new Runnable() { + @Override + public void run() { + updateFlexibleSpaceText(scrollView.getCurrentScrollY()); + } + }); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + updateFlexibleSpaceText(scrollY); + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + + private void updateFlexibleSpaceText(final int scrollY) { + ViewHelper.setTranslationY(mFlexibleSpaceView, -scrollY); + int adjustedScrollY = scrollY; + if (scrollY < 0) { + adjustedScrollY = 0; + } else if (mFlexibleSpaceHeight < scrollY) { + adjustedScrollY = mFlexibleSpaceHeight; + } + + // Special logic for WebView. + adjustTopMargin(mWebViewContainer, adjustedScrollY <= mFlexibleSpaceHeight ? 0 : mFlexibleSpaceHeight + getActionBarSize()); + + float maxScale = (float) (mFlexibleSpaceHeight - mToolbarView.getHeight()) / mToolbarView.getHeight(); + float scale = maxScale * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight; + + ViewHelper.setPivotX(mTitleView, 0); + ViewHelper.setPivotY(mTitleView, 0); + ViewHelper.setScaleX(mTitleView, 1 + scale); + ViewHelper.setScaleY(mTitleView, 1 + scale); + ViewHelper.setTranslationY(mTitleView, ViewHelper.getTranslationY(mFlexibleSpaceView) + mFlexibleSpaceView.getHeight() - mTitleView.getHeight() * (1 + scale)); + int maxTitleTranslationY = mToolbarView.getHeight() + mFlexibleSpaceHeight - (int) (mTitleView.getHeight() * (1 + scale)); + int titleTranslationY = (int) (maxTitleTranslationY * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight); + ViewHelper.setTranslationY(mTitleView, titleTranslationY); + } + + private void adjustTopMargin(View view, int topMargin) { + final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams(); + + if (layoutParams.topMargin == topMargin) { + return; + } + + layoutParams.topMargin = topMargin; + + view.setLayoutParams(layoutParams); + } +} From 9fc4412be081b299c42a9fd267e58fa13a39b220 Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Sun, 12 Apr 2015 14:40:09 +0800 Subject: [PATCH 051/208] Filter UI for the sample browser. --- samples/AndroidManifest.xml | 5 +- samples/build.gradle | 8 +-- samples/res/layout/activity_main.xml | 34 ++++++++- .../samples/MainActivity.java | 69 ++++++++++++++++--- 4 files changed, 99 insertions(+), 17 deletions(-) diff --git a/samples/AndroidManifest.xml b/samples/AndroidManifest.xml index da039c99..43cb84fb 100644 --- a/samples/AndroidManifest.xml +++ b/samples/AndroidManifest.xml @@ -21,7 +21,7 @@ + android:label="@string/title_activity_main" + android:theme="@style/AppTheme.Toolbar"> diff --git a/samples/build.gradle b/samples/build.gradle index c8f3f029..5eee459b 100644 --- a/samples/build.gradle +++ b/samples/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' + classpath 'com.android.tools.build:gradle:1.1.3' } } @@ -19,7 +19,7 @@ repositories { } dependencies { - compile 'com.android.support:appcompat-v7:21.0.2' + compile 'com.android.support:appcompat-v7:22.0.0' compile 'com.nineoldandroids:library:2.4.0' compile 'com.melnykov:floatingactionbutton:1.0.7' debugCompile project(':library') @@ -34,8 +34,8 @@ apply from: 'version.gradle' project.ext.versionInfo.releaseVersionName = SYNCED_VERSION_NAME android { - compileSdkVersion 21 - buildToolsVersion "21.1.1" + compileSdkVersion 22 + buildToolsVersion "22.0.1" defaultConfig { applicationId "com.github.ksoichiro.android.observablescrollview.samples" diff --git a/samples/res/layout/activity_main.xml b/samples/res/layout/activity_main.xml index f2dd4cbb..4944b618 100644 --- a/samples/res/layout/activity_main.xml +++ b/samples/res/layout/activity_main.xml @@ -13,9 +13,37 @@ See the License for the specific language governing permissions and limitations under the License. --> - \ No newline at end of file + tools:context=".MainActivity" + android:orientation="vertical" + > + + + + + + + + + \ No newline at end of file diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java index 50fd880a..d2aa280e 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java @@ -16,17 +16,22 @@ package com.github.ksoichiro.android.observablescrollview.samples; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; +import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; +import android.widget.Spinner; +import android.widget.SpinnerAdapter; import java.text.Collator; import java.util.ArrayList; @@ -51,18 +56,56 @@ public int compare(Map lhs, Map rhs) { return collator.compare(lhs.get("className"), rhs.get("className")); } }; + private ListView listView; + + // Quickly navigate through the examples. + static enum Filter { + All, + GridView, + RecyclerView, + ScrollView, + ListView, + WebView, + Toolbar, + ActionBar, + FlexibleSpace, + Parallax, + ViewPager, + } + + Filter currentFilter = Filter.All; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - ListView listView = (ListView) findViewById(android.R.id.list); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + listView = (ListView) findViewById(android.R.id.list); + listView.setOnItemClickListener(this); + + Spinner spinner = (Spinner) findViewById(R.id.spinner_toolbar); + spinner.setAdapter(new FilterAdapter(this)); + spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + currentFilter = Filter.values()[position]; + refreshData(); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + } + + private void refreshData() { listView.setAdapter(new SimpleAdapter(this, getData(), R.layout.list_item_main, new String[]{TAG_CLASS_NAME, TAG_DESCRIPTION,}, new int[]{R.id.className, R.id.description,})); - listView.setOnItemClickListener(this); } @Override @@ -111,12 +154,16 @@ private List> getData() { if (nameLabel.contains(".")) { nameLabel = nameLabel.replaceAll("[^.]*\\.", ""); } - addItem(data, - nameLabel, - nextLabel, - activityIntent( - info.activityInfo.applicationInfo.packageName, - info.activityInfo.name)); + + // Filter logic. + if (currentFilter == Filter.All || nameLabel.contains(currentFilter.name())) { + addItem(data, + nameLabel, + nextLabel, + activityIntent( + info.activityInfo.applicationInfo.packageName, + info.activityInfo.name)); + } } } @@ -148,4 +195,10 @@ public void onItemClick(AdapterView parent, View view, int position, long id) Intent intent = (Intent) map.get(TAG_INTENT); startActivity(intent); } + + private class FilterAdapter extends ArrayAdapter { + public FilterAdapter(Context context) { + super(context, android.R.layout.simple_list_item_1, Filter.values()); + } + } } \ No newline at end of file From be167bdbcec630ef7828693a69ac1339327d7618 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 1 May 2015 09:35:54 +0900 Subject: [PATCH 052/208] Updated components definition for Travis CI since the example app now requires new SDK and build tools. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 97309768..b5270ad5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,11 @@ language: android android: components: - build-tools-21.1.1 + - build-tools-22.0.1 - tools - android-19 - android-21 + - android-22 - sys-img-armeabi-v7a-android-19 - extra-android-support - extra-android-m2repository From cc73c69665169ed3e217f480b34f7dd395da20ad Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 1 May 2015 10:15:25 +0900 Subject: [PATCH 053/208] #109 Updated AppCompat lib to 22.1.1 in the sample app, and replaced deprecated ActionBarActivity to AppCompatActivity. --- samples/build.gradle | 2 +- .../android/observablescrollview/samples/AboutActivity.java | 4 ++-- .../android/observablescrollview/samples/BaseActivity.java | 4 ++-- .../samples/FragmentActionBarControlListViewFragment.java | 4 ++-- .../samples/FragmentTransitionSecondFragment.java | 6 +++--- .../android/observablescrollview/samples/MainActivity.java | 5 ++--- .../samples/ViewPagerTabFragmentParentFragment.java | 4 ++-- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/samples/build.gradle b/samples/build.gradle index 5eee459b..c567e594 100644 --- a/samples/build.gradle +++ b/samples/build.gradle @@ -19,7 +19,7 @@ repositories { } dependencies { - compile 'com.android.support:appcompat-v7:22.0.0' + compile 'com.android.support:appcompat-v7:22.1.1' compile 'com.nineoldandroids:library:2.4.0' compile 'com.melnykov:floatingactionbutton:1.0.7' debugCompile project(':library') diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java index fa656382..b4e1df1b 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java @@ -20,7 +20,7 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.text.Html; import android.text.util.Linkify; import android.util.TypedValue; @@ -30,7 +30,7 @@ import android.widget.LinearLayout; import android.widget.TextView; -public class AboutActivity extends ActionBarActivity { +public class AboutActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java index 3ee7edfb..2e776dd2 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java @@ -17,7 +17,7 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.content.res.TypedArray; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.RecyclerView; import android.util.TypedValue; import android.view.View; @@ -28,7 +28,7 @@ import java.util.ArrayList; -public abstract class BaseActivity extends ActionBarActivity { +public abstract class BaseActivity extends AppCompatActivity { private static final int NUM_OF_ITEMS = 100; private static final int NUM_OF_ITEMS_FEW = 3; diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java index d4bef833..2f9fae3b 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java @@ -18,7 +18,7 @@ import android.os.Bundle; import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -55,7 +55,7 @@ public void onDownMotionEvent() { @Override public void onUpOrCancelMotionEvent(ScrollState scrollState) { - ActionBarActivity activity = (ActionBarActivity) getActivity(); + AppCompatActivity activity = (AppCompatActivity) getActivity(); if (activity == null) { return; } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java index ff63644a..6f5e7789 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java @@ -17,7 +17,7 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -42,14 +42,14 @@ public void onDestroyView() { } private void showToolbar() { - ActionBarActivity activity = (ActionBarActivity) getActivity(); + AppCompatActivity activity = (AppCompatActivity) getActivity(); if (activity != null) { activity.getSupportActionBar().show(); } } private void hideToolbar() { - ActionBarActivity activity = (ActionBarActivity) getActivity(); + AppCompatActivity activity = (AppCompatActivity) getActivity(); if (activity != null) { activity.getSupportActionBar().hide(); } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java index d2aa280e..85f3f9c7 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java @@ -21,7 +21,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; @@ -31,7 +31,6 @@ import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.Spinner; -import android.widget.SpinnerAdapter; import java.text.Collator; import java.util.ArrayList; @@ -42,7 +41,7 @@ import java.util.Map; -public class MainActivity extends ActionBarActivity implements AdapterView.OnItemClickListener { +public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { private static final String CATEGORY_SAMPLES = MainActivity.class.getPackage().getName(); private static final String TAG_CLASS_NAME = "className"; private static final String TAG_DESCRIPTION = "description"; diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentParentFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentParentFragment.java index 01deef91..00d9464f 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentParentFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabFragmentParentFragment.java @@ -20,7 +20,7 @@ import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.view.ViewPager; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -56,7 +56,7 @@ public class ViewPagerTabFragmentParentFragment extends BaseFragment implements public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_viewpagertabfragment_parent, container, false); - ActionBarActivity parentActivity = (ActionBarActivity) getActivity(); + AppCompatActivity parentActivity = (AppCompatActivity) getActivity(); mPagerAdapter = new NavigationAdapter(getChildFragmentManager()); mPager = (ViewPager) view.findViewById(R.id.pager); mPager.setAdapter(mPagerAdapter); From 825c919243b1a9f8c4616729c46756e862dd4a1b Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 1 May 2015 10:57:36 +0900 Subject: [PATCH 054/208] #87 Fixed lagging when scrolling on FlexibleSpaceWithImageWithViewPagerTab2Activity with @Jalcubo's solution. --- ...bleSpaceWithImageWithViewPagerTab2Activity.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java index 9f1e958d..85ab8d9e 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java @@ -97,6 +97,15 @@ protected void onCreate(Bundle savedInstanceState) { ScrollUtils.addOnGlobalLayoutListener(mInterceptionLayout, new Runnable() { @Override public void run() { + // Extra space is required to move mInterceptionLayout when it's scrolled. + // It's better to adjust its height when it's laid out + // than to adjust the height when scroll events (onMoveMotionEvent) occur + // because it causes lagging. + // See #87: https://github.com/ksoichiro/Android-ObservableScrollView/issues/87 + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams(); + lp.height = getScreenHeight() + mFlexibleSpaceHeight; + mInterceptionLayout.requestLayout(); + updateFlexibleSpace(); } }); @@ -158,11 +167,6 @@ public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) { int flexibleSpace = mFlexibleSpaceHeight - mTabHeight; float translationY = ScrollUtils.getFloat(ViewHelper.getTranslationY(mInterceptionLayout) + diffY, -flexibleSpace, 0); updateFlexibleSpace(translationY); - if (translationY < 0) { - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mInterceptionLayout.getLayoutParams(); - lp.height = (int) (-translationY + getScreenHeight()); - mInterceptionLayout.requestLayout(); - } } @Override From 95680deb8386772caecce66c2d0288eec25e5c45 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 1 May 2015 11:31:56 +0900 Subject: [PATCH 055/208] Bumped version to 1.5.1. --- README.md | 5 ++++- gradle.properties | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b2fb0944..ef9c0913 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ repositories { } dependencies { - compile 'com.github.ksoichiro:android-observablescrollview:1.5.0' + compile 'com.github.ksoichiro:android-observablescrollview:1.5.1' } ``` @@ -157,6 +157,9 @@ This project is built and tested under the following environment. ## Release notes +* v1.5.1 + * Fix `scrollY` of `onScrollChanged` in `ObservableGridView` jumps + when the first visible item changes. * v1.5.0 * Add a helper class `CacheFragmentStatePagerAdapter` to implement ViewPager pattern. * Fix that swipe down (over-scroll) causes item click. diff --git a/gradle.properties b/gradle.properties index 5fab10e0..e2432c6e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.5.1-SNAPSHOT +VERSION_NAME=1.5.1 SYNCED_VERSION_NAME=1.5.0 GROUP=com.github.ksoichiro From 25b30d0c736ee7ea572d2c1283ab60b4caa49e02 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 1 May 2015 13:33:00 +0900 Subject: [PATCH 056/208] Updated synced version of the library. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e2432c6e..947ae31c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ VERSION_NAME=1.5.1 -SYNCED_VERSION_NAME=1.5.0 +SYNCED_VERSION_NAME=1.5.1 GROUP=com.github.ksoichiro POM_DESCRIPTION=Android library to observe scroll events on scrollable views. From 5bc75ceb5bc27a876e44dcdc886f57c1f6ab1df2 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 1 May 2015 13:33:48 +0900 Subject: [PATCH 057/208] Updated version of Android Gradle plugin and build tools. --- .travis.yml | 1 - library/build.gradle | 4 ++-- samples/build.gradle | 2 +- wercker.yml | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index b5270ad5..e9fb0398 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: android android: components: - - build-tools-21.1.1 - build-tools-22.0.1 - tools - android-19 diff --git a/library/build.gradle b/library/build.gradle index 20f59fb2..a32d2230 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -4,7 +4,7 @@ buildscript { } dependencies { classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.1.0' - classpath 'com.android.tools.build:gradle:1.0.0' + classpath 'com.android.tools.build:gradle:1.2.2' } } @@ -26,7 +26,7 @@ dependencies { android { compileSdkVersion 21 - buildToolsVersion "21.1.1" + buildToolsVersion "22.0.1" defaultConfig { minSdkVersion 9 diff --git a/samples/build.gradle b/samples/build.gradle index c567e594..3aefb1e7 100644 --- a/samples/build.gradle +++ b/samples/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.1.3' + classpath 'com.android.tools.build:gradle:1.2.2' } } diff --git a/wercker.yml b/wercker.yml index 7765eb2d..e89770a0 100644 --- a/wercker.yml +++ b/wercker.yml @@ -13,7 +13,7 @@ build: echo $ANDROID_UPDATE_FILTER echo $ANDROID_NDK_HOME - android-sdk-update: - filter: tools,platform-tools,android-21,build-tools-21.1.1,extra-android-support,extra-android-m2repository + filter: tools,platform-tools,android-21,android-22,build-tools-22.0.1,extra-android-support,extra-android-m2repository # A step that executes `gradle build` command - script: name: run gradle From 49db40502b75930b060c4b4087a50398dca27e5b Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 1 May 2015 17:13:55 +0900 Subject: [PATCH 058/208] Modified styles of Toolbar of MainActivity. --- .../res/drawable-xxxhdpi/ic_action_info.png | Bin 0 -> 1391 bytes samples/res/layout/activity_main.xml | 34 +++++++++++++----- samples/res/menu/menu_main.xml | 1 + samples/res/values/styles.xml | 16 +++++++++ .../samples/MainActivity.java | 3 +- 5 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 samples/res/drawable-xxxhdpi/ic_action_info.png diff --git a/samples/res/drawable-xxxhdpi/ic_action_info.png b/samples/res/drawable-xxxhdpi/ic_action_info.png new file mode 100644 index 0000000000000000000000000000000000000000..fed22b87dca9fc8857f812ad73e54872fdacf0f1 GIT binary patch literal 1391 zcmV-#1(5oQP)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11nfyfK~#90?VZ1G6h{=tKZlM#z!HjN1gTOag->!2P}&4V3Jq}q zqM(I14K;L#B2uVQ;36bxh)6{Wha!%IjDZdiL?V&m!c{;-ije#V7;!1yokAR+cV_3u zo85gM>Ex3eci#NkebXBbhZuV7(Kev#m;lO{0LqvE%9sGkm;lO{0Lt{W4X9uW zm;v^WKGSb~AAbN|Zu|W8*7x6l&8${?&?|5tb`ba!SODe-ze!#L_km^LC*XPD@uE_S z05gcg7l02FpX&)C#xn4G;&WA^DL@Z64O{`hMoXB*}I-Edkyn)!9oE#x&4)_t+ zm-}<@J2cPvAU5AYt|1wz1n2|TfUkkQiO!LOy~s6OBhjEWMF^Tg zx^>56A6S`Hq}v>IR2^G@_kfkr-!`&=G}Ifh4j5B_Dc}+9b^e$Q;0TdwLaa8RkA$7J zJ0Ij4ZxN|Yh$X;Rv@hoG#&P7DgNYG>7LlGp7x^5x9&&(C0p^gta?Y;i5cm$bjm&HU z90E=P=a5OhTxc9Ihm+zY`lEr(+12?qm_@y5Q(w(D$30xZB)(y*>(oV17Vf; z)PRM0wnU?c46^2TDKkGRqv?F`@Rd&uctZMzrd%j#Dq<2(acqZN1vm)&8Ddcfc^8Mr zAYDU{cKB%`PChdM(&6MYmk=}s{7w0fvX{nd;2kQKE?sKC4Ceeg^bz0fii-d_=hoXs zrSD9Dv_uhLI)WixrP6mUvAwh!P&Lph;+3Bf7J`&90hBQTl!+pMkq{RFG}czzLZ$Ca zfV7wZ%9sGkL=nLCSsdg4RQk?pKw2yWDO2gYCN9V{WiBY%g3KoHAcjR9;{gsY^0|(L zm&0u7Bu+kS;$taI2|-aGOL>kwY-G6c6vxzPK9g}4hTL)Xl?jjrXJ7fgA@c`%`Q65p z-Uh4#AGj9wT@SeIS-y*S`N+2h^ni!x=6fr-20R9i`q({(QiS6)jU@uJWkI~GiGAP~x|26<75D^M z^$Nn$R{B$9+DZ_Eq9!#hka6PHXxVdP52VI-$7e;1_z zl3lRWePmXEOJqz;W!qQ@o!Q1}%o?(EcLrI+)?$5R!%-BR54M$Ir?)bkgl* - + android:layout_height="match_parent" + android:orientation="horizontal"> + + + + - + android:layout_height="match_parent"> + + \ No newline at end of file diff --git a/samples/res/menu/menu_main.xml b/samples/res/menu/menu_main.xml index 1a525fde..bf2b06e9 100644 --- a/samples/res/menu/menu_main.xml +++ b/samples/res/menu/menu_main.xml @@ -22,6 +22,7 @@ android:id="@+id/menu_about" android:orderInCategory="100" android:title="@string/menu_about" + android:icon="@drawable/ic_action_info" app:showAsAction="ifRoom" /> diff --git a/samples/res/values/styles.xml b/samples/res/values/styles.xml index 4c1781a1..5e90b566 100644 --- a/samples/res/values/styles.xml +++ b/samples/res/values/styles.xml @@ -25,6 +25,9 @@ @color/primary @color/primaryDark @color/accent + + @style/AppTheme.SpinnerItemStyle + @style/AppTheme.SpinnerDropDownItemStyle + + + + + + diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java index 85f3f9c7..84408d61 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java @@ -197,7 +197,8 @@ public void onItemClick(AdapterView parent, View view, int position, long id) private class FilterAdapter extends ArrayAdapter { public FilterAdapter(Context context) { - super(context, android.R.layout.simple_list_item_1, Filter.values()); + super(context, android.R.layout.simple_spinner_item, Filter.values()); + setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); } } } \ No newline at end of file From 4d6c1e65a8339818957d577f65459c22e0bcdfe5 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 3 May 2015 14:49:24 +0900 Subject: [PATCH 059/208] Added website's basic structure. --- .gitignore | 1 + .travis-script.sh | 19 ++++ .travis.yml | 47 +++++---- README.md | 64 ++++++------- docs/_data.json | 8 ++ docs/_layout.ejs | 34 +++++++ docs/advanced/_data.json | 8 ++ docs/basic/_data.json | 20 ++++ docs/example/_data.json | 14 +++ docs/{ => example}/eclipse.md | 0 docs/{ => example}/wercker.md | 8 +- docs/faq.md | 6 +- docs/overview.md | 3 + docs/quick-start/_data.json | 11 +++ website/.bowerrc | 3 + website/.gitignore | 7 ++ website/bower.json | 32 +++++++ website/gulpfile.js | 153 +++++++++++++++++++++++++++++ website/harp.json | 13 +++ website/package.json | 27 ++++++ website/public/404.ejs | 2 + website/public/CONTRIBUTING.ejs | 3 + website/public/_data.json | 14 +++ website/public/_footer.ejs | 16 ++++ website/public/_head.ejs | 9 ++ website/public/_layout.ejs | 20 ++++ website/public/_nav.ejs | 20 ++++ website/public/css/main.less | 164 ++++++++++++++++++++++++++++++++ website/public/index.ejs | 3 + website/public/js/main.coffee | 12 +++ 30 files changed, 683 insertions(+), 58 deletions(-) create mode 100644 .travis-script.sh create mode 100644 docs/_data.json create mode 100644 docs/_layout.ejs create mode 100644 docs/advanced/_data.json create mode 100644 docs/basic/_data.json create mode 100644 docs/example/_data.json rename docs/{ => example}/eclipse.md (100%) rename docs/{ => example}/wercker.md (89%) create mode 100644 docs/overview.md create mode 100644 docs/quick-start/_data.json create mode 100644 website/.bowerrc create mode 100644 website/.gitignore create mode 100644 website/bower.json create mode 100644 website/gulpfile.js create mode 100644 website/harp.json create mode 100644 website/package.json create mode 100644 website/public/404.ejs create mode 100644 website/public/CONTRIBUTING.ejs create mode 100644 website/public/_data.json create mode 100644 website/public/_footer.ejs create mode 100644 website/public/_head.ejs create mode 100644 website/public/_layout.ejs create mode 100644 website/public/_nav.ejs create mode 100644 website/public/css/main.less create mode 100644 website/public/index.ejs create mode 100644 website/public/js/main.coffee diff --git a/.gitignore b/.gitignore index a1383ac2..cb4636b6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ aarDependencies/ .project .classpath project.properties +*.swp diff --git a/.travis-script.sh b/.travis-script.sh new file mode 100644 index 00000000..03005a81 --- /dev/null +++ b/.travis-script.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +echo "TEST_TARGET=${TEST_TARGET}" +echo "TRAVIS_PULL_REQUEST=${TRAVIS_PULL_REQUEST}" +echo "TRAVIS_BRANCH=${TRAVIS_BRANCH}" + +if [ "$TEST_TARGET" = "android" ]; then + # Release build type is only for Google Play store currently, + # which resolve dependency from Maven Central. + # This causes build errors while developing a new feature, so disable release build. + ./gradlew --full-stacktrace assembleDevDebug connectedCheck +elif [ "$TEST_TARGET" = "website" ]; then + if [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ ! -z "$GH_TOKEN" ]; then + echo "Update website..." + pushd website > /dev/null 2>&1 + npm run deploy + popd > /dev/null 2>&1 + fi +fi diff --git a/.travis.yml b/.travis.yml index e9fb0398..4c3a6117 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,33 @@ language: android -android: - components: - - build-tools-22.0.1 - - tools - - android-19 - - android-21 - - android-22 - - sys-img-armeabi-v7a-android-19 - - extra-android-support - - extra-android-m2repository - licenses: - - 'android-sdk-license-.+' +env: + global: + - GIT_COMMITTER_NAME=ksoichiro + - GIT_COMMITTER_EMAIL=soichiro.kashima@gmail.com + - GIT_AUTHOR_NAME=ksoichiro + - GIT_AUTHOR_EMAIL=soichiro.kashima@gmail.com + - secure: JvSTH/5Jl7x3IwCvErTKoW6AjGr03MLwmJwiRQ8sDeEITeLS45EJCnpzvOZXIaxY98Uhl9WqYLYCkUWPkM6yqFEsKUEXKYFLkDoec3ek8GKUkiGWSsV5jCEkVBx8RqLWGbpSg7U/0Ua2doZqIYkBEJ1t5wKiANrt+q/04+jpXWs= + matrix: + - TEST_TARGET=android + - TEST_TARGET=website +cache: + directories: + - website/node_modules + - website/bower_components +install: + - true && ([ "$TEST_TARGET" != "website" ] || (cd website && npm install && cd ..)) + - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=build-tools-22.0.1) + - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=tools) + - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-19) + - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-21) + - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-22) + - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=sys-img-armeabi-v7a-android-19) + - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=extra-android-support) + - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=extra-android-m2repository) before_script: - - echo no | android create avd --force -n test -t android-19 --abi default/armeabi-v7a - - emulator -avd test -no-skin -no-audio -no-window & - - android-wait-for-emulator + - true && ([ "$TEST_TARGET" != "android" ] || (echo no | android create avd --force -n test -t android-19 --abi default/armeabi-v7a)) + - true && ([ "$TEST_TARGET" != "android" ] || emulator -avd test -no-skin -no-audio -no-window &) + - true && ([ "$TEST_TARGET" != "android" ] || android-wait-for-emulator) script: - # Release build type is only for Google Play store currently, - # which resolve dependency from Maven Central. - # This causes build errors while developing a new feature, so disable release build. - - travis_retry ./gradlew --full-stacktrace assembleDevDebug connectedCheck + - "/bin/bash .travis-script.sh" after_success: - ./gradlew :library:coveralls diff --git a/README.md b/README.md index ef9c0913..41f5e76d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -Android-ObservableScrollView -=== +# Android-ObservableScrollView [![Build Status](http://img.shields.io/travis/ksoichiro/Android-ObservableScrollView.svg?style=flat)](https://travis-ci.org/ksoichiro/Android-ObservableScrollView) [![Coverage Status](https://img.shields.io/coveralls/ksoichiro/Android-ObservableScrollView/master.svg?style=flat)](https://coveralls.io/r/ksoichiro/Android-ObservableScrollView?branch=master) @@ -10,22 +9,22 @@ Android-ObservableScrollView Android library to observe scroll events on scrollable views. It's easy to interact with the Toolbar introduced in Android 5.0 Lollipop and may be helpful to implement look and feel of Material Design apps. -![](samples/images/demo12.gif) -![](samples/images/demo10.gif) -![](samples/images/demo11.gif) -![](samples/images/demo13.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo12.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo10.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo11.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo13.gif) -![](samples/images/demo1.gif) -![](samples/images/demo2.gif) -![](samples/images/demo3.gif) -![](samples/images/demo4.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo1.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo2.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo3.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo4.gif) -![](samples/images/demo5.gif) -![](samples/images/demo6.gif) -![](samples/images/demo7.gif) -![](samples/images/demo8.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo5.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo6.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo7.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo8.gif) -![](samples/images/demo9.gif) +![](https://raw.githubusercontent.com/ksoichiro/Android-ObservableScrollView/master/samples/images/demo9.gif) ## Samples @@ -42,7 +41,7 @@ If you'd like to install the latest one, install it manually with Gradle. ### wercker If you are a wercker user, you can download the latest build artifact. -[See here for details](https://github.com/ksoichiro/Android-ObservableScrollView/tree/master/docs/wercker.md). +[See here for details](docs/example/wercker.md). [![wercker status](https://app.wercker.com/status/8d1e27d9f4a662b25dbe70402733582b/m/master "wercker status")](https://app.wercker.com/project/bykey/8d1e27d9f4a662b25dbe70402733582b) @@ -87,7 +86,7 @@ dependencies { ``` Basically this project supports Android Studio. -If you'd like to use Eclipse, please [see here](https://github.com/ksoichiro/Android-ObservableScrollView/tree/master/docs/eclipse.md). +If you'd like to use Eclipse, please [see here](docs/example/eclipse.md). It's partially supported. ### Add widgets to your layout @@ -140,11 +139,11 @@ You can handle these widgets with `Scrollable` interface. | Widget | Since | Note | |:------:|:-----:| ---- | -| ListView | v1.0.0 | | -| ScrollView | v1.0.0 | | -| WebView | v1.0.0 | | +| ListView | v1.0.0 | - | +| ScrollView | v1.0.0 | - | +| WebView | v1.0.0 | - | | RecyclerView | v1.1.0 | It's supported but RecyclerView provides scroll states and position with [OnScrollListener](https://developer.android.com/reference/android/support/v7/widget/RecyclerView.OnScrollListener.html). You should use it if you don't have any reason. | -| GridView | v1.2.0 | | +| GridView | v1.2.0 | - | ## Environment @@ -219,17 +218,18 @@ Please check the [FAQ](docs/faq.md) and [contributing guideline](https://github. ## License - Copyright 2014 Soichiro Kashima +```license +Copyright 2014 Soichiro Kashima - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/docs/_data.json b/docs/_data.json new file mode 100644 index 00000000..355808da --- /dev/null +++ b/docs/_data.json @@ -0,0 +1,8 @@ +{ + "overview": { + "title": "Overview" + }, + "faq": { + "title": "FAQ" + } +} diff --git a/docs/_layout.ejs b/docs/_layout.ejs new file mode 100644 index 00000000..2af19b85 --- /dev/null +++ b/docs/_layout.ejs @@ -0,0 +1,34 @@ + + + +<%- partial("../_head", { title: title }) %> + + + +<%- partial("../_nav") %> + +

+
+ +
+<%- yield %> +
+
+
+ +<%- partial("../_footer") %> + + diff --git a/docs/advanced/_data.json b/docs/advanced/_data.json new file mode 100644 index 00000000..514d00f9 --- /dev/null +++ b/docs/advanced/_data.json @@ -0,0 +1,8 @@ +{ + "sliding-up": { + "title": "Sliding up pattern" + }, + "viewpager": { + "title": "ViewPager pattern" + } +} diff --git a/docs/basic/_data.json b/docs/basic/_data.json new file mode 100644 index 00000000..9dddf927 --- /dev/null +++ b/docs/basic/_data.json @@ -0,0 +1,20 @@ +{ + "show-hide-action-bar": { + "title": "Show and hide the Action Bar" + }, + "translating-toolbar": { + "title": "Translating Toolbar" + }, + "translating-scrollable": { + "title": "Translating scrollable views" + }, + "parallax-image": { + "title": "Parallax image" + }, + "sticky-header": { + "title": "Sticky header" + }, + "filling-gap": { + "title": "Filling gap on top of the Toolbar" + } +} diff --git a/docs/example/_data.json b/docs/example/_data.json new file mode 100644 index 00000000..35001c75 --- /dev/null +++ b/docs/example/_data.json @@ -0,0 +1,14 @@ +{ + "google-play": { + "title": "Google Play store" + }, + "wercker": { + "title": "Download from wercker" + }, + "android-studio": { + "title": "Setting up Android Studio" + }, + "eclipse": { + "title": "Setting up Eclipse" + } +} diff --git a/docs/eclipse.md b/docs/example/eclipse.md similarity index 100% rename from docs/eclipse.md rename to docs/example/eclipse.md diff --git a/docs/wercker.md b/docs/example/wercker.md similarity index 89% rename from docs/wercker.md rename to docs/example/wercker.md index 05698fbd..860468cc 100644 --- a/docs/wercker.md +++ b/docs/example/wercker.md @@ -1,4 +1,4 @@ -# Download the sample app from wercker +# Download from wercker [wercker](http://wercker.com/) is a CI service and this project uses wercker to provide the latest sample apk. @@ -18,16 +18,16 @@ Then select the commit link that you want to download. Note that green check mark in front of the link means successful builds and red ones are failure, and you can only download the app from the green ones. -![](images/wercker_1.png) +![](../images/wercker_1.png) ## Open the last section Scroll the screen, and click anywhere in the "inspect build result" section to open it. -![](images/wercker_2.png) +![](../images/wercker_2.png) ## Download the artifact Finally, you can download the apk file by clicking the `artifact.tar.gz` link. -![](images/wercker_3.png) +![](../images/wercker_3.png) diff --git a/docs/faq.md b/docs/faq.md index 839212f1..038b2359 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,4 +1,4 @@ -# Frequently asked questions +# FAQ These are frequently asked questions from GitHub issues, emails I received from users, etc. @@ -15,13 +15,13 @@ If you're interested in implementing new samples or fixing bugs, please help me. ### A. Yes, it does partially. -Please see [here](eclipse.md) for details. +Please see [here](../docs/example/eclipse.md) for details. ## Q. Doesn't work! ### A. Please describe your issue as much as possible. -As I wrote in [the contribution guideline](../CONTRIBUTING.md), +As I wrote in [the contribution guideline](https://github.com/ksoichiro/Android-ObservableScrollView/blob/master/CONTRIBUTING.md), the library itself only provides the scroll information, and creating awesome scrolling effects depends deeply on how you use it: layout, offset calculation to animate views, etc. diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 00000000..94692d74 --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,3 @@ +# Overview + + diff --git a/docs/quick-start/_data.json b/docs/quick-start/_data.json new file mode 100644 index 00000000..7da0eae8 --- /dev/null +++ b/docs/quick-start/_data.json @@ -0,0 +1,11 @@ +{ + "dependencies": { + "title": "Dependencies" + }, + "layout": { + "title": "Layout" + }, + "animation": { + "title": "Animation codes" + } +} diff --git a/website/.bowerrc b/website/.bowerrc new file mode 100644 index 00000000..69fad358 --- /dev/null +++ b/website/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "bower_components" +} diff --git a/website/.gitignore b/website/.gitignore new file mode 100644 index 00000000..46aaef49 --- /dev/null +++ b/website/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +bower_components/ +public/lib/ +public/docs/ +www/ +repo/ +*.log diff --git a/website/bower.json b/website/bower.json new file mode 100644 index 00000000..eee2a270 --- /dev/null +++ b/website/bower.json @@ -0,0 +1,32 @@ +{ + "name": "Android-ObservableScrollView", + "description": "bower components for Android-ObservableScrollView", + "dependencies": { + "jquery": "1.11.2", + "bootstrap": "3.3.4", + "components-font-awesome": "4.3.0", + "highlightjs": "8.5.0", + "respond-minmax": "1.4.2", + "html5shiv": "3.7.2" + }, + "overrides": { + "jquery": { + "main": "dist/*.min.*" + }, + "bootstrap": { + "main": ["dist/css/*.min.css", "dist/js/*.min.js", "dist/fonts/*"] + }, + "components-font-awesome": { + "main": ["css/*.min.css", "fonts/*"] + }, + "highlightjs": { + "main": ["*.js", "**/*.css"] + }, + "respond-minmax": { + "main": "dest/*.min.js" + }, + "html5shiv": { + "main": "dist/*.min.js" + } + } +} diff --git a/website/gulpfile.js b/website/gulpfile.js new file mode 100644 index 00000000..981f0ae0 --- /dev/null +++ b/website/gulpfile.js @@ -0,0 +1,153 @@ +var gulp = require('gulp'); +var mainBowerFiles = require('main-bower-files'); +var git = require('gulp-git'); +var harp = require('harp'); +var del = require('del'); +var gutil = require('gulp-util'); + +var ghToken = process.env.GH_TOKEN; + +// You should replace this configs to your project's values. +var project = { + githubRepoOwner: 'ksoichiro', + name: 'Android-ObservableScrollView', +}; + +if (process.env.GH_TOKEN) { + // Travis CI + var gitBaseUrl = 'github.com/' + project.githubRepoOwner + '/' + project.name + '.git'; + project['gitUrl'] = 'https://' + gitBaseUrl; + project['gitPushUrl'] = 'https://' + ghToken + '@' + gitBaseUrl; +} else { + // Local + var gitBaseUrl = 'github.com:' + project.githubRepoOwner + '/' + project.name + '.git'; + project['gitUrl'] = 'git@' + gitBaseUrl; + project['gitPushUrl'] = 'origin'; +} + +var paths = { + bower: "./bower_components", + harp: { + project: ".", + output: "./www" + }, + docs: "../docs", + dest: { + root: "./www", + lib: "./public/lib", + docs: "./public/docs" + }, + repo: "repo" +}; + +var port = 9000; + +gulp.task('clean', function(cb) { + del([paths.dest.root, paths.dest.lib, paths.dest.docs, paths.repo], cb); +}); + +gulp.task('build', ['copy'], function(cb) { + // This task is for production, so BASE_URL should be a project name. + // $BASE_URL is referenced in harp.json, and it will be replaced by harp (envy). + process.env.BASE_URL = '/' + project.name + harp.compile(paths.harp.project, paths.harp.output, function(err) { + if (err) { + gutil.log('build failed: ' + err); + } + gutil.log('Compile done'); + cb(); + }); +}); + +gulp.task('copyDocs', function() { + return gulp.src(paths.docs + '/**/*') + .pipe(gulp.dest(paths.dest.docs)); +}); + +gulp.task('copyBowerFiles', function() { + // Return streams so that the dependent tasks can detect when it has done. + return gulp.src(mainBowerFiles(), { base: paths.bower }) + .pipe(gulp.dest(paths.dest.lib)); +}); + +gulp.task('copy', ['copyDocs', 'copyBowerFiles'], function() { +}); + +gulp.task('remove-repo', ['build'], function(cb) { + // Remove old directory, if exists. + gutil.log('removing repo..'); + del([paths.repo], cb); +}); + +gulp.task('git-clone', ['remove-repo'], function(cb) { + // Clone to build directory, commit files to gh-pages branch, and push it. + git.clone(project.gitUrl, {args: paths.repo}, function(err) { + if (err) { + gutil.log('Failed to clone: ' + err); + } else { + gutil.log('clone done'); + } + cb(); + }); +}); + +gulp.task('deploy', ['git-clone'], function(cb) { + gutil.log('check out gh-pages...'); + git.checkout('origin/gh-pages', {args: '-b gh-pages', cwd: paths.repo}, function(err) { + if (err) { + gutil.log('Failed to check out branch: ' + err); + throw err; + } else { + gutil.log('copying files...'); + gulp.src(paths.harp.output + '/**') + .pipe(gulp.dest(paths.repo)) + .on('finish', function() { + gutil.log('finished to copy'); + + git.exec({args: 'diff-index HEAD --', cwd: paths.repo}, function (err, stdout) { + if (err) { + gutil.log('Failed to check diff: ' + err); + throw err; + } else if (stdout) { + // There are some changes for gh-pages + gutil.log('There are some changes to commit.'); + gulp.src(paths.repo) + .pipe(git.add({args: '-A', cwd: paths.repo})) + .pipe(git.commit('Updated website.', {cwd: paths.repo})) + .on('end', function() { + gutil.log('finish'); + gutil.log('git push ' + project.gitPushUrl + ' gh-pages --quiet'); + git.push(project.gitPushUrl, 'gh-pages', {args: '--quiet', /*quiet: true, */cwd: paths.repo}, function(err) { + if (err) { + gutil.log('Failed to push: ' + err); + throw err; + } else { + gutil.log('Pushed successfully.'); + } + cb(); + }); + }); + } else { + gutil.log('Nothing to commit.'); + cb(); + } + }); + }); + } + }); +}); + +gulp.task('start', ['copy'], function() { + // This task is for development locally, so BASE_URL should be empty. + // $BASE_URL is referenced in harp.json, and it will be replaced by harp (envy). + process.env.BASE_URL = '' + harp.server(paths.harp.project, { port: port }, function(err) { + if (err) { + gutil.log('Failed to start'); + gutil.log(err); + } else { + gutil.log('Started server: http://localhost:' + port); + gutil.log('Press Ctrl+C to quit'); + } + }); +}); diff --git a/website/harp.json b/website/harp.json new file mode 100644 index 00000000..7ae9c6ef --- /dev/null +++ b/website/harp.json @@ -0,0 +1,13 @@ +{ + "globals": { + "baseUrl": "$BASE_URL", + "title": "Android-ObservableScrollView", + "name": "Android-ObservableScrollView", + "description": "Android library to observe scroll events on scrollable views.", + "githubUser": "ksoichiro", + "githubRepo": "Android-ObservableScrollView", + "githubUrl": "https://github.com/ksoichiro/Android-ObservableScrollView", + "copyrightYear": "2014", + "copyrightHolder": "Soichiro Kashima" + } +} diff --git a/website/package.json b/website/package.json new file mode 100644 index 00000000..820c9fe5 --- /dev/null +++ b/website/package.json @@ -0,0 +1,27 @@ +{ + "name": "Android-ObservableScrollView", + "description": "Web site for Android-ObservableScrollView", + "repository": { + "type": "git", + "url": "https://github.com/ksoichiro/Android-ObservableScrollView.git" + }, + "dependencies": { + }, + "devDependencies": { + "bower": "1.3.12", + "gulp": "3.8.11", + "del": "1.1.1", + "harp": "0.17.0", + "main-bower-files": "2.7.0", + "gulp-git": "1.2.1", + "gulp-util": "3.0.4" + }, + "scripts": { + "prepublish": "bower install --config.interactive=false", + "start": "gulp start", + "copy": "gulp copy", + "build": "gulp build", + "deploy": "gulp deploy", + "clean": "gulp clean" + } +} diff --git a/website/public/404.ejs b/website/public/404.ejs new file mode 100644 index 00000000..9365e9b5 --- /dev/null +++ b/website/public/404.ejs @@ -0,0 +1,2 @@ +

404

+

Page Not Found

diff --git a/website/public/CONTRIBUTING.ejs b/website/public/CONTRIBUTING.ejs new file mode 100644 index 00000000..b23e9637 --- /dev/null +++ b/website/public/CONTRIBUTING.ejs @@ -0,0 +1,3 @@ +
+<%- partial("../../CONTRIBUTING") %> +
diff --git a/website/public/_data.json b/website/public/_data.json new file mode 100644 index 00000000..b678da98 --- /dev/null +++ b/website/public/_data.json @@ -0,0 +1,14 @@ +{ + "quick-start": { + "title": "Quick start" + }, + "example": { + "title": "Try the example app" + }, + "basic": { + "title": "Basic techniques" + }, + "advanced": { + "title": "Advanced techniques" + } +} diff --git a/website/public/_footer.ejs b/website/public/_footer.ejs new file mode 100644 index 00000000..bb47b70c --- /dev/null +++ b/website/public/_footer.ejs @@ -0,0 +1,16 @@ + + + + + + + diff --git a/website/public/_head.ejs b/website/public/_head.ejs new file mode 100644 index 00000000..7685b0ee --- /dev/null +++ b/website/public/_head.ejs @@ -0,0 +1,9 @@ + + + +<%- title %> | <%- description %> + + + + + diff --git a/website/public/_layout.ejs b/website/public/_layout.ejs new file mode 100644 index 00000000..ad1bc928 --- /dev/null +++ b/website/public/_layout.ejs @@ -0,0 +1,20 @@ + + + +<%- partial("_head") %> + + + +<%- partial("_nav") %> + +
+
+
+<%- yield %> +
+
+
+ +<%- partial("_footer") %> + + diff --git a/website/public/_nav.ejs b/website/public/_nav.ejs new file mode 100644 index 00000000..411c7cf5 --- /dev/null +++ b/website/public/_nav.ejs @@ -0,0 +1,20 @@ + diff --git a/website/public/css/main.less b/website/public/css/main.less new file mode 100644 index 00000000..60eb2e40 --- /dev/null +++ b/website/public/css/main.less @@ -0,0 +1,164 @@ +@import (reference) '../../bower_components/bootstrap/less/bootstrap.less'; + +// Overwrite bootstrap's default +.override-container(@width: 970px) { + @media (min-width: 1200px) { + & { + width: @width; + } + } +} +.container { + .override-container; +} + +// Fonts +body, .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6, p { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; +} +body, p { + font-size: 14px; +} +h1 { + font-size: 26px; +} +h2 { + font-size: 22px; +} +h3 { + font-size: 20px; +} + +// Colors +@theme-main-color: #009688; // 500 +@theme-border-color: #00897B; // 600 +@theme-brand-color: #E0F2F1; // 50 +@theme-focus-color: #00695C; // 800 +@theme-active-color: #00695C; // 800 +.navbar-inverse { + background-color: @theme-main-color; + border-color: @theme-border-color; + .navbar-brand, + .navbar-nav>li>a { + color: @theme-brand-color; + } + .navbar-nav>.active>a, + .navbar-nav>.active>a:focus, + .navbar-nav>.active>a:hover { + background-color: @theme-active-color; + } + .navbar-nav>.open>a, + .navbar-nav>.open>a:focus, + .navbar-nav>.open>a:hover { + background-color: @theme-focus-color; + } + .navbar-toggle { + background-color: @theme-main-color; + } + .navbar-toggle { + border-color: @theme-focus-color; + &:hover, + &:focus { + background-color: @theme-border-color; + } + } + .navbar-collapse { + border-color: @theme-border-color; + } +} +code { + color: #009688; // Teal 500 + background-color: lighten(#E0F2F1, 7%); // Teal 50 +} + +// Markdown fix +.md { + // Apply .table to all s + table { + .table; + } +} + +// Navbar +nav.navbar { + @shadow-h-offset: 0px; + @shadow-v-offset: -3px; + @shadow-blur-radius: 6px; + @shadow-spread-radius: 6px; + @shadow-color: #ccc; + -moz-box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; + -webkit-box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; + box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; +} + +// Footer +.footer { + .container; + .override-container; + padding: 15px; + border-top: 1px solid lighten(#E0F2F1, 7%); + .btns { + text-align: right; + padding-bottom: 15px; + .ghstar { + width: 100px; + height: 20px; + } + .ghfork { + width: 100px; + height: 20px; + } + } + .copyright { + .text-muted; + .clearfix; + text-align: right; + font-size: 12px; + line-height: 150%; + } +} + +// Page without sidebar +#content { +} + +// Page with sidebar +#grid-content { + .make-row(); + #sidebar { + .make-xs-column(6); + .make-sm-column(3); + + ul { + padding-left: 0px; + } + &>ul { + border-right: 1px solid #E0E0E0; + padding-right: 15px; + li { + position: relative; + display: block; + } + &>li { + padding: 8px; + } + .section { + font-size: 14px; + font-weight: 400; + margin: 0px; + margin-bottom: 8px; + color: #009688; + } + .topic { + &>a { + color: #212121; + } + } + } + } + #sidebar-main-content { + .make-xs-column(12); + .make-sm-column(9); + } +} diff --git a/website/public/index.ejs b/website/public/index.ejs new file mode 100644 index 00000000..e17b67b8 --- /dev/null +++ b/website/public/index.ejs @@ -0,0 +1,3 @@ +
+<%- partial('../../README') %> +
diff --git a/website/public/js/main.coffee b/website/public/js/main.coffee new file mode 100644 index 00000000..ecd5e184 --- /dev/null +++ b/website/public/js/main.coffee @@ -0,0 +1,12 @@ +# Disable highlight for license quotation +$('.language-license').addClass 'nohighlight' + +# Remove .md, except external links +$("a[href$='.md']").not("[href^='http']").each -> + @.href = @.href.replace /\.md$/, "" + +# Insert subdirectory for links +base = $("x-meta[name='base']").attr('value') +if base != "" + $("a[href$='.md']").not("[href^='http']").each -> + @.href = @.href.replace 'docs/', "#{base}/docs/" From 722a607d6a3b96be918b06b9601003fe2a737293 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 3 May 2015 16:20:34 +0900 Subject: [PATCH 060/208] Added logo. --- website/public/css/main.less | 6 ++++++ website/public/images/logo.png | Bin 0 -> 1078 bytes 2 files changed, 6 insertions(+) create mode 100644 website/public/images/logo.png diff --git a/website/public/css/main.less b/website/public/css/main.less index 60eb2e40..7b32baf9 100644 --- a/website/public/css/main.less +++ b/website/public/css/main.less @@ -90,6 +90,12 @@ nav.navbar { -moz-box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; -webkit-box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; + .navbar-brand { + background-image: url('../images/logo.png'); + background-repeat: no-repeat; + background-position: 4px center; + padding-left: 56px; + } } // Footer diff --git a/website/public/images/logo.png b/website/public/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6c9f489badc03abffe749c40308d84ccf1bd8475 GIT binary patch literal 1078 zcmV-61j+k}P)F6XU=)mkQ7{Td!6+C7 zBOCnx|9?IRlb{|9?!JC`pT9K2fD*fzsNr}Zl>ow|cmfO<85#aFGJ5fsXXy-x2ap^O z10;EZk%93aBO~JC&wl05;60HEW4gFL~=$Yd{AovTV058!b;NuFS0 zWc&|x{BNj7tb}Uwm1yb#bjP1~aQ6a?{ruC17o>QC5$plDgkCGbOU)6@^7vZ&kIIO$dxMLn&?G#VDvLgmC=1 z+={A;xs|mz91o!x8QJ9f8kwoqm`k^IMDhUBUu4Jc$|%ki<>8fyGqFsZ8XRMZ9s^9^ z7(h5)noq!6f|t+ta$a>44#$H60E8J7Cp1&X1IU0GSgOF1%ER~XKE3$-@exce!oW0r zR%o0JI60w~LU6~UnuF8vAP@h&UrZU;3TPlFg>{EM$`V>-2~ z`PrwBTd3uD8pipEpv&@zA=2GH~ zus1Q+*LC64m6SN1DjomFIc? zc>wH*gC{<6Ia+-MIzWhCj;E>zK;YYfmY`y~>!cPy!Q{4k#PwYSXh1=N{0KfOG>HxoP2ex_AHtz}-M#Pm0=( wr<(^r0Mrcx^`xljc)9~vH*gdn0e}Dl016by0^RUx#sB~S07*qoM6N<$f=q<%jsO4v literal 0 HcmV?d00001 From 88bccdec881857fa9e70613bff411f44ab0a5af2 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 3 May 2015 16:41:30 +0900 Subject: [PATCH 061/208] Added watch task to copy docs / bower_components. --- website/gulpfile.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/website/gulpfile.js b/website/gulpfile.js index 981f0ae0..a400c279 100644 --- a/website/gulpfile.js +++ b/website/gulpfile.js @@ -42,8 +42,16 @@ var paths = { var port = 9000; -gulp.task('clean', function(cb) { - del([paths.dest.root, paths.dest.lib, paths.dest.docs, paths.repo], cb); +gulp.task('cleanDocs', function(cb) { + del([paths.dest.docs], cb); +}); + +gulp.task('cleanBowerFiles', function(cb) { + del([paths.dest.lib], cb); +}); + +gulp.task('clean', ['cleanDocs', 'cleanBowerFiles'], function(cb) { + del([paths.dest.root, paths.repo], cb); }); gulp.task('build', ['copy'], function(cb) { @@ -59,12 +67,12 @@ gulp.task('build', ['copy'], function(cb) { }); }); -gulp.task('copyDocs', function() { +gulp.task('copyDocs', ['cleanDocs'], function() { return gulp.src(paths.docs + '/**/*') .pipe(gulp.dest(paths.dest.docs)); }); -gulp.task('copyBowerFiles', function() { +gulp.task('copyBowerFiles', ['cleanBowerFiles'], function() { // Return streams so that the dependent tasks can detect when it has done. return gulp.src(mainBowerFiles(), { base: paths.bower }) .pipe(gulp.dest(paths.dest.lib)); @@ -146,6 +154,8 @@ gulp.task('start', ['copy'], function() { gutil.log('Failed to start'); gutil.log(err); } else { + gulp.watch([paths.docs + '/**/*', '!' + paths.docs + '/**/*.sw*'], ['copyDocs']); + gulp.watch(paths.bower + '/**/*', ['copyBowerFiles']); gutil.log('Started server: http://localhost:' + port); gutil.log('Press Ctrl+C to quit'); } From 67cf29001fa6e03a61efad0ccd0d8f3fcd5b1c0e Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 3 May 2015 17:03:35 +0900 Subject: [PATCH 062/208] Created documentation pages (empty). --- docs/_layout.ejs | 1 + docs/advanced/sliding-up.md | 2 ++ docs/advanced/viewpager.md | 2 ++ docs/basic/_data.json | 6 +++--- docs/basic/filling-gap.md | 2 ++ docs/basic/parallax-image.md | 2 ++ docs/basic/show-hide-action-bar.md | 1 + docs/basic/sticky-header.md | 2 ++ docs/basic/translating-itself.md | 3 +++ docs/basic/translating-toolbar.md | 2 ++ docs/example/_data.json | 6 +++--- docs/example/android-studio.md | 2 ++ docs/example/eclipse.md | 4 ++-- docs/example/google-play.md | 2 ++ docs/quick-start/animation.md | 2 ++ docs/quick-start/dependencies.md | 16 ++++++++++++++++ docs/quick-start/layout.md | 2 ++ website/public/css/main.less | 10 +++++++++- 18 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 docs/advanced/sliding-up.md create mode 100644 docs/advanced/viewpager.md create mode 100644 docs/basic/filling-gap.md create mode 100644 docs/basic/parallax-image.md create mode 100644 docs/basic/show-hide-action-bar.md create mode 100644 docs/basic/sticky-header.md create mode 100644 docs/basic/translating-itself.md create mode 100644 docs/basic/translating-toolbar.md create mode 100644 docs/example/android-studio.md create mode 100644 docs/example/google-play.md create mode 100644 docs/quick-start/animation.md create mode 100644 docs/quick-start/dependencies.md create mode 100644 docs/quick-start/layout.md diff --git a/docs/_layout.ejs b/docs/_layout.ejs index 2af19b85..e5247e08 100644 --- a/docs/_layout.ejs +++ b/docs/_layout.ejs @@ -11,6 +11,7 @@
s From c4deb4015e373721a124f58363e698f47d60cef2 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 3 May 2015 21:59:28 +0900 Subject: [PATCH 063/208] Added index for each sections. --- docs/_layout.ejs | 6 ++++-- docs/advanced/_data.json | 3 +++ docs/advanced/index.md | 8 ++++++++ docs/basic/_data.json | 3 +++ docs/basic/index.md | 10 ++++++++++ docs/example/_data.json | 3 +++ docs/example/index.md | 9 +++++++++ docs/overview.md | 23 +++++++++++++++++++++++ docs/quick-start/_data.json | 3 +++ docs/quick-start/index.md | 8 ++++++++ 10 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 docs/advanced/index.md create mode 100644 docs/basic/index.md create mode 100644 docs/example/index.md create mode 100644 docs/quick-start/index.md diff --git a/docs/_layout.ejs b/docs/_layout.ejs index e5247e08..5ff744f1 100644 --- a/docs/_layout.ejs +++ b/docs/_layout.ejs @@ -14,10 +14,12 @@
  • <% for (var sectionSlug in public._data) { %>
  • -

    <%- public._data[sectionSlug].title %>

    +

    <%- public._data[sectionSlug].title %>

  • diff --git a/docs/advanced/_data.json b/docs/advanced/_data.json index 514d00f9..88118141 100644 --- a/docs/advanced/_data.json +++ b/docs/advanced/_data.json @@ -1,4 +1,7 @@ { + "index": { + "title": "Advanced techniques" + }, "sliding-up": { "title": "Sliding up pattern" }, diff --git a/docs/advanced/index.md b/docs/advanced/index.md new file mode 100644 index 00000000..cc599afb --- /dev/null +++ b/docs/advanced/index.md @@ -0,0 +1,8 @@ +# Advanced techniques + +This section describes advanced scrolling techniques. +When you've done this topic, you could be be an expert of handling scrolls! +I'd appreciate it if you could suggest new patterns or improvements of the library. + +1. [Sliding up pattern](../../docs/advanced/sliding-up.md) +1. [ViewPager pattern](../../docs/advanced/viewpager.md) diff --git a/docs/basic/_data.json b/docs/basic/_data.json index ed8e543f..89b06f43 100644 --- a/docs/basic/_data.json +++ b/docs/basic/_data.json @@ -1,4 +1,7 @@ { + "index": { + "title": "Basic techniques" + }, "show-hide-action-bar": { "title": "Show and hide the Action Bar" }, diff --git a/docs/basic/index.md b/docs/basic/index.md new file mode 100644 index 00000000..01a74bc3 --- /dev/null +++ b/docs/basic/index.md @@ -0,0 +1,10 @@ +# Basic techniques + +This section explains the basic scrolling techniques. + +1. [Show and hide the Action Bar](../../docs/basic/show-hide-action-bar.md) +1. [Translating the Toolbar](../../docs/basic/translating-toolbar.md) +1. [Translating itself](../../docs/basic/translating-itself.md) +1. [Parallax image](../../docs/basic/parallax-image.md) +1. [Sticky header](../../docs/basic/sticky-header.md) +1. [Filling gap on top of the Toolbar](../../docs/basic/filling-gap.md) diff --git a/docs/example/_data.json b/docs/example/_data.json index 9aa85d4f..0016d988 100644 --- a/docs/example/_data.json +++ b/docs/example/_data.json @@ -1,4 +1,7 @@ { + "index": { + "title": "Try the example app" + }, "google-play": { "title": "Download from Google Play" }, diff --git a/docs/example/index.md b/docs/example/index.md new file mode 100644 index 00000000..502d4205 --- /dev/null +++ b/docs/example/index.md @@ -0,0 +1,9 @@ +# Try the examples app + +To understand how it works, let's see the existing example app +and check if there are some patterns you want to implement. + +1. [Download from Google Play](../../docs/example/google-play.md) +1. [Download from wercker](../../docs/eaxmple/wercker.md) +1. [Build on Android Studio](../../docs/example/android-studio.md) +1. [Build on Eclipse](../../docs/example/eclipse.md) diff --git a/docs/overview.md b/docs/overview.md index 94692d74..9cb6e39d 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -1,3 +1,26 @@ # Overview +Android-ObservableScrollView is a library to handle scroll position for Android library, and contains lots of examples to demonstrate how this library works. +However, creating awesome scrolling effects depends deeply on how you use it: layout, offset calculation to animate views, etc. + +This documentation describes how to install this library and apply to your application. + +## Try quickly + +See [Quick start](../docs/quick-start/index.md) section first. + +## See the complete examples + +Download from Google Play or wercker for quick trial, +or you'd like to build it, see [Try the example app](../docs/example/index.md) section. + +## Learn from basics + +Okay, now you've setup environments, let's learn how to use it in your app. +See [Basic techniques](../docs/basic/index.md) section. + +## Challenge complex and awesome techniques + +If you'd like to create complex, awesome scrolling animation using ViewPager or something, +please check out [Advanced techniques](../docs/advanced/index.md) section. diff --git a/docs/quick-start/_data.json b/docs/quick-start/_data.json index 7da0eae8..2ef0780c 100644 --- a/docs/quick-start/_data.json +++ b/docs/quick-start/_data.json @@ -1,4 +1,7 @@ { + "index": { + "title": "Quick start" + }, "dependencies": { "title": "Dependencies" }, diff --git a/docs/quick-start/index.md b/docs/quick-start/index.md new file mode 100644 index 00000000..ca30df3b --- /dev/null +++ b/docs/quick-start/index.md @@ -0,0 +1,8 @@ +# Quicks start + +Thank you for having interest in this library! +In this section, I'll show some quick instructions for introducing this library into your app. + +1. [Dependencies](../../docs/quick-start/dependencies.md) +1. [Layout](../../docs/quick-start/layout.md) +1. [Animation codes](../../docs/quick-start/animation.md) From ab21a7700b597f5c9b98816cc30ec4b6dfd9c508 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 3 May 2015 22:42:06 +0900 Subject: [PATCH 064/208] Modified code highlighting. --- README.md | 26 ++++++++--------- website/bower.json | 2 +- website/public/CONTRIBUTING.ejs | 3 -- website/public/_head.ejs | 1 - website/public/css/code.less | 50 +++++++++++++++++++++++++++++++++ website/public/css/main.less | 2 ++ 6 files changed, 66 insertions(+), 18 deletions(-) delete mode 100644 website/public/CONTRIBUTING.ejs create mode 100644 website/public/css/code.less diff --git a/README.md b/README.md index 41f5e76d..6ce5a288 100644 --- a/README.md +++ b/README.md @@ -105,27 +105,27 @@ Widgets above provides callbacks with `ObservableScrollViewCallbacks` interface. You can listen scroll events of the widgets by using `setScrollViewCallbacks()`. ```java - ObservableListView listView = (ObservableListView) findViewById(R.id.list); - listView.setScrollViewCallbacks(this); +ObservableListView listView = (ObservableListView) findViewById(R.id.list); +listView.setScrollViewCallbacks(this); ``` Then implement your interaction codes to the callbacks. Example: ```java - @Override - public void onUpOrCancelMotionEvent(ScrollState scrollState) { - ActionBar ab = getSupportActionBar(); - if (scrollState == ScrollState.UP) { - if (ab.isShowing()) { - ab.hide(); - } - } else if (scrollState == ScrollState.DOWN) { - if (!ab.isShowing()) { - ab.show(); - } +@Override +public void onUpOrCancelMotionEvent(ScrollState scrollState) { + ActionBar ab = getSupportActionBar(); + if (scrollState == ScrollState.UP) { + if (ab.isShowing()) { + ab.hide(); + } + } else if (scrollState == ScrollState.DOWN) { + if (!ab.isShowing()) { + ab.show(); } } +} ``` See [sample app's Activity codes](https://github.com/ksoichiro/Android-ObservableScrollView/tree/master/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples) for more details. diff --git a/website/bower.json b/website/bower.json index eee2a270..42842a61 100644 --- a/website/bower.json +++ b/website/bower.json @@ -20,7 +20,7 @@ "main": ["css/*.min.css", "fonts/*"] }, "highlightjs": { - "main": ["*.js", "**/*.css"] + "main": ["*.js"] }, "respond-minmax": { "main": "dest/*.min.js" diff --git a/website/public/CONTRIBUTING.ejs b/website/public/CONTRIBUTING.ejs deleted file mode 100644 index b23e9637..00000000 --- a/website/public/CONTRIBUTING.ejs +++ /dev/null @@ -1,3 +0,0 @@ -
    -<%- partial("../../CONTRIBUTING") %> -
    diff --git a/website/public/_head.ejs b/website/public/_head.ejs index 7685b0ee..b9781860 100644 --- a/website/public/_head.ejs +++ b/website/public/_head.ejs @@ -4,6 +4,5 @@ <%- title %> | <%- description %> - diff --git a/website/public/css/code.less b/website/public/css/code.less new file mode 100644 index 00000000..4e152cf3 --- /dev/null +++ b/website/public/css/code.less @@ -0,0 +1,50 @@ +// Based on androidstudio.css +// made by Author: Pedro Oliveira + +@code-bg-color: #263238; // Blue Grey 900 +pre { + background: @code-bg-color; + border: 1px solid #CFD8DC; // Blue Grey 100 + border-width: 1px 4px; + border-radius: 0px; +} +.nohighlight, +.hljs { + color: #A9B7C6; + background: @code-bg-color; + display: block; + overflow-x: auto; + padding: 12px; + webkit-text-size-adjust: none; +} +.hljs-number { + color: #6897BB; +} + +.hljs-keyword, .hljs-deletion { + color: #CC7832; +} +.hljs-javadoc { + color: #629755; +} +.hljs-comment { + color: #808080; +} +.hljs-annotation { + color: #BBB529; +} +.hljs-string, .hljs-addition { + color: #6A8759; +} +.hljs-function .hljs-title, .hljs-change { + color: #FFC66D; +} +.hljs-tag .hljs-title, .hljs-doctype { + color: #E8BF6A; +} +.hljs-tag .hljs-attribute { + color: #BABABA; +} +.hljs-tag .hljs-value { + color: #A5C261; +} diff --git a/website/public/css/main.less b/website/public/css/main.less index 115025a2..56f7b74a 100644 --- a/website/public/css/main.less +++ b/website/public/css/main.less @@ -176,3 +176,5 @@ nav.navbar { .make-sm-column(9); } } + +@import 'code.less'; From 0e42bce0de767e7c417263c80ac100c885c94556 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 3 May 2015 22:45:04 +0900 Subject: [PATCH 065/208] Fixed section link color. --- website/public/css/main.less | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/website/public/css/main.less b/website/public/css/main.less index 56f7b74a..e7179716 100644 --- a/website/public/css/main.less +++ b/website/public/css/main.less @@ -162,7 +162,11 @@ nav.navbar { font-weight: 400; margin: 0px; margin-bottom: 8px; - color: #009688; + &, + &>a { + color: #009688; + text-decoration: none; + } } .topic { &>a { From 94f08f75d7db33c3580869e7f15700058cc651a5 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 3 May 2015 23:02:33 +0900 Subject: [PATCH 066/208] Added contribution docs index (mainly for me). --- docs/contributor/_data.json | 14 ++++++++++++++ docs/contributor/ci.md | 2 ++ docs/contributor/index.md | 8 ++++++++ docs/contributor/release.md | 2 ++ docs/contributor/update-website.md | 2 ++ docs/overview.md | 5 +++++ website/public/_data.json | 3 +++ 7 files changed, 36 insertions(+) create mode 100644 docs/contributor/_data.json create mode 100644 docs/contributor/ci.md create mode 100644 docs/contributor/index.md create mode 100644 docs/contributor/release.md create mode 100644 docs/contributor/update-website.md diff --git a/docs/contributor/_data.json b/docs/contributor/_data.json new file mode 100644 index 00000000..28ab9142 --- /dev/null +++ b/docs/contributor/_data.json @@ -0,0 +1,14 @@ +{ + "index": { + "title": "For contributors" + }, + "ci": { + "title": "CI" + }, + "update-website": { + "title": "Update website" + }, + "release": { + "title": "Release" + } +} diff --git a/docs/contributor/ci.md b/docs/contributor/ci.md new file mode 100644 index 00000000..1c85bc5a --- /dev/null +++ b/docs/contributor/ci.md @@ -0,0 +1,2 @@ +# CI + diff --git a/docs/contributor/index.md b/docs/contributor/index.md new file mode 100644 index 00000000..a839f990 --- /dev/null +++ b/docs/contributor/index.md @@ -0,0 +1,8 @@ +# For contributors + +This section explains some operations for managing the project. + +1. [CI](../../docs/contributor/ci.md) +1. [Update website](../../docs/contributor/update-website.md) +1. [Release](../../docs/contributor/release.md) + diff --git a/docs/contributor/release.md b/docs/contributor/release.md new file mode 100644 index 00000000..8efbea5d --- /dev/null +++ b/docs/contributor/release.md @@ -0,0 +1,2 @@ +# Release + diff --git a/docs/contributor/update-website.md b/docs/contributor/update-website.md new file mode 100644 index 00000000..8f20da5c --- /dev/null +++ b/docs/contributor/update-website.md @@ -0,0 +1,2 @@ +# Update website + diff --git a/docs/overview.md b/docs/overview.md index 9cb6e39d..2aa6c4f1 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -24,3 +24,8 @@ See [Basic techniques](../docs/basic/index.md) section. If you'd like to create complex, awesome scrolling animation using ViewPager or something, please check out [Advanced techniques](../docs/advanced/index.md) section. + +## If you're interested in improving this library... + +Please see the [contribution guideline](../CONTRIBUTING.md). +Also, [For contributiors](../docs/contributor/index.md) section will be useful to understand / manage the entire project. diff --git a/website/public/_data.json b/website/public/_data.json index b678da98..472b8dc2 100644 --- a/website/public/_data.json +++ b/website/public/_data.json @@ -10,5 +10,8 @@ }, "advanced": { "title": "Advanced techniques" + }, + "contributor": { + "title": "For contributors" } } From 7d0ac62ff0d841396367deaca191eb9fb7942d35 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 3 May 2015 23:18:48 +0900 Subject: [PATCH 067/208] Added favicon. --- website/public/_head.ejs | 1 + website/public/favicon.ico | Bin 0 -> 1150 bytes 2 files changed, 1 insertion(+) create mode 100644 website/public/favicon.ico diff --git a/website/public/_head.ejs b/website/public/_head.ejs index b9781860..6bdc35a3 100644 --- a/website/public/_head.ejs +++ b/website/public/_head.ejs @@ -5,4 +5,5 @@ + diff --git a/website/public/favicon.ico b/website/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..16e82b11e2b364b2823f4efcf6f74af4982ec660 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lFaYIf1Q>zlDg&_&kr<>Fqz48# zn-mGe|DuX zihdA37pNa(21pMs{RdA>_}@3f8%2Lhzs>)w3PG?K$P7~S|Azu(Gl(mHjX|Nkw! z+QFuP%qXlE!E684J)J1-pE%nGtUtRF;&*)dGt2n@Z{6Jv*PmG~^ncNsNTT%5TIvmU z;})oX5C;@5==wooxcm<@1Ev>UKP;Yc=?BS!%z&vw){iWPPd~CcTx>M`EV$I+5(nzX amK#BNf*1@`Mjh}U7!x3TfSEK5(+2>$p=I3w literal 0 HcmV?d00001 From 4009b942988624fe3941ad49a98b7c245cccb290 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 00:27:26 +0900 Subject: [PATCH 068/208] Wrote quick start section. --- docs/overview.md | 2 +- docs/quick-start/animation.md | 157 +++++++++++++++++++++++++++++++ docs/quick-start/dependencies.md | 14 ++- docs/quick-start/layout.md | 16 ++++ 4 files changed, 186 insertions(+), 3 deletions(-) diff --git a/docs/overview.md b/docs/overview.md index 2aa6c4f1..e77c2510 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -1,6 +1,6 @@ # Overview -Android-ObservableScrollView is a library to handle scroll position for Android library, and contains lots of examples to demonstrate how this library works. +Android-ObservableScrollView is a library to handle scroll position for Android apps, and contains lots of examples to demonstrate how this library works. However, creating awesome scrolling effects depends deeply on how you use it: layout, offset calculation to animate views, etc. diff --git a/docs/quick-start/animation.md b/docs/quick-start/animation.md index 91c3f6e5..c775e80d 100644 --- a/docs/quick-start/animation.md +++ b/docs/quick-start/animation.md @@ -1,2 +1,159 @@ # Animation codes +This time, we implement ActionBar animation using `AppCompatActivity` in the support library. + +## Apply layout to the activity + +At first, let `Activity` extend the `AppCompatActivity` and set [the layout we wrote](../../../docs/quick-start/layout.md) to it. + +```java +import android.support.v7.app.AppCompatActivity; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); +``` + +## Initialize ObservableListView + +Add some initialization codes to `onCreate()`: + +```java + @Override + protected void onCreate(Bundle savedInstanceState) { + : + ObservableListView listView = (ObservableListView) findViewById(R.id.list); + listView.setScrollViewCallbacks(this); + } +``` + +You will see an error around `setScrollViewCallbacks(this)` because the Activity does not implement the required interface yet. +So add `implements ObservableScrollViewCallbacks` to the Activity definition: + +```java +public class MainActivity extends AppCompatActivity + implements ObservableScrollViewCallbacks { +``` + +Then implement required methods: + +```java + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, + boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } +} +``` + +Now we can handle the scroll events. + +## Populate list data + +Before write codes to animate views, set data to ListView. + +```java + // Add these codes after ListView initialization + ArrayList items = new ArrayList(); + for (int i = 1; i <= 100; i++) { + items.add("Item " + i); + } + listView.setAdapter(new ArrayAdapter( + this, android.R.layout.simple_list_item_1, items)); +``` + +## Animate with scroll events + +Finally, we can write the main code now. +Add some code to show/hide the ActionBar in `onUpOrCancelMotionEvent` method for example. + +```java + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + ActionBar ab = getSupportActionBar(); + if (scrollState == ScrollState.UP) { + if (ab.isShowing()) { + ab.hide(); + } + } else if (scrollState == ScrollState.DOWN) { + if (!ab.isShowing()) { + ab.show(); + } + } + } +``` + +`ScrollState` parameter indicates the direction of swiping, and this event will occur when you touch up (or cancel) the ListView. +This is just an introduction so we don't use other events like `onScrollChanged`. + +Now let's build and launch the app. + +You can see the ActionBar gets hidden or shown when you swipe the ListView. + +As you can see, the most important codes are the animation codes in the callbacks. +You can learn how to write these code in this tutorial. + +In the [next section](../../../docs/example/index.md), we'll check the existing examples to see what we can do with this library. + +## Program list + +Following codes are the entire Activity, just for your reference. + +```java +import android.support.v7.app.AppCompatActivity; +// other imports and package statement are omitted + +public class MainActivity extends AppCompatActivity + implements ObservableScrollViewCallbacks { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + ObservableListView listView = (ObservableListView) findViewById(R.id.list); + listView.setScrollViewCallbacks(this); + + // TODO These are dummy. Populate your data here. + ArrayList items = new ArrayList(); + for (int i = 1; i <= 100; i++) { + items.add("Item " + i); + } + listView.setAdapter(new ArrayAdapter( + this, android.R.layout.simple_list_item_1, items)); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, + boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + ActionBar ab = getSupportActionBar(); + if (scrollState == ScrollState.UP) { + if (ab.isShowing()) { + ab.hide(); + } + } else if (scrollState == ScrollState.DOWN) { + if (!ab.isShowing()) { + ab.show(); + } + } + } +} +``` diff --git a/docs/quick-start/dependencies.md b/docs/quick-start/dependencies.md index b4a0badf..e8739da1 100644 --- a/docs/quick-start/dependencies.md +++ b/docs/quick-start/dependencies.md @@ -1,8 +1,12 @@ # Dependencies -## Gradle +This library is published to the Maven Central repository, so you can use it through Gradle/Maven. +You can use it in Eclipse, but Android Studio (or Gradle) is recommended. +In Quick start guide, we assume you're using Android Studio. -You should replace `VERSION` to the appropriate version number like `1.5.0`. +## build.gradle + +Write the following dependency configuration to your `build.gradle`. ```groovy repositories { @@ -10,7 +14,13 @@ repositories { } dependencies { + // Other dependencies are omitted compile 'com.github.ksoichiro:android-observablescrollview:VERSION' } ``` +You should replace `VERSION` to the appropriate version number like `1.5.0`. + +Then, click "sync" button to get the library using the configuration above. + +To confirm the available versions, search [the Maven Central Repository](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.ksoichiro%22%20AND%20a%3A%22android-observablescrollview%22). \ No newline at end of file diff --git a/docs/quick-start/layout.md b/docs/quick-start/layout.md index 701466c9..95b45f61 100644 --- a/docs/quick-start/layout.md +++ b/docs/quick-start/layout.md @@ -1,2 +1,18 @@ # Layout +After adding the dependency, let's write layout file such as `res/layout/activity_main.xml`. + +This time, we'll use only one element `ObservableListView`. + +```xml + +``` + +Android-ObservableScrollView provides several types of views that are scroll-able such as `ObservableScrollView`, `ObservableGridView`, etc. +And they extend the standard widget (`ScrollView`, `GridView`, ...), and they provide some callbacks to get scroll events. + +After writing the above layout, you can write animation codes using these callbacks. From 6a0fa07cacdfa000d71b71a7a800b29322b0709f Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 00:53:24 +0900 Subject: [PATCH 069/208] Added links to the next page. --- docs/advanced/index.md | 2 ++ docs/advanced/sliding-up.md | 1 + docs/basic/_data.json | 2 +- docs/basic/filling-gap.md | 1 + docs/basic/index.md | 4 +++- docs/basic/parallax-image.md | 1 + docs/basic/show-hide-action-bar.md | 4 +++- docs/basic/translating-itself.md | 2 +- docs/basic/translating-toolbar.md | 1 + docs/example/android-studio.md | 1 + docs/example/eclipse.md | 7 +++++++ docs/example/google-play.md | 1 + docs/example/index.md | 2 ++ docs/example/wercker.md | 2 ++ docs/quick-start/animation.md | 2 ++ docs/quick-start/dependencies.md | 4 +++- docs/quick-start/index.md | 2 ++ docs/quick-start/layout.md | 2 ++ 18 files changed, 36 insertions(+), 5 deletions(-) diff --git a/docs/advanced/index.md b/docs/advanced/index.md index cc599afb..3adac725 100644 --- a/docs/advanced/index.md +++ b/docs/advanced/index.md @@ -6,3 +6,5 @@ I'd appreciate it if you could suggest new patterns or improvements of the libra 1. [Sliding up pattern](../../docs/advanced/sliding-up.md) 1. [ViewPager pattern](../../docs/advanced/viewpager.md) + +[Next: Sliding up pattern »](../../docs/advanced/sliding-up.md) diff --git a/docs/advanced/sliding-up.md b/docs/advanced/sliding-up.md index 7c177abb..4cd86334 100644 --- a/docs/advanced/sliding-up.md +++ b/docs/advanced/sliding-up.md @@ -1,2 +1,3 @@ # Sliding up pattern +[Next: ViewPager pattern »](../../docs/advanced/viewpager.md) diff --git a/docs/basic/_data.json b/docs/basic/_data.json index 89b06f43..44f93c7d 100644 --- a/docs/basic/_data.json +++ b/docs/basic/_data.json @@ -3,7 +3,7 @@ "title": "Basic techniques" }, "show-hide-action-bar": { - "title": "Show and hide the Action Bar" + "title": "Show and hide the ActionBar" }, "translating-toolbar": { "title": "Translating the Toolbar" diff --git a/docs/basic/filling-gap.md b/docs/basic/filling-gap.md index 121b5594..6fc557ac 100644 --- a/docs/basic/filling-gap.md +++ b/docs/basic/filling-gap.md @@ -1,2 +1,3 @@ # Filling gap on top of the Toolbar +[Next: Advanced techniques »](../../docs/advanced/index.md) diff --git a/docs/basic/index.md b/docs/basic/index.md index 01a74bc3..28739702 100644 --- a/docs/basic/index.md +++ b/docs/basic/index.md @@ -2,9 +2,11 @@ This section explains the basic scrolling techniques. -1. [Show and hide the Action Bar](../../docs/basic/show-hide-action-bar.md) +1. [Show and hide the ActionBar](../../docs/basic/show-hide-action-bar.md) 1. [Translating the Toolbar](../../docs/basic/translating-toolbar.md) 1. [Translating itself](../../docs/basic/translating-itself.md) 1. [Parallax image](../../docs/basic/parallax-image.md) 1. [Sticky header](../../docs/basic/sticky-header.md) 1. [Filling gap on top of the Toolbar](../../docs/basic/filling-gap.md) + +[Next: Show and hide the ActionBar »](../../docs/basic/show-hide-action-bar.md) diff --git a/docs/basic/parallax-image.md b/docs/basic/parallax-image.md index b2b76ce5..0e319533 100644 --- a/docs/basic/parallax-image.md +++ b/docs/basic/parallax-image.md @@ -1,2 +1,3 @@ # Parallax image +[Next: Sticky header »](../../docs/basic/filling-gap.md) diff --git a/docs/basic/show-hide-action-bar.md b/docs/basic/show-hide-action-bar.md index d2d1ce38..1486f437 100644 --- a/docs/basic/show-hide-action-bar.md +++ b/docs/basic/show-hide-action-bar.md @@ -1 +1,3 @@ -# Show and hide the Action Bar +# Show and hide the ActionBar + +[Next: Translating the Toolbar »](../../docs/basic/translating-toolbar.md) diff --git a/docs/basic/translating-itself.md b/docs/basic/translating-itself.md index e66d0747..4364300a 100644 --- a/docs/basic/translating-itself.md +++ b/docs/basic/translating-itself.md @@ -1,3 +1,3 @@ # Translating itself - +[Next: Parallax image »](../../docs/basic/parallax-image.md) diff --git a/docs/basic/translating-toolbar.md b/docs/basic/translating-toolbar.md index ea405ec1..9fe05bba 100644 --- a/docs/basic/translating-toolbar.md +++ b/docs/basic/translating-toolbar.md @@ -1,2 +1,3 @@ # Translating the Toolbar +[Next: Translating itself »](../../docs/basic/translating-itself.md) diff --git a/docs/example/android-studio.md b/docs/example/android-studio.md index 2cb6c571..abd06a4a 100644 --- a/docs/example/android-studio.md +++ b/docs/example/android-studio.md @@ -1,2 +1,3 @@ # Build on Android Studio +[Next: Build on Eclipse »](../../docs/example/eclipse.md) diff --git a/docs/example/eclipse.md b/docs/example/eclipse.md index e304e8d6..82d07c2a 100644 --- a/docs/example/eclipse.md +++ b/docs/example/eclipse.md @@ -22,10 +22,16 @@ Please note that with these instructions you could bulid project on Eclipse, but ### Get the source codes +Get the source code of the library and example app, by cloning git repository or downloading archives. + +If you use git, execute the following command in your workspace directory. + ``` $ git clone https://github.com/ksoichiro/Android-ObservableScrollView.git ``` +If you are using Windows, try it on GitBash or Cygwin or something that supports git. + ### Define ANDROID_HOME environment variable If you haven't define the environment variable `ANDROID_HOME` yet, define it to indicate Android SDK root directory. @@ -59,3 +65,4 @@ This will generate dependency codes from AAR files using Gradle wrapper and some That's all! +[Next: Basic techniques »](../../docs/basic/index.md) diff --git a/docs/example/google-play.md b/docs/example/google-play.md index 5e8cce76..1de1c7f6 100644 --- a/docs/example/google-play.md +++ b/docs/example/google-play.md @@ -1,2 +1,3 @@ # Download from Google Play +[Next: Download from wercker »](../../docs/example/wercker.md) diff --git a/docs/example/index.md b/docs/example/index.md index 502d4205..e73dc4d9 100644 --- a/docs/example/index.md +++ b/docs/example/index.md @@ -7,3 +7,5 @@ and check if there are some patterns you want to implement. 1. [Download from wercker](../../docs/eaxmple/wercker.md) 1. [Build on Android Studio](../../docs/example/android-studio.md) 1. [Build on Eclipse](../../docs/example/eclipse.md) + +[Next: Download from Google Play »](../../docs/example/google-play.md) diff --git a/docs/example/wercker.md b/docs/example/wercker.md index 860468cc..51074ced 100644 --- a/docs/example/wercker.md +++ b/docs/example/wercker.md @@ -31,3 +31,5 @@ Scroll the screen, and click anywhere in the "inspect build result" section to o Finally, you can download the apk file by clicking the `artifact.tar.gz` link. ![](../images/wercker_3.png) + +[Next: Build on Android Studio »](../../docs/example/android-studio.md) diff --git a/docs/quick-start/animation.md b/docs/quick-start/animation.md index c775e80d..f061da76 100644 --- a/docs/quick-start/animation.md +++ b/docs/quick-start/animation.md @@ -157,3 +157,5 @@ public class MainActivity extends AppCompatActivity } } ``` + +[Next: Try the example app »](../../docs/example/index.md) diff --git a/docs/quick-start/dependencies.md b/docs/quick-start/dependencies.md index e8739da1..7d2d5208 100644 --- a/docs/quick-start/dependencies.md +++ b/docs/quick-start/dependencies.md @@ -23,4 +23,6 @@ You should replace `VERSION` to the appropriate version number like `1.5.0`. Then, click "sync" button to get the library using the configuration above. -To confirm the available versions, search [the Maven Central Repository](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.ksoichiro%22%20AND%20a%3A%22android-observablescrollview%22). \ No newline at end of file +To confirm the available versions, search [the Maven Central Repository](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.ksoichiro%22%20AND%20a%3A%22android-observablescrollview%22). + +[Next: Layout »](../../docs/quick-start/layout.md) diff --git a/docs/quick-start/index.md b/docs/quick-start/index.md index ca30df3b..759d2825 100644 --- a/docs/quick-start/index.md +++ b/docs/quick-start/index.md @@ -6,3 +6,5 @@ In this section, I'll show some quick instructions for introducing this library 1. [Dependencies](../../docs/quick-start/dependencies.md) 1. [Layout](../../docs/quick-start/layout.md) 1. [Animation codes](../../docs/quick-start/animation.md) + +[Next: Dependencies »](../../docs/quick-start/dependencies.md) diff --git a/docs/quick-start/layout.md b/docs/quick-start/layout.md index 95b45f61..df81efb0 100644 --- a/docs/quick-start/layout.md +++ b/docs/quick-start/layout.md @@ -16,3 +16,5 @@ Android-ObservableScrollView provides several types of views that are scroll-abl And they extend the standard widget (`ScrollView`, `GridView`, ...), and they provide some callbacks to get scroll events. After writing the above layout, you can write animation codes using these callbacks. + +[Next: Animation codes »](../../docs/quick-start/animation.md) From 64daa1a6b8ac8ec4a29df379e7933a667374271d Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 02:18:41 +0900 Subject: [PATCH 070/208] Implemented menu toggle button. --- docs/_layout.ejs | 3 +++ website/public/_nav.ejs | 2 +- website/public/css/main.less | 37 ++++++++++++++++++++++++++++++++++- website/public/js/main.coffee | 9 +++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/docs/_layout.ejs b/docs/_layout.ejs index 5ff744f1..67b8ec7e 100644 --- a/docs/_layout.ejs +++ b/docs/_layout.ejs @@ -8,6 +8,9 @@ <%- partial("../_nav") %>
    +

    + +

    s - table { - .table; - } +// Apply .table to all
    s +table { + .table; } // Navbar diff --git a/website/public/index.ejs b/website/public/index.ejs index e17b67b8..56e6bccc 100644 --- a/website/public/index.ejs +++ b/website/public/index.ejs @@ -1,3 +1 @@ -
    <%- partial('../../README') %> -
    From dc787fc8f5b2dc59ebb83cbd50f2c91236a37f56 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 09:17:32 +0900 Subject: [PATCH 073/208] Fixed some styles for readability and fixed typo. --- docs/quick-start/index.md | 2 +- website/public/css/main.less | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/quick-start/index.md b/docs/quick-start/index.md index 759d2825..31b63d0f 100644 --- a/docs/quick-start/index.md +++ b/docs/quick-start/index.md @@ -1,4 +1,4 @@ -# Quicks start +# Quick start Thank you for having interest in this library! In this section, I'll show some quick instructions for introducing this library into your app. diff --git a/website/public/css/main.less b/website/public/css/main.less index 061b158b..d99d8a39 100644 --- a/website/public/css/main.less +++ b/website/public/css/main.less @@ -13,7 +13,7 @@ } // Fonts -body, .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6, p { +body, h1, h2, h3, h4, h5, h6, p { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 300; color: #212121; @@ -21,12 +21,16 @@ body, .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6, p { body, p { font-size: 14px; } +h1, h2 { + color: #009688; +} h1 { font-size: 28px; font-weight: 400; } h2 { font-size: 22px; + font-weight: 400; } h3 { font-size: 20px; From 9128dc6474afb19a1b5ddfac4ff5132bae0dbd10 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 09:41:23 +0900 Subject: [PATCH 074/208] Classified example Activities and listed on each topic pages. --- docs/advanced/sliding-up.md | 10 ++++++++++ docs/advanced/viewpager.md | 9 +++++++++ docs/basic/_data.json | 9 ++++++--- docs/basic/filling-gap.md | 16 ++++++++++++++++ docs/basic/flexible-space-toolbar.md | 9 +++++++++ docs/basic/flexible-space-with-image.md | 11 +++++++++++ docs/basic/parallax-image.md | 6 ++++++ docs/basic/show-hide-action-bar.md | 9 +++++++++ docs/basic/sticky-header.md | 9 +++++++++ docs/basic/translating-itself.md | 3 --- docs/basic/translating-toolbar.md | 12 +++++++++++- 11 files changed, 96 insertions(+), 7 deletions(-) create mode 100644 docs/basic/flexible-space-toolbar.md create mode 100644 docs/basic/flexible-space-with-image.md delete mode 100644 docs/basic/translating-itself.md diff --git a/docs/advanced/sliding-up.md b/docs/advanced/sliding-up.md index 4cd86334..21494d15 100644 --- a/docs/advanced/sliding-up.md +++ b/docs/advanced/sliding-up.md @@ -1,3 +1,13 @@ # Sliding up pattern +This topic describes how to slide a panel from the bottom like Google's "Map" app, +which are implemented in the following examples. + +* SlidingUpBaseActivity +* SlidingUpGridViewActivity +* SlidingUpListViewActivity +* SlidingUpRecyclerViewActivity +* SlidingUpScrollViewActivity +* SlidingUpWebViewActivity + [Next: ViewPager pattern »](../../docs/advanced/viewpager.md) diff --git a/docs/advanced/viewpager.md b/docs/advanced/viewpager.md index 2c2bdebd..54a3caf9 100644 --- a/docs/advanced/viewpager.md +++ b/docs/advanced/viewpager.md @@ -1,2 +1,11 @@ # ViewPager pattern +This topic describes how to integrate scrollable views with ViewPager, +which are implemented in the following examples. + +* ViewPagerTab2Activity +* ViewPagerTabActivity +* ViewPagerTabFragmentActivity +* ViewPagerTabListViewActivity +* ViewPagerTabScrollViewActivity + diff --git a/docs/basic/_data.json b/docs/basic/_data.json index 44f93c7d..d05c8adf 100644 --- a/docs/basic/_data.json +++ b/docs/basic/_data.json @@ -8,15 +8,18 @@ "translating-toolbar": { "title": "Translating the Toolbar" }, - "translating-itself": { - "title": "Translating itself" - }, "parallax-image": { "title": "Parallax image" }, "sticky-header": { "title": "Sticky header" }, + "flexible-space-toolbar": { + "title": "Flexible space on the Toolbar" + }, + "flexible-space-with-image": { + "title": "Flexible space with image" + }, "filling-gap": { "title": "Filling gap on top of the Toolbar" } diff --git a/docs/basic/filling-gap.md b/docs/basic/filling-gap.md index 6fc557ac..ae2688be 100644 --- a/docs/basic/filling-gap.md +++ b/docs/basic/filling-gap.md @@ -1,3 +1,19 @@ # Filling gap on top of the Toolbar +This topic describes how to fill the gap on top of the Toolbar, +which are implemented in the following examples. + +* FillGapBaseActivity +* FillGapListViewActivity +* FillGapRecyclerViewActivity +* FillGapScrollViewActivity +* FillGap2BaseActivity +* FillGap2ListViewActivity +* FillGap2RecyclerViewActivity +* FillGap2ScrollViewActivity +* FillGap3BaseActivity +* FillGap3ListViewActivity +* FillGap3RecyclerViewActivity +* FillGap3ScrollViewActivity + [Next: Advanced techniques »](../../docs/advanced/index.md) diff --git a/docs/basic/flexible-space-toolbar.md b/docs/basic/flexible-space-toolbar.md new file mode 100644 index 00000000..a0439b12 --- /dev/null +++ b/docs/basic/flexible-space-toolbar.md @@ -0,0 +1,9 @@ +# Flexible space on the Toolbar + +This topic describes how to create flexible space on the Toolbar, +which are implemented in the following examples. + +* FlexibleSpaceToolbarScrollViewActivity +* FlexibleSpaceToolbarWebViewActivity + +[Next: Flexible space with image »](../../docs/basic/flexible-space-with-image.md) diff --git a/docs/basic/flexible-space-with-image.md b/docs/basic/flexible-space-with-image.md new file mode 100644 index 00000000..d888d95f --- /dev/null +++ b/docs/basic/flexible-space-with-image.md @@ -0,0 +1,11 @@ +# Flexible space with image + +This topic describes how to create flexible space with image, +which are implemented in the following examples. + +* FlexibleSpaceWithImageListViewActivity +* FlexibleSpaceWithImageRecyclerViewActivity +* FlexibleSpaceWithImageScrollViewActivity +* FlexibleSpaceWithImageWithViewPagerTab2Activity + +[Next: Filling gap on top of the Toolbar »](../../docs/basic/filling-gap.md) diff --git a/docs/basic/parallax-image.md b/docs/basic/parallax-image.md index 0e319533..22c096b5 100644 --- a/docs/basic/parallax-image.md +++ b/docs/basic/parallax-image.md @@ -1,3 +1,9 @@ # Parallax image +This topic describes how to create parallax effect, +which are implemented in the following examples. + +* ParallaxToolbarListViewActivity +* ParallaxToolbarScrollViewActivity + [Next: Sticky header »](../../docs/basic/filling-gap.md) diff --git a/docs/basic/show-hide-action-bar.md b/docs/basic/show-hide-action-bar.md index 1486f437..34a70c8b 100644 --- a/docs/basic/show-hide-action-bar.md +++ b/docs/basic/show-hide-action-bar.md @@ -1,3 +1,12 @@ # Show and hide the ActionBar +This topic describes how to show and hide the ActionBar, +which are implemented in the following examples. + +* ActionBarControlGridViewActivity +* ActionBarControlListViewActivity +* ActionBarControlRecyclerViewActivity +* ActionBarControlScrollViewActivity +* ActionBarControlWebViewActivity + [Next: Translating the Toolbar »](../../docs/basic/translating-toolbar.md) diff --git a/docs/basic/sticky-header.md b/docs/basic/sticky-header.md index 70f7b390..9a7a829a 100644 --- a/docs/basic/sticky-header.md +++ b/docs/basic/sticky-header.md @@ -1,2 +1,11 @@ # Sticky header +This topic describes how to keep header on top of the screen, +which are implemented in the following examples. + +* StickyHeaderListViewActivity +* StickyHeaderRecyclerViewActivity +* StickyHeaderScrollViewActivity +* StickyHeaderWebViewActivity + +[Next: Flexible space on the Toolbar »](../../docs/basic/flexible-space-toolbar.md) diff --git a/docs/basic/translating-itself.md b/docs/basic/translating-itself.md deleted file mode 100644 index 4364300a..00000000 --- a/docs/basic/translating-itself.md +++ /dev/null @@ -1,3 +0,0 @@ -# Translating itself - -[Next: Parallax image »](../../docs/basic/parallax-image.md) diff --git a/docs/basic/translating-toolbar.md b/docs/basic/translating-toolbar.md index 9fe05bba..b2f7ce23 100644 --- a/docs/basic/translating-toolbar.md +++ b/docs/basic/translating-toolbar.md @@ -1,3 +1,13 @@ # Translating the Toolbar -[Next: Translating itself »](../../docs/basic/translating-itself.md) +This topic describes how to translate the Toolbar, +which are implemented in the following examples. + +* ToolbarControlBaseActivity +* ToolbarControlGridViewActivity +* ToolbarControlListViewActivity +* ToolbarControlRecyclerViewActivity +* ToolbarControlScrollViewActivity +* ToolbarControlWebViewActivity + +[Next: Parallax image »](../../docs/basic/parallax-image.md) From 65d64e56b39723cf581ec265c7a27d8ed63e347d Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 09:49:50 +0900 Subject: [PATCH 075/208] Changed link style. --- website/public/css/main.less | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/website/public/css/main.less b/website/public/css/main.less index d99d8a39..30e16df1 100644 --- a/website/public/css/main.less +++ b/website/public/css/main.less @@ -74,8 +74,15 @@ h3 { } } code { - color: #009688; // Teal 500 - background-color: lighten(#E0F2F1, 7%); // Teal 50 + color: @theme-main-color; + background-color: lighten(@theme-brand-color, 7%); +} +a, +a:hover { + color: @theme-main-color; +} +a:hover { + text-decoration: none; } // Layout From 24d07d517ee6d2ef8467b505381a2589387287c6 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 09:50:31 +0900 Subject: [PATCH 076/208] Changed title for documentation pages. --- website/public/_head.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/public/_head.ejs b/website/public/_head.ejs index 6bdc35a3..48ec367b 100644 --- a/website/public/_head.ejs +++ b/website/public/_head.ejs @@ -1,7 +1,7 @@ -<%- title %> | <%- description %> +<%- title %> | <%- title === name ? description : name %> From c80b66a154625972b093b038e17f205b281893c4 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 14:11:03 +0900 Subject: [PATCH 077/208] Changed top page design and separated less files. --- docs/_layout.ejs | 1 + website/bower.json | 4 + website/public/_data.json | 4 + website/public/css/{code.less => _code.less} | 0 website/public/css/_colors.less | 47 ++++ website/public/css/_fonts.less | 24 ++ website/public/css/_footer.less | 28 +++ website/public/css/_layout.less | 24 ++ website/public/css/_mixins.less | 8 + website/public/css/_navbar.less | 43 ++++ website/public/css/_roboto-fonts.less | 34 +++ website/public/css/_sidebar.less | 72 ++++++ website/public/css/_site-top.less | 57 +++++ website/public/css/main.less | 236 +------------------ website/public/index.ejs | 45 ++++ website/public/js/main.coffee | 16 ++ 16 files changed, 416 insertions(+), 227 deletions(-) rename website/public/css/{code.less => _code.less} (100%) create mode 100644 website/public/css/_colors.less create mode 100644 website/public/css/_fonts.less create mode 100644 website/public/css/_footer.less create mode 100644 website/public/css/_layout.less create mode 100644 website/public/css/_mixins.less create mode 100644 website/public/css/_navbar.less create mode 100644 website/public/css/_roboto-fonts.less create mode 100644 website/public/css/_sidebar.less create mode 100644 website/public/css/_site-top.less diff --git a/docs/_layout.ejs b/docs/_layout.ejs index 67b8ec7e..744e2c00 100644 --- a/docs/_layout.ejs +++ b/docs/_layout.ejs @@ -16,6 +16,7 @@
    • <% for (var sectionSlug in public._data) { %> + <% if (sectionSlug == "index") { continue; } %>
    • <%- public._data[sectionSlug].title %>

        diff --git a/website/bower.json b/website/bower.json index 42842a61..ee307f89 100644 --- a/website/bower.json +++ b/website/bower.json @@ -4,6 +4,7 @@ "dependencies": { "jquery": "1.11.2", "bootstrap": "3.3.4", + "roboto-fontface": "0.4.0", "components-font-awesome": "4.3.0", "highlightjs": "8.5.0", "respond-minmax": "1.4.2", @@ -16,6 +17,9 @@ "bootstrap": { "main": ["dist/css/*.min.css", "dist/js/*.min.js", "dist/fonts/*"] }, + "roboto-fontface": { + "main": ["fonts/Roboto-Thin*", "fonts/Roboto-Light*", "fonts/Roboto-Regular*"] + }, "components-font-awesome": { "main": ["css/*.min.css", "fonts/*"] }, diff --git a/website/public/_data.json b/website/public/_data.json index a2b6fc8d..b6a7fe24 100644 --- a/website/public/_data.json +++ b/website/public/_data.json @@ -1,4 +1,8 @@ { + "index": { + "title": "Android-ObservableScrollView", + "layout": false + }, "quick-start": { "title": "Quick start" }, diff --git a/website/public/css/code.less b/website/public/css/_code.less similarity index 100% rename from website/public/css/code.less rename to website/public/css/_code.less diff --git a/website/public/css/_colors.less b/website/public/css/_colors.less new file mode 100644 index 00000000..d5ccfa83 --- /dev/null +++ b/website/public/css/_colors.less @@ -0,0 +1,47 @@ +@theme-main-color: #009688; // 500 +@theme-focus-color: #00897B; // 600 +@theme-brand-color: #E0F2F1; // 50 +@theme-focus-border-color: #00695C; // 800 +@theme-active-color: #00695C; // 800 +.navbar-inverse { + background-color: @theme-main-color; + border-color: @theme-main-color; + .navbar-brand, + .navbar-nav>li>a { + color: @theme-brand-color; + } + .navbar-nav>.active>a, + .navbar-nav>.active>a:focus, + .navbar-nav>.active>a:hover { + background-color: @theme-active-color; + } + .navbar-nav>.open>a, + .navbar-nav>.open>a:focus, + .navbar-nav>.open>a:hover { + background-color: @theme-focus-color; + } + .navbar-toggle { + background-color: @theme-main-color; + } + .navbar-toggle { + border-color: @theme-focus-border-color; + &:hover, + &:focus { + background-color: @theme-focus-color; + } + } + .navbar-collapse { + border-color: @theme-focus-border-color; + } +} +code { + color: @theme-main-color; + background-color: lighten(@theme-brand-color, 7%); +} +a, +a:hover { + color: @theme-main-color; +} +a:hover { + text-decoration: none; +} diff --git a/website/public/css/_fonts.less b/website/public/css/_fonts.less new file mode 100644 index 00000000..318ffb30 --- /dev/null +++ b/website/public/css/_fonts.less @@ -0,0 +1,24 @@ +@import '_roboto-fonts.less'; + +body, h1, h2, h3, h4, h5, h6, p { + font-family: Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; + color: #212121; +} +body, p { + font-size: 14px; +} +h1, h2 { + color: #009688; +} +h1 { + font-size: 28px; + font-weight: 400; +} +h2 { + font-size: 22px; + font-weight: 400; +} +h3 { + font-size: 20px; +} diff --git a/website/public/css/_footer.less b/website/public/css/_footer.less new file mode 100644 index 00000000..7181131e --- /dev/null +++ b/website/public/css/_footer.less @@ -0,0 +1,28 @@ +@import (reference) '_mixins.less'; + +// Footer +.footer { + .container; + .override-container; + padding: 15px; + border-top: 1px solid lighten(#E0F2F1, 7%); + .btns { + text-align: right; + padding-bottom: 15px; + .ghstar { + width: 100px; + height: 20px; + } + .ghfork { + width: 100px; + height: 20px; + } + } + .copyright { + .text-muted; + .clearfix; + text-align: right; + font-size: 12px; + line-height: 150%; + } +} diff --git a/website/public/css/_layout.less b/website/public/css/_layout.less new file mode 100644 index 00000000..748e2c45 --- /dev/null +++ b/website/public/css/_layout.less @@ -0,0 +1,24 @@ +@import (reference) '_mixins.less'; + +html, +body { + overflow-x: hidden; +} + +body { + padding-top: 70px; +} + +h1 { + margin-top: 8px; +} + +// Markdown fix +// Apply .table to all
    s +table { + .table; +} + +.container { + .override-container; +} diff --git a/website/public/css/_mixins.less b/website/public/css/_mixins.less new file mode 100644 index 00000000..b65e40bd --- /dev/null +++ b/website/public/css/_mixins.less @@ -0,0 +1,8 @@ +// Overwrite bootstrap's default +.override-container(@width: 970px) { + @media (min-width: 1200px) { + & { + width: @width; + } + } +} diff --git a/website/public/css/_navbar.less b/website/public/css/_navbar.less new file mode 100644 index 00000000..271afdc9 --- /dev/null +++ b/website/public/css/_navbar.less @@ -0,0 +1,43 @@ +.navbar-shadow() { + @shadow-h-offset: 0px; + @shadow-v-offset: -3px; + @shadow-blur-radius: 6px; + @shadow-spread-radius: 6px; + @shadow-color: rgba(32, 32, 32, 0.4); + -moz-box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; + -webkit-box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; + box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; +} +.navbar-shadow-clear() { + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +nav.navbar { + .navbar-brand { + background-image: url('../images/logo.png'); + background-repeat: no-repeat; + background-position: 4px center; + padding-left: 56px; + } +} +body#site-top { + #content-header { + display: none; + } + nav.navbar { + .navbar-shadow(); + } + #main-content { + padding-top: 20px; + h1 { + display: none; + } + } +} +body:not(#site-top) { + nav.navbar { + .navbar-shadow(); + } +} diff --git a/website/public/css/_roboto-fonts.less b/website/public/css/_roboto-fonts.less new file mode 100644 index 00000000..72f4f47e --- /dev/null +++ b/website/public/css/_roboto-fonts.less @@ -0,0 +1,34 @@ +@roboto-font-path: '../lib/roboto-fontface/fonts'; + +.roboto-font(@type, @weight, @style) { + @font-face { + font-family: 'Roboto'; + src: url('@{roboto-font-path}/Roboto-@{type}.eot'); + src: url('@{roboto-font-path}/Roboto-@{type}.eot?#iefix') format('embedded-opentype'), + url('@{roboto-font-path}/Roboto-@{type}.woff2') format('woff2'), + url('@{roboto-font-path}/Roboto-@{type}.woff') format('woff'), + url('@{roboto-font-path}/Roboto-@{type}.ttf') format('truetype'), + url('@{roboto-font-path}/Roboto-@{type}.svg#Roboto') format('svg'); + font-weight: @weight; + font-style: @style; + } + + @font-face { + font-family: 'Roboto-@{type}'; + src: url('@{roboto-font-path}/Roboto-@{type}.eot'); + src: url('@{roboto-font-path}/Roboto-@{type}.eot?#iefix') format('embedded-opentype'), + url('@{roboto-font-path}/Roboto-@{type}.woff2') format('woff2'), + url('@{roboto-font-path}/Roboto-@{type}.woff') format('woff'), + url('@{roboto-font-path}/Roboto-@{type}.ttf') format('truetype'), + url('@{roboto-font-path}/Roboto-@{type}.svg#Roboto') format('svg'); + } +} + +.roboto-font-pair(@type, @weight) { + .roboto-font('@{type}', @weight, normal); + .roboto-font('@{type}Italic', @weight, italic); +} + +.roboto-font-pair('Thin', 100); +.roboto-font-pair('Light', 300); +.roboto-font-pair('Regular', 400); diff --git a/website/public/css/_sidebar.less b/website/public/css/_sidebar.less new file mode 100644 index 00000000..cdb2d354 --- /dev/null +++ b/website/public/css/_sidebar.less @@ -0,0 +1,72 @@ +@import (reference) '../../bower_components/bootstrap/less/bootstrap.less'; + +// Page with sidebar +.btn-sidebar-toggle { + .btn; + .btn-default; + .btn-xs; +} +#grid-content { + .make-row(); + #sidebar { + .make-xs-column(6); + .make-sm-column(3); + + ul { + padding-left: 0px; + } + &>ul { + border-right: 1px solid #E0E0E0; + padding-right: 15px; + li { + position: relative; + display: block; + } + &>li { + padding: 8px; + } + .section { + font-size: 14px; + font-weight: 400; + margin: 0px; + margin-bottom: 8px; + &, + &>a { + color: #009688; + text-decoration: none; + } + } + .topic { + &>a { + color: #212121; + } + } + } + } + #sidebar-main-content { + .make-xs-column(12); + .make-sm-column(9); + } +} + +@media screen and (max-width: 767px) { + #grid-content { + position: relative; + -webkit-transition: all .25s ease-out; + -o-transition: all .25s ease-out; + transition: all .25s ease-out; + left: 0; + + &.active { + left: 50%; + } + + #sidebar { + left: -50%; + position: absolute; + top: 0; + width: 50%; + } + } +} + diff --git a/website/public/css/_site-top.less b/website/public/css/_site-top.less new file mode 100644 index 00000000..e117c250 --- /dev/null +++ b/website/public/css/_site-top.less @@ -0,0 +1,57 @@ +@import (reference) '_mixins.less'; + +// Top page animation +@media screen and (min-width: 768px) { + body#site-top { + padding-top: 0px; + nav.navbar { + background: none; + border-color: transparent; + .navbar-shadow-clear(); + &.sticky { + background-color: @theme-main-color; + .navbar-shadow(); + } + .navbar-brand { + background-image: none; + background-position: left center; + padding-left: 0px; + visibility: hidden; + &.visible { + visibility: visible; + padding-left: 15px; + } + } + } + nav.navbar { + #navbar { + &.right { + @translate-amount: -250px; + -moz-transform: translateX(@translate-amount); + -webkit-transform: translateX(@translate-amount); + -o-transform: translateX(@translate-amount); + -ms-transform: translateX(@translate-amount); + } + } + } + + padding-top: 0px; + @content-header-height: 300px; + #content-header { + display: block; + padding-top: 100px; + height: @content-header-height; + background-color: @theme-main-color; + #site-title { + font-weight: 100; + padding-top: 50px; + font-size: 48px; + color: #ffffff; + visibility: hidden; + &.visible { + visibility: visible; + } + } + } + } +} diff --git a/website/public/css/main.less b/website/public/css/main.less index 30e16df1..4ca1fb55 100644 --- a/website/public/css/main.less +++ b/website/public/css/main.less @@ -1,228 +1,10 @@ @import (reference) '../../bower_components/bootstrap/less/bootstrap.less'; - -// Overwrite bootstrap's default -.override-container(@width: 970px) { - @media (min-width: 1200px) { - & { - width: @width; - } - } -} -.container { - .override-container; -} - -// Fonts -body, h1, h2, h3, h4, h5, h6, p { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-weight: 300; - color: #212121; -} -body, p { - font-size: 14px; -} -h1, h2 { - color: #009688; -} -h1 { - font-size: 28px; - font-weight: 400; -} -h2 { - font-size: 22px; - font-weight: 400; -} -h3 { - font-size: 20px; -} - -// Colors -@theme-main-color: #009688; // 500 -@theme-border-color: #00897B; // 600 -@theme-brand-color: #E0F2F1; // 50 -@theme-focus-color: #00695C; // 800 -@theme-active-color: #00695C; // 800 -.navbar-inverse { - background-color: @theme-main-color; - border-color: @theme-border-color; - .navbar-brand, - .navbar-nav>li>a { - color: @theme-brand-color; - } - .navbar-nav>.active>a, - .navbar-nav>.active>a:focus, - .navbar-nav>.active>a:hover { - background-color: @theme-active-color; - } - .navbar-nav>.open>a, - .navbar-nav>.open>a:focus, - .navbar-nav>.open>a:hover { - background-color: @theme-focus-color; - } - .navbar-toggle { - background-color: @theme-main-color; - } - .navbar-toggle { - border-color: @theme-focus-color; - &:hover, - &:focus { - background-color: @theme-border-color; - } - } - .navbar-collapse { - border-color: @theme-border-color; - } -} -code { - color: @theme-main-color; - background-color: lighten(@theme-brand-color, 7%); -} -a, -a:hover { - color: @theme-main-color; -} -a:hover { - text-decoration: none; -} - -// Layout - -html, -body { - overflow-x: hidden; -} - -body { - padding-top: 70px; -} - -h1 { - margin-top: 8px; -} - -// Markdown fix -// Apply .table to all
    s -table { - .table; -} - -// Navbar -nav.navbar { - @shadow-h-offset: 0px; - @shadow-v-offset: -3px; - @shadow-blur-radius: 6px; - @shadow-spread-radius: 6px; - @shadow-color: rgba(32, 32, 32, 0.4); - -moz-box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; - -webkit-box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; - box-shadow: @shadow-h-offset @shadow-v-offset @shadow-blur-radius @shadow-spread-radius @shadow-color; - .navbar-brand { - background-image: url('../images/logo.png'); - background-repeat: no-repeat; - background-position: 4px center; - padding-left: 56px; - } -} - -// Footer -.footer { - .container; - .override-container; - padding: 15px; - border-top: 1px solid lighten(#E0F2F1, 7%); - .btns { - text-align: right; - padding-bottom: 15px; - .ghstar { - width: 100px; - height: 20px; - } - .ghfork { - width: 100px; - height: 20px; - } - } - .copyright { - .text-muted; - .clearfix; - text-align: right; - font-size: 12px; - line-height: 150%; - } -} - -// Page without sidebar -#content { -} - -// Page with sidebar -.btn-sidebar-toggle { - .btn; - .btn-default; - .btn-xs; -} -#grid-content { - .make-row(); - #sidebar { - .make-xs-column(6); - .make-sm-column(3); - - ul { - padding-left: 0px; - } - &>ul { - border-right: 1px solid #E0E0E0; - padding-right: 15px; - li { - position: relative; - display: block; - } - &>li { - padding: 8px; - } - .section { - font-size: 14px; - font-weight: 400; - margin: 0px; - margin-bottom: 8px; - &, - &>a { - color: #009688; - text-decoration: none; - } - } - .topic { - &>a { - color: #212121; - } - } - } - } - #sidebar-main-content { - .make-xs-column(12); - .make-sm-column(9); - } -} - -@media screen and (max-width: 767px) { - #grid-content { - position: relative; - -webkit-transition: all .25s ease-out; - -o-transition: all .25s ease-out; - transition: all .25s ease-out; - left: 0; - - &.active { - left: 50%; - } - - #sidebar { - left: -50%; - position: absolute; - top: 0; - width: 50%; - } - } -} - -@import 'code.less'; +@import (reference) '_mixins.less'; +@import '_fonts.less'; +@import '_colors.less'; +@import '_layout.less'; +@import '_navbar.less'; +@import '_footer.less'; +@import '_sidebar.less'; +@import '_site-top.less'; +@import '_code.less'; diff --git a/website/public/index.ejs b/website/public/index.ejs index 56e6bccc..2e00626b 100644 --- a/website/public/index.ejs +++ b/website/public/index.ejs @@ -1 +1,46 @@ + + + +<%- partial("_head") %> + + + + + +
    +
    +

    <%- title %>

    +
    +
    + +
    +
    + +
    <%- partial('../../README') %> +
    +
    +
    + +<%- partial("_footer") %> + + diff --git a/website/public/js/main.coffee b/website/public/js/main.coffee index b0c37701..6f391d5a 100644 --- a/website/public/js/main.coffee +++ b/website/public/js/main.coffee @@ -19,3 +19,19 @@ $(document).ready -> $('[data-toggle="offcanvas"]').text 'Hide menu' else $('[data-toggle="offcanvas"]').text 'Show menu' + +if $('#site-top') + $(window).scroll -> + if 70 < $(document).scrollTop() + $('.navbar-brand').addClass('visible') + $('#site-title').removeClass('visible') + $('#navbar').removeClass('right') + else + $('.navbar-brand').removeClass('visible') + $('#site-title').addClass('visible') + $('#navbar').addClass('right') + + if 250 < $(document).scrollTop() + $('nav').addClass('sticky') + else + $('nav').removeClass('sticky') From 0cf2f107f1ac2c128db8cd1e18d2312f5d31ee51 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 14:31:15 +0900 Subject: [PATCH 078/208] Fixed top page styles for small screen. --- website/public/css/_navbar.less | 20 +------------------- website/public/css/_site-top.less | 26 ++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/website/public/css/_navbar.less b/website/public/css/_navbar.less index 271afdc9..667b56f2 100644 --- a/website/public/css/_navbar.less +++ b/website/public/css/_navbar.less @@ -15,6 +15,7 @@ } nav.navbar { + .navbar-shadow(); .navbar-brand { background-image: url('../images/logo.png'); background-repeat: no-repeat; @@ -22,22 +23,3 @@ nav.navbar { padding-left: 56px; } } -body#site-top { - #content-header { - display: none; - } - nav.navbar { - .navbar-shadow(); - } - #main-content { - padding-top: 20px; - h1 { - display: none; - } - } -} -body:not(#site-top) { - nav.navbar { - .navbar-shadow(); - } -} diff --git a/website/public/css/_site-top.less b/website/public/css/_site-top.less index e117c250..2124ace3 100644 --- a/website/public/css/_site-top.less +++ b/website/public/css/_site-top.less @@ -1,6 +1,27 @@ @import (reference) '_mixins.less'; +@import (reference) '_colors.less'; + +@content-header-height: 300px; +body#site-top { + padding-top: 0px; + #content-header { + padding-top: 100px; + height: @content-header-height; + background-color: #ffffff; + #site-title { + font-weight: 100; + padding-top: 50px; + color: @theme-main-color; + } + } + #main-content { + padding-top: 30px; + h1 { + display: none; + } + } +} -// Top page animation @media screen and (min-width: 768px) { body#site-top { padding-top: 0px; @@ -36,15 +57,12 @@ } padding-top: 0px; - @content-header-height: 300px; #content-header { display: block; padding-top: 100px; height: @content-header-height; background-color: @theme-main-color; #site-title { - font-weight: 100; - padding-top: 50px; font-size: 48px; color: #ffffff; visibility: hidden; From 7c660bf13f72577ea1b3211b16ddc0838749b1c5 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 16:48:12 +0900 Subject: [PATCH 079/208] Wrote "Show and hide the ActionBar". --- docs/basic/show-hide-action-bar.md | 99 ++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/docs/basic/show-hide-action-bar.md b/docs/basic/show-hide-action-bar.md index 34a70c8b..12c281e8 100644 --- a/docs/basic/show-hide-action-bar.md +++ b/docs/basic/show-hide-action-bar.md @@ -9,4 +9,103 @@ which are implemented in the following examples. * ActionBarControlScrollViewActivity * ActionBarControlWebViewActivity +--- + +## Using the basic callbacks + +Suppose you've already checked the "[Quick start](../../docs/quick-start/index.md)" section, +you wouldn't know the meaning of the codes yet. +So at first, let's see how those codes work. + +### ObservableScrollViewCallbacks + +In the quick start guide, you wrote the implementation of `ObservableScrollViewCallbacks` (following methods). + +```java +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { +} +@Override +public void onDownMotionEvent() { +} +@Override +public void onUpOrCancelMotionEvent(ScrollState scrollState) { +} +``` + +These are the methods of `ObservableScrollViewCallbacks` interface and all the `Observable*View`s can handle this callbacks. + +### onScrollChanged callback + +This is called when the scroll change events occurred. +This won't be called just after the view is laid out, so if you'd like to initialize the position of your views with this method, you should call this manually or invoke scroll as appropriate. + +You would expect it to be called after `onCreate`, +but `ListView` (or other views) does not call back +scroll change event, so `Observable*Views` cannot +call this. +Therefore you should write like this: + +```java +onScrollChanged(mListView.getCurrentScrollY(), false, false); +``` + +I know it's a bad pattern to call the "callback" methods from us, they should be called by the library when they should be. However, we cannot improve this because this behavior depends on the view classes in the Android SDK. + +### onDownMotionEvent callback + +This is called when the down motion events occur. +This can be useful if you'd like to know when the touch (or dragging) has begun. + +### onUpOrCancelMotionEvent callback + +This is called when the dragging ended or canceled. +This is useful when you move some views when the scroll ends: showing/hiding a view, sliding a view to the anchor point, etc. + +## How it works: ActionBar animation + +As I explained in the quick start section, the main animation code is in the `onUpOrCancelMotionEvent`. + +What we want to do is: + +1. to hide the ActionBar when we swipe up the view, because we want to see the contents. +1. to show the ActionBar when we swipe down the view, because we want to tap a button on the ActionBar (it could be sharing the contents or going back to the former screen, for example). + +Either way, we should get the direction of scrolling when the dragging ends. +`onUpOrCancelMotionEvent` callback has a `ScrollState` parameter. This parameter indicates the direction of the scroll, so we can write like this: + +```java +public void onUpOrCancelMotionEvent(ScrollState scrollState) { + if (scrollState == ScrollState.UP) { + // TODO show or hide the ActionBar + } else if (scrollState == ScrollState.DOWN) { + // TODO show or hide the ActionBar + } +} +``` + +When you move your finger from the bottom of the screen to the top (swiping up), the state will be `ScrollState.UP`. So we can write `ActionBar#hide()` in this condition. +And this event occurs every time you scroll the view, so if the `ActionBar` is already hidden, you don't have to hide it anymore. + +Now you know how to handle the other direction. +Show the ActionBar when `ScrollState.DOWN` is passed to the callback. + +```java +ActionBar ab = getSupportActionBar(); +if (scrollState == ScrollState.UP) { + if (ab.isShowing()) { + ab.hide(); + } +} else if (scrollState == ScrollState.DOWN) { + if (!ab.isShowing()) { + ab.show(); + } +} +``` + +## Conclusion + +If you'd like to animate the ActionBar, you should write animation codes in the `onUpOrCancelMotionEvent` callback. +Also, we used `ObservableListView` in the quick start, but in this pattern all types of `Observable*View`s have the same behavior, so you can write exactly the same. + [Next: Translating the Toolbar »](../../docs/basic/translating-toolbar.md) From c59eeb5ea9e1344f1042d2233f64aa04d21bc985 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 19:32:13 +0900 Subject: [PATCH 080/208] Replaced favicons. --- website/public/_head.ejs | 18 +++++++++- website/public/android-icon-144x144.png | Bin 0 -> 7226 bytes website/public/android-icon-192x192.png | Bin 0 -> 7488 bytes website/public/android-icon-36x36.png | Bin 0 -> 2081 bytes website/public/android-icon-48x48.png | Bin 0 -> 2638 bytes website/public/android-icon-72x72.png | Bin 0 -> 3693 bytes website/public/android-icon-96x96.png | Bin 0 -> 4633 bytes website/public/apple-icon-114x114.png | Bin 0 -> 5698 bytes website/public/apple-icon-120x120.png | Bin 0 -> 5819 bytes website/public/apple-icon-144x144.png | Bin 0 -> 7226 bytes website/public/apple-icon-152x152.png | Bin 0 -> 7535 bytes website/public/apple-icon-180x180.png | Bin 0 -> 9698 bytes website/public/apple-icon-57x57.png | Bin 0 -> 3172 bytes website/public/apple-icon-60x60.png | Bin 0 -> 3254 bytes website/public/apple-icon-72x72.png | Bin 0 -> 3693 bytes website/public/apple-icon-76x76.png | Bin 0 -> 3673 bytes website/public/apple-icon-precomposed.png | Bin 0 -> 8062 bytes website/public/apple-icon.png | Bin 0 -> 8062 bytes website/public/browserconfig.xml | 2 ++ website/public/favicon-16x16.png | Bin 0 -> 1684 bytes website/public/favicon-32x32.png | Bin 0 -> 1901 bytes website/public/favicon-96x96.png | Bin 0 -> 4633 bytes website/public/favicon.ico | Bin 1150 -> 1150 bytes website/public/manifest.json | 41 ++++++++++++++++++++++ website/public/ms-icon-144x144.png | Bin 0 -> 7226 bytes website/public/ms-icon-150x150.png | Bin 0 -> 7576 bytes website/public/ms-icon-310x310.png | Bin 0 -> 22554 bytes website/public/ms-icon-70x70.png | Bin 0 -> 3627 bytes 28 files changed, 60 insertions(+), 1 deletion(-) create mode 100755 website/public/android-icon-144x144.png create mode 100755 website/public/android-icon-192x192.png create mode 100755 website/public/android-icon-36x36.png create mode 100755 website/public/android-icon-48x48.png create mode 100755 website/public/android-icon-72x72.png create mode 100755 website/public/android-icon-96x96.png create mode 100755 website/public/apple-icon-114x114.png create mode 100755 website/public/apple-icon-120x120.png create mode 100755 website/public/apple-icon-144x144.png create mode 100755 website/public/apple-icon-152x152.png create mode 100755 website/public/apple-icon-180x180.png create mode 100755 website/public/apple-icon-57x57.png create mode 100755 website/public/apple-icon-60x60.png create mode 100755 website/public/apple-icon-72x72.png create mode 100755 website/public/apple-icon-76x76.png create mode 100755 website/public/apple-icon-precomposed.png create mode 100755 website/public/apple-icon.png create mode 100755 website/public/browserconfig.xml create mode 100755 website/public/favicon-16x16.png create mode 100755 website/public/favicon-32x32.png create mode 100755 website/public/favicon-96x96.png mode change 100644 => 100755 website/public/favicon.ico create mode 100755 website/public/manifest.json create mode 100755 website/public/ms-icon-144x144.png create mode 100755 website/public/ms-icon-150x150.png create mode 100755 website/public/ms-icon-310x310.png create mode 100755 website/public/ms-icon-70x70.png diff --git a/website/public/_head.ejs b/website/public/_head.ejs index 48ec367b..98d710c8 100644 --- a/website/public/_head.ejs +++ b/website/public/_head.ejs @@ -5,5 +5,21 @@ - + + + + + + + + + + + + + + + + + diff --git a/website/public/android-icon-144x144.png b/website/public/android-icon-144x144.png new file mode 100755 index 0000000000000000000000000000000000000000..504f74db622050507115cee5a8a647185fad49b8 GIT binary patch literal 7226 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 bFDhN48t#lcq9d5LGYYUWw=-)o@k#j~ZT*6> literal 0 HcmV?d00001 diff --git a/website/public/android-icon-36x36.png b/website/public/android-icon-36x36.png new file mode 100755 index 0000000000000000000000000000000000000000..c770eddedd6b7ca7e7b713d90721afed0b16ab4c GIT binary patch literal 2081 zcmZ{k3pAAL8pmJEgz18kVH&w?A(i=N%nT+f7Q;+#qbY{iX2zJAnn7lS+AWvjjLwK8 zm)v5Tlp>eOu4I>xl!{VPyNfNCjdCf9#QwC-TBozuI_rC%?^*Br{GR{+`PR4Ao9g4` zwi1Ow0RUFg++BR(Y_=2{%iwn($jyZlg74(%1VByt@_9BA-m6G_-JC(yL+oGh#+d8w z>j}Uia{v;K0Wb$2B@6=)4FNC`0ssXTKt~o_XE?xxWt^REE?{x#Ionu%3O><{bZ1H6 z?EXEd?fbU@fXt$~I5GX3-{%C0ba(5v#Sc9cH7Jz2nok3cGMt<&QD$aV+Gfc~NCdv+ zSd04V)y3{Is*&;P>12V2{P>-Tgf6sdD#rASY3US0SG8+!ADn}X z4)YK0!Nd)ii+`)*4=#L|Exw%ia&+eUl@0C7`)w~3mSQX%aiwQ=xbJ*B`Y{$45mEeR%WW+j~TMC1%7nRUEPDCoNlFn*Q2bpfqWJmS|#J z$2k{hD=Un)BW|c)?b~M6SGhUBn|52UCZ&w85n|@>%fzOHle+pkKBknPb5}6)=^GqF zg^%huloSik=@p3{Rtb2slc*xiRVEr&!!m~O|f2-dzI%JrNy>R$my*c(@hrptcqS3ck}8* zVllse9{HSJ!)*Z)n9uE_8t?T)$e)bbf!JUOu{>3V|5; z#epk|i?Jgmd9lO3$8&tQn0`Etyuqo#Qtz0t91f1aldx)&(CY(Rl5fu0ADC1)+t>jE$;@la`Akn*T z{3JWral*hY{+q#CrJCR2v4u&?t?Ag(`ikRur!b5neyk;#!mB7aBb(9!?$cwx!cL9zL09s%e(aOh-*kxl*Za)fIKr1;p!B%gd$)Bzw^=odIvK z=esRc7}B2~^a<1UpeFywEmRyUaV}DORp?JI$-3P>G>L8dg^4)IIJ&oIdZ1yWHXFIh z==}UGrrv8AIhH=jMiv_CbYj{LXxx*A_U|jE861IHod)n_ax#0yoEI3nudRfjuo-@< zJT+=w9X{hQQcH^ED3O|@r#QVz10`|x^j1*|;hfS!g^N(|dM#g)Yjr9J2EhS0E6^wr z>-oLuF2({DgShqNOxyU8gi7!Fu8hh)QB_VnDsHpR%^m07v069pG&E7h#TvP*9j18W z{qKIHzT8*;TfJoP9*$ap1~6BE6Z$i114r`^XBQKtUc$-=o8vAgjejV;_?hs^PvW0_d~Ku8d`0cC>hiK@CEfJ2$!(_# zK8YC5pR%w$5t$U^o#II*@oP(HrZi*mQ-`B% z{0R4z>kS+AR!*49?K{--U*@f-7q^)s);7s-7iJG-O~|b&w%~H+zlc8AxtTBMUV|qU z+9ZZDd#b~@^QJVX|C*pu?U6Pun1JFE=KX}7J>do8_VUGN6{)Q3Y$OrHs9s?n^1MDh zpwhf!mHw^R(_a6!UVaw=5wgn{(-msxOEo2j(|F&O%Q~~hejSft!WZ=hsjI&%DTU$#qY{8GUu?krgZb`Bp$K?FzCa4__5>{u zl?*fJA9Vi1_z6R$f+gh1B@+!8G57)T6pDG$xJVxOJ{2x9oWH$xkJmqRJee>B@I)KP z)`o<)A^w?;u7MG)A9QvJ5Agux?3`7)Z455$`JuGimj=RwA|CwtIB~dC6v4-Zu@4Hl z5#bWte}4>)Od>%9h{VC$hjF-gh{WbX9G?BsvLkctL&?@~hc!Ik8sW4z>;P!4UM^M6 HAxVD&SEOxc literal 0 HcmV?d00001 diff --git a/website/public/android-icon-48x48.png b/website/public/android-icon-48x48.png new file mode 100755 index 0000000000000000000000000000000000000000..66e484a741f1ca6b6b53987c353b0ef3818ca369 GIT binary patch literal 2638 zcmZ{m2{e>@AIBfYzEg}rD9e>FGng?3!-z80tWn828wNEq)^Q0bWvT3>q8mj>i(bh( zmgBkzX+rj7OQws^b$K71_nf!)p7WmP_j`Wl|Nnh{-|zo9&w0*yQcpRa6cXb^34r(0yxUj6!biqC-~pgEQ*_6VA6)a$T~1m8)qP5f;6gRP#>D{uVl)9D zAsGO^gIfs;0Kf!QmV5yKn-2ic@Y}DPO~HnMzuieIU~m7r*I1qf?g)q5c+tUX^CM4m z2jc)hu$5qiccV0qtBWr|wm%06Sb68w(T=1cPc~s{F&+Wak8Hio}T)l9XFEqUeHK14{iML9=hEq*VP@k7 zEnt%n_BYO#ddE^X(F$=GGi{=g%x%4EJDl3S#5EBXmsV5@f+|>1N$EMqz8RG}DsxFB z=&1EaktdN+wSN8#3`ZNI)9)QHWSz(uV8?%Hq9 z6Doiv+YX11yIV{Sd*KA{=)C@tkXieT+Z}l<{6kK{0B*%y;(iIOYw(N@|Lc!zJ6V=K zO`m&{kxm(_v*d9T2CCc}1|#`#yJ^Yx#cRcSwz98Ph8JZfPR>#fZMsUR=z3xEs!mux zN`VUG>IU@H)q1~_X$mb&6MNjLA9TCkw0FIYw}SV)OC_h&^c4RpRo>gI-D!1K#;18! zZT*Y~DV}nLLwTpSI^T7mAu5E^m{v2Eu#E9;ff{^@vAA-`t*r(&Q<54hKZf|DN-@*| zB559N7Le#@~gy5+eXsUSe8j_-51|f zB55dnU7|>VVD;wAEV^i`=``S@^IHpKpk7Zu#p`ceYdS_$przw`cS zy4md5BC0n~!fs6c+K?#T?uiSwE`2qcmYZh@KUi&fWQ6%EqgTp2|9wuPc_*Yf?v|0_ zvkOJ{%Y6d+`&H)JJJ#L?Yx*2&P?M9E%>8B|qU=1K^6-L^;^gLRy-vr@9R6`zw&!)k z>!2G0ttd*BF}Drb6vq{w2>GK#-_;WHy+7;2-gQXrRY{4ea^>_17%yz=s#|dSEM}|8 z$z5q)@4oZ*2W&^#w&^Ta*8HX*%*(i?i7cT%E}()s_#oeueyy>Yl@@ExGMSA3LhbV| z?5yA*bt=^`YVcnaeYr}hQa7JUX^o_^GfLCjhjE!rEG0I_@ROpJ^v<^xv-R60$=wF! zQQPV92CMJ2q)o?82$H%*GUP$L_i_sKB8o5@-R>KuG0iNH=SKT zPp$Ez`I!F|@$0n)H0^}cd2D1Zr))1tq{|*7r=cqRK4|On2|50dA!gKqQ~#ef`RBtF zJbO2B7mUuLC9b^VJYHTVPxc9a8GR1ZXe$}|Wvg?jd1j-aM_wD}=u&piqg{pHV-ALX z2G^(;=XK!BP0vdn7HW7IIvYBMv=3jM`+zE1ytQYiPnBY)h8FQX^yt7ph0IV{$Erwqx&0;?xk5J(Q593t)0P3bM%Cv! zYtJ6Os-2u!7TB{!iB`5H7_*d4Uf)Pa-rSSrN~wpWP+eR_x@wbttK`GQ2~d2rO{R{g zbof->5OY(85N#L1B>HbN5qM z`@1;WixO<&3wn{HfKi%4>A~vRd-qedoX_3-dQJVg*X)A^>o-kC^Synj2H7d2z3)pM z-xp%BuivI=NR|Em)$>-pHsP#EObttgL!XT;pW z1PSADym0+Zl!fFQC$52Ov0nViD8Xxj&l6x%wp+K2mGX)Yh$S8^vt4Q&vcA$1s#P`{ z=)?al^wPX=y~pk^J@Gu<73HrN1dR6o4>TCoo{T_01`!)@kq8z*AAv;aBET?oLn5&H zD6E0uF$4mOKqR(~Apd8878Xb)MgRYSwcCE_;J}G}2YMKZ5$Si42oPv~WTJx~nHmtR ziqArOK6gg8)XL`HNt5%_T`cz7t;{HcfIKXe@2Kng$_8X$}eP)GyAnM`Och)DjV z<4TPo0tidX>{8rku;uYn%iVo{rh0sT! u{E@~a{{SQc literal 0 HcmV?d00001 diff --git a/website/public/android-icon-72x72.png b/website/public/android-icon-72x72.png new file mode 100755 index 0000000000000000000000000000000000000000..d127245eae9c35c68b9f5250e6761b86724e64e2 GIT binary patch literal 3693 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 Sk4#7z07G3Q{H3;I?0*6LR7h(8 literal 0 HcmV?d00001 diff --git a/website/public/apple-icon-120x120.png b/website/public/apple-icon-120x120.png new file mode 100755 index 0000000000000000000000000000000000000000..ff3eec0aa7faf23292248301f609d09b42fb4ae4 GIT binary patch literal 5819 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 V9N!gJf&ozh(7CIxiBh+T`WMy;)inSB literal 0 HcmV?d00001 diff --git a/website/public/apple-icon-144x144.png b/website/public/apple-icon-144x144.png new file mode 100755 index 0000000000000000000000000000000000000000..504f74db622050507115cee5a8a647185fad49b8 GIT binary patch literal 7226 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 literal 0 HcmV?d00001 diff --git a/website/public/apple-icon-precomposed.png b/website/public/apple-icon-precomposed.png new file mode 100755 index 0000000000000000000000000000000000000000..40454e05cbe976c41f066a91bcf1028091a92272 GIT binary patch literal 8062 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 tT~*6hO*hcjPfb +#ffffff \ No newline at end of file diff --git a/website/public/favicon-16x16.png b/website/public/favicon-16x16.png new file mode 100755 index 0000000000000000000000000000000000000000..cb5c8f81dc2dc472bc1fac187fe503a59cafc0d9 GIT binary patch literal 1684 zcmZ`%drVVT7(Wc1Jo=<97fNwxlsR2XOKE{BP6!C7MdXc$w3OQiw7sRhZRrCjP+H`n z%*iGUaPfsv1Rp>K$do@Mb22icGnvsp=2Qg1;fzaWX4t8k$V~k5J?=T@_kF+bocrxd zPfI5HG5r7l#MG3;3>YaM^YMm$9h;m9!!kv@I39rW2mJq)E{AI`Lq>7}=$m5Az(trW zB|{8=H5>qk6M&a+%kdZhBNu>K2>_zQ03f=~kzJbr_^3L4M^+L9_@yUP3$uud0>q*T zvTBL)0w(mzLZ%HPl@*29brg*>*osjxS)f@RP>u#$Gy!@A(~8k?CDW#3n94%z(6=E-Ouv3mYaIl1aso<`J@YP%Uatq9m7?E2i5q#A&AP%_imTq*s+A^=4X84kOV+_dL z?oA~433-G~LZU~biE+{X8wG@oLSkGDA%^cwB9cuiN<|sjs;1g8xP3Nl$n7v36RGfmUnGI@u}fJ%+&gjg-zg;u+`I5C!(&G}CKvO10iOI#AYoj8%iPn;q)@p1QFicB_+p&=u%3(VZsOmEqFf z71C6-vbmb+wg%Rl5w{g-um(5ULz}Btwm23S z7f1P-%isv=@r*RjY=2O^kkzZ+%4)=mb@{>lxANdJi#SdSqd0 zjgK(pjR<{)hV`A0(yMrTa^fxDQU7|l@YSW{8(>U%ZCkIEZH55v)WrC#pqnzumKDvB z7xaJmHE#Q9{pr$KX2%Y~r5|KYw_&g5xuGKagU9Fp_%y9s-$+3zP0gn_S3a5>evWYV zj)}JJBdrHJ!mow3b${93*7a5Ux8JmN6{W9h`P~LO_ys?Hr(Ay* zzROxXDGx7^;;2ZbM_~YwT%Lf)(F=@oZf^ROTY@>I-H8a2ftQ6eTYYEUgM5~V|^0q}YjMx_J&BnrZq&o`!Nwhfq)yy6%_Mg<;5}{S0I&fi_ut*#t3DxB|;9&;K2T9suw#U14vCu KOYBRK)cyzQ=UWN@ literal 0 HcmV?d00001 diff --git a/website/public/favicon-32x32.png b/website/public/favicon-32x32.png new file mode 100755 index 0000000000000000000000000000000000000000..bd8e204dab1c215a65f621b10cb81e64ca95924f GIT binary patch literal 1901 zcmZ{k3pAAL8pl6O8N|43XGW@{Y>w$-zQJI|C1%fHOm1UJ6P1}U#>9-t%(%6SMlPX) zP{>v(#F1N_By1#eQgS&ex$Gj9T(_0cL@xW&I%}QIUhAy)dA?_T@AG^9|My$(THkqZ zPghO#t?B>(Xj0rrG^iSFBz!aU6vf8Mp#tMNdpH9?ZHC4Qa})HgB%-+zfvWCplN%)4 zjphLWv8DhJe+~eap{@9j03ZPYU@8y*2zda26c#@4v4WJ8D8V zh6t)|-_oTs)Df~9rI4KIhudc_&8E>gC|R6B)ZH~h5DY)4+C{!sb&qkee2F%dN=_}! zYIu?Ct-fn{)X(=L!rOm8Rc8?mQ%k?wh4lVGFZYQ$yX1i1&f^0o{f37uMcoVXv8JB< z?fax~0@#($?Oq5NIQ`qe={KrtEo<+o70A@;$}n1o&$yC)m4Oqqd{xb3f}c?Iy2uuOyS^ z_-n{66i8@*02?BELHi~i$TZ1QfP@;JB6zAc*NcQ z*F#Cx&(nW?aU(?X{pbu_bUZuWKxd>T`bm43|LSgs9*JeSWN#4`E!63_ zi1uYzp0&nvU1Y4gMu*aIo)s__;HTaWC~Vmfb%IM;w&)ZLOaPynEi>SyR_w6RW=6 z(su`QI#p1hp&2&Ra)p{;gO`gizi)RS4J2d_*3E1p92s75c|U9p7Afo4-#)oVOO}TU zm@)&TW-ZUv8|L?f_cR~)XguDAW3SuVR7y;RHCn`eu@l}S1>RRCJ~R&;)GCtc~V znWpx0Yb6|;X(Waz{&lp`;Y-}Ju7_khV?ij&5rK+ z?MEhVdPf^|4oI@o>6N9VP$D0i=`&Z;GM^%aN0EG%?(V(A*TzWMm|7(1#sw`R&Oo_4 z|IUD6nWQKOyYHxW(>mF~=z1pnQXgtSqqGoFn($as$E0|_jz2a2q4tfJ;xm`zHDWV7 zK3qD;_TQoVxPuu~_*P|WOAD2CdwD6aGoHSCV7Z50wYUx+QP;oa_?z<9cxB_}nzdD` ziWayfdVPLyeQet6R6&NFV%AOq{q#)4#Hp^$k28gSWg5QPVeqnHsKfWl{TIUYy4N{G2c81gb)so8zKgx&4hLF*I2INk%wl6f l9Fq;QIQWgQwPxdktj(bbbLf0}yhSg_0icjQNmax^=^r(eFFF7K literal 0 HcmV?d00001 diff --git a/website/public/favicon-96x96.png b/website/public/favicon-96x96.png new file mode 100755 index 0000000000000000000000000000000000000000..0afa873a331b3e148196d72ec5d13e9e8af1993d GIT binary patch literal 4633 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 WJs|W%Mx6io1|tGgdj4H?xPJk(NSh@9 literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lFaYIf1Q>zlDg&_&kr<>Fqz48# zn-mGe|DuX zihdA37pNa(21pMs{RdA>_}@3f8%2Lhzs>)w3PG?K$P7~S|Azu(Gl(mHjX|Nkw! z+QFuP%qXlE!E684J)J1-pE%nGtUtRF;&*)dGt2n@Z{6Jv*PmG~^ncNsNTT%5TIvmU z;})oX5C;@5==wooxcm<@1Ev>UKP;Yc=?BS!%z&vw){iWPPd~CcTx>M`EV$I+5(nzX amK#BNf*1@`Mjh}U7!x3TfSEK5(+2>$p=I3w diff --git a/website/public/manifest.json b/website/public/manifest.json new file mode 100755 index 00000000..013d4a6a --- /dev/null +++ b/website/public/manifest.json @@ -0,0 +1,41 @@ +{ + "name": "App", + "icons": [ + { + "src": "\/android-icon-36x36.png", + "sizes": "36x36", + "type": "image\/png", + "density": "0.75" + }, + { + "src": "\/android-icon-48x48.png", + "sizes": "48x48", + "type": "image\/png", + "density": "1.0" + }, + { + "src": "\/android-icon-72x72.png", + "sizes": "72x72", + "type": "image\/png", + "density": "1.5" + }, + { + "src": "\/android-icon-96x96.png", + "sizes": "96x96", + "type": "image\/png", + "density": "2.0" + }, + { + "src": "\/android-icon-144x144.png", + "sizes": "144x144", + "type": "image\/png", + "density": "3.0" + }, + { + "src": "\/android-icon-192x192.png", + "sizes": "192x192", + "type": "image\/png", + "density": "4.0" + } + ] +} \ No newline at end of file diff --git a/website/public/ms-icon-144x144.png b/website/public/ms-icon-144x144.png new file mode 100755 index 0000000000000000000000000000000000000000..504f74db622050507115cee5a8a647185fad49b8 GIT binary patch literal 7226 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 literal 0 HcmV?d00001 diff --git a/website/public/ms-icon-310x310.png b/website/public/ms-icon-310x310.png new file mode 100755 index 0000000000000000000000000000000000000000..140fc141148a967afb6a4c63a38c0fe350a30fef GIT binary patch literal 22554 zcmc$_g;$er{60QvjFgh@mPT4Sq!Ab@C9T9jKt0)a@hG}WLW5SHZq0|Ex#*==b< z0Z+IN%6iHmP-PO)tqmUV8p{K!p#mx!VcZ5@u-a)t^*|thZV)Iq90a-n-U{9VfxJXO zpdD)v=*edgh{ip;$xsgX0BozHp$7VQ|DW4hlm@&*;I3)v0X%E|&qICCSpfukk)Wlf z{M>hLzx6tZq5qTcjr=4D8E3Br1L_l!+B`K!_x*C5ycVPEagOZa;%Lsb!{*3>*6*2- zpDpjQbt%l#1z-gY(OC^{CQT(v4;D#0rTlTmNS=qCV<7Bf7P3uk(nTYvC&8irkN;E< zVg8GtNh!RDuS#c4Sf<~sc!Jt5bd=fj&IB9(q<~Il?C~PxIDBvi{1=0%^ECC8mD#BM zSYsO`H}W1368foTf8?&E;^BB?(B=I268P|t?2*MG!E25PP9*i2X&s!GcbyJD=NPh9FB-cn}); zKI#D~V?*^MMVQHu-S*@o^%&KmzUphkg!;rD`**1XYpV1 zNP2sd_;QJnmakJ6R@FUTKq-UQLd1UlJfCF~k3-XP(DOcidt+NE5@e^F)hMpft$JB( z(4Zf0fuH{fqzZB(ExF#^svX&ngm6e0J9kr)M-+)o5g-4{?)wj)?OcPxJidn7geQU$ zB}NcNtrx-n;ClS}G|Ee~*zKT<>yx`0ZQRv&NKsobf0T_&w#wVW9ixQoZ7nWu2?gxQ?7%jJl8>c)tRD?GIlHajvYI|6Q zg26Rii`T1N7}@%ntF7fRrB%0Rr+Q^if^bnXFxGg^8O6B-*}EQfwLIj;h)OPZmy^Dk z$K+-JrBJEFLF5#jii@k$uqx!jle}#&23ZSvxO~cy@^NNhQ7Tp^U6QbgrkzJ68)jG7p!~wrTFF>#vQTXj9^W3DLif$oShXM;vV~<^&}bZj6YDZ$rhsS=4Bfiv(>#D8J)D ziBi>h4-6BztJQNlV?w!V8)^$3A8B7fCQg@nl}J<)%n}otbnn5Ip4#>IT<9y zsd8j0vRU7ICpqXqwlh9>fz-NLpXd`;Te~pFTTB#j_eH{QEtMJQ;P8_ow~OI0RV+3} z0uo0{ACFjKe}qhp>x*>;#ddJl3XepvVP)>NRCx~n!k6a!9K@BCxWVm5@e7ceojfey zue`b$(w>Bl=rd*BAN+MUG+lO-?E(JOcE*^k<}gC7fk(lR5*1^B#}uQfr}p{*pQaia z16Ihg@{F_}TEFNG)eC4( zjFjsK*`m>zkYQJv38Aknj915heP*v?5S8N{Mb>*l(dzFaxg@Yd=C~yeBYtWMvidQi zdbMCFY0`PfbZ%6h9<-!AO$kb5MROKxy56VWF+=u{@Qvi-Ku6b>MX}2Vet{R9FOW&D z6y~%jnV!PNf8%4;mF_-p?e=%)9Q2I0vg0*>S?RZXS-sG9$P&1=J}gHYHV}!XC_zMX zOXPmgDqW%>&?Jfeh|WBvco?(A==4La$-8Ih&kH-}X7=wA?P%^^dY|$bK~3lc$ly4~ z+%Dpp!DserpuE`Nihwp@?WZ;jorj!ydS3nLE&Yze8Ev!T>ryy03(1{z^TpAm#g9M@ zkDVU874G?#gj=z=)l|59c;52t=i@n|Z@Ix4S!i)kruREoY{J)o3~>xKtwqNd3nS;( z`noVfy&))um@eAje8S4-8#69fqJg40Z}Eq7G1d=hH(y%bQ4}LW{IDFY_pl$Hna^Eq zHAig6866Fqi{mQ)D=FCmS3Z>=Ro-=>kB$|W3rqpWUUqERX3TxDbo*tes!Z$WWs33K z+~9bCKys(u%tj(NgoBmsXlAXqEhPO9*rQ_|$;`-5 zwTt1;#`L;!lQX6%zVANCqGdor-w`&!C+mVYa8MT=rm#bX=W8>>+RDXS_z?bT3*~Ju6fO+ z%$qN{qF?j0xKU3vjVN?rLdP%gf00F#d@~UU6=e#p&Om<|QjU7zP0}jP+@SbZFdq++ z*Ui54RRpfns+{?+YMcE+Z(OnECC&;vy|IW zZi8Y9DVrxd)ry7PX+X-E8f?gn{zP$8naNetIT*|)-WdTNc{{G>YhogtT!q}YO)_u&j`2q717D4*D zKq{p*nF{5U^6f=g_LoWMwqGCaJB3n3~NH2lhA?f z=qhcPj0hF)x!CvF6r{b6QnC&xFL>OaJKHlLM1i*h)-gmUKrl4}O-DcBkO<1@^s7on zlc~b=yz=olKcdYZXim-wH90Cih3334 z8hQymUHS<`_Y3mnAwCEax6ooi`k(gJZ{)$bH&^V{g1O*xzVnFyWi#kd$jxk7o|5Ew z^Jg1JLGMQP&y z8#OK<1rJ=4>F27w>MV6!l-)}!&kMbku2JV%$UT<`yDE5Yp8JdzM&_i9fGP6j{y4b=0p}i2?NP;(PM=Ag8 zF}`JrI+Q~v@u|q6Hb?8AiVNJ+#~u1ok?B#MJoHpvv?|q%6s->=0?d1Ur-R2q`l78uM~BbIq9WE3XiU(=t@Ux-l@%2`zVzl0Ufw1X7#bh*;CqzW0!%w?6MND{Y z{eo!Rl9H5E#LuOG&LBkhls!N-q?ttpLcH}vh&X8_hbTit>@h6OFTXO+3JZ&vxJaEO zO9S7xt~KHlrgclO}Nt+>5Uak6!NWb?WEE0cK_V;*8S#tK^e!_ zU|djw=pN$h%HLrx#++EGShpD1@R%gYiAW9&5a&<7+-6N3jfzU{BWnY(% z^xmW-ws&IEyH`SM*=1o_U|dn9iI(-L{zz>71R0x#TMBda$k6+IxeP^vK8LD05U+)) zULAj8xO#mj1b_d5x;kxBQ1&4fuCx5seC;={0Tpl}%=oDxCUFxOdq!S`L3AXXG>M_($$QA#mYz{2$v`7XhD{~B0LpajnGr;c-e~aI3 zHXp7PRoA`zzk!z3()89D!ol!5Ss+@d77l~!jzktRgBX1yE zQR!IGnNgIF=+twjxacq#i``V3-$c!(z9`vo)gi;q_JAPF=G0uZF?)RL+>(UvZ{&0$ zL)lt@fP1#)if?yFy)cCUQz zNEI4HqZlaD1hruBkDg4%{oF^L2{Mq}gUvd>Ku7XNcxh&j2W)vEn&~m5J5{w)5`;SM zIcQ&Yzpbr_$Wxt1PJwT}pg~_^5^ROfrXw%>83eWVmwKIU7jpuDs|%IIC)nlUKs|Cr zBO3=#c|}ONxT_Tfgnw5+>$u;H(H0cL?YE0I{t3|+hV?ZRwdNaM0mx&hqbmEA8db1e zG=+}@59qqM2e6*E-#8Ik`m|ww{q(xUEaG1s^<_VWMX10=2%p$}W@hRhb#MzK4*g!R1Ie<;E0kAkYMGt)`~Oag@vYqx-KNtkJ>F9sZc>kF zIN_APcHat*{#~TbSH50IIq=rNV8>x2`SVQZFE==>YmPMdc%1CODzi^<;V+g#i)a$R zC>)j(O1|3H!x@`Nifv#`-)80?(|Qs!noEW|NP?%}t&_9-N{pQMarzku!bYKP?ZWJ>mG@Lc8<3nKp9NC)SsUjNw^E$}V)Rr9CA zH`)~lb0wC5BDiG6lrGGh!3g?nQIvA0NsX~{#@RJ^|0NF7k7ush&>@NwN^>3L#`<7X zUWb_xLmx(G;_44j>)ZQ2<`+i0RCs#bw`e@ac+;Xom0bHwXl4~>D2z8TeNV|0Q}c1d6@GP_PWF*0eoY`6<7Vuti3pj*SB4_2eGlSt6?E1 zQ99Z*9b=3_Y^&PZI_p7q zAf^}CTo+rq#_{=L2^#te4zpmB(Eso;+Y0-}o{nm-q={5;aA}-$ZnspC2s&gIPKL`*Pp)MY_}#~ zM|x{Oo8L19`D`)+YmX`+kgV|`!W|mj2fz^Se@Y!q3+h5F3)rn~b-dJhBx2fyU~l)NLR;0OS_Oa2(o$jG4j4YqVt zm4`I>P!i>I(>}aP!P^n)#1R+f?+PdNrPgyyB^T~gNxPo2pfpK@R6{;|&7|}#m>}6- z>c8xYlX!#DNdfgF}?Cb=Grd{9LvEe+lB78ZeKbmsw8?|y!_J&mRy ztY{5C>!H@vt|~{(4u07mzefp!L+N#_4ffxmdM-G2w_C?L?p93w3r- zZEoM1(iAK))nrP^^a%05via=oC(5v?>Ob`Yn&$goyb3~ z#9os2QcqlULKq&mI5kmd{i*{B>zpEQC+>GEpIU;+S2uc| z%SWQhOnHtEGKTc9Y}b#Bs=t&jRZw~HZi+-Ea!Rz%fZg3Y#92X(K%`^QzuB%`h?bk! z8BkwdDLI6T%BuV=s$cHacLi!Wm4@s`YrzCZ{?&GZpNK8lv*6G9-8X%HDD-HJ{NKgi z0xg)M$z5Z`00}79`RnNd6^bjYre|X*iKjVF2{cA$^WKs6Qg}AR*tM3~mx~y?EA|aR zIP*LeK2LI-N5g9#-Z{AI%XNM%BX`=8p&4OBSH)%L#lnqN_)RWbxZ|8!wnPI!v&s?+ z;o`zG#m;%m1NOWtb%?o{6*6WdnQsBgmCay{n>Y=JjCPD@jMf=N zJRSt*&xt4R%lb8clV5X~OBxIq7zf_ryvw0owUL5Omrww(sUoDh3N; z7NG1B#-n)n4Ngn}NGzfE`Ozn`hvwk0DKAYempq3{KO?-h1R;J4tzm+e-!5r4uMb@L zLW~!re|$WK1rUKZX;~P%{`?XpropG!^clIaQk3pEt!HR(cg3CNE)B)| zO?25Q;)*v3F$UnHb9u2bNq=LUsHH1HX|~wr-KL;I(2cyOUU$HRv8aMHnxYab9ersZ zL}R63)e+&rakV1nz58)eve+H>hBu6onU&*>K^R?Jgaa8J($>*F%VB(%N}#IYjV?VtU|le=Xf=9RGB zZLlRO6U1=d(6{PcA9L5( z=QLTDlz$wFSkA`h2<`g;5@qO*YD+lvd!d-`k6=Rir7t#sqMT$2IprQABNRN}jWuCA z#S9zf&H23&TO4!!i_#;#hmey0nc@|)TvtAIYtzNen_rz=QwG)@(Jq&_cWOF~ao0|L zZEMO8Ba<$BSy|B-c^)PoPyCS;)S~D?!huteKD~cSJLa{J{ke*!6%UrZmR0+3Z-!x$ z&MOIBrLl4%A1dt4it>9;1YxtzOt`D7Qe`V+!KlH4kv-G`X=h6X-S4aGC2N+O#S{K{ z9b%s2ltXNohA_YPx#6elvbP(99CCrx*kM^?(QJ3?Q|{8Pv=6&GO7`ooiZp_r_KM_^ zcLOL0WX?_lCLS`~&tov=8fMKDayEIwelb+!&ayd0az4Q){I)@-=qHy%km1yW63Ta$ zAk8{ew(o7p_I2r2DnND)Z(%Ysw`mFYI{4e)W0xZxd8JV=j$hl6SSeI<*77hoOaI88 zbN0?r)O_u8f;8*>ePMq4cc_T9Y9uy5WTMfGa0bg(z3)ew@8EmUxWu68bWsVOa?brD^77NN(ZAKfyEp`Y=}hqyN@dL=LR#u*+}xdOP@4Alc~e zzO}6p!e2imjZ!nOWleH18uK&hz~CYQC(ns-h!~uCr(wLXC$t=LpW8BH&CJl^KlQ%i zSI6iYRx4gbcKW5U=x6RY_!OEc`F81|^~L4yMv^F_g59%yL#RhQbi&Z<%Xq>v zmFG(a9TA}FQUuVHSqj_oTJ1A^t12T$5%44{#cNU^{>qInRqZFxLUk_aikA9>Aj}=x zGv^m%kV~OaYj(WB1acdDPsa8JKO*Ar(7vPd6K7z84)~p3SPJ*XX-!6F-tY?azfH>` z3vWW0*Q`%1?-VhlQf4JYAOF0Qlj!PM5xKM9$Yk>6vN%7jfcC|e>IGi!5x_6ekE!;) zGOKcc)j$4njGg+gfDaI>2Q70)Fx{mI{2na$diN+7(XU^_K)kBUdJ@>~-`RaYl&N&g?`5g`rs zi|&r!7u4|TBMS?RTsXGY2NKEdz!@B7F9 z^j*9>^)ccyQR)|op(WGDiU$k&@<-ae#jC&O_~8EnvD!bJK=dW2++USqEA_t~Ck3`z z;BbE>&S5#Pe$XNw%wxJ%i! z1b_3#HXeIAR(9K5QkJ<&IWf94Z6Ah#^l+O2X_QKx@jDKrfZ62Yt1A!vJVoi zO+H=N*%~lNmMHNu#KiBYFXmcLo%(Usi)#tuCuhyvLewQ6@jWh32&OyouYa^fA)Ke@^E9mKK>U2tB9}9bQBf@-1lO#E3rl|nl z#gzAfOtEbo(vn)3x6)wNJ25N~czjC$Z$5$mg#--V7ujezyVrjkTNjp8j(d$9dw>JU zrG1&Lt$XpBH|f?Bd-cAf&RSCtZuh!x!|J(5NxqR{En1FpAwrt(>uco3K^57`0xo$6 z(|c#9vlBg7FuG?-9*Wb5UH&Sw4;Tb$M~rkH-RIaJGmw-?%*ZHM0@QL5Z+XwhQ$S zVYw~m`hNhlTu;ztuK_LjV4EPZf#A0{21^EQSTM!p`QNK;Q z*Wm3FB=_ed`u5tQC=?V-?I|7@ltRz?s4`_Ofa=JkNDrol%NCrkjYnk8aCJV>zY@Ck zugRYhAOYW&^L%oa2yAbQ6}EuGG(J^*_%9NvuMb)^cM}-gKR=Lc3p}PO+^4$9RaLCt zU*oua<3(^ka#+2zykA3T*2?SPZU2*Zv7QgLf_|te+!9R~C@!8uKyQFvb|Cp(eRytuK)glq!$YI)?y)bCNHPGupI0=TQd`McbvO`B z{ZbuIGwY#!Y&%6e2{V~_r8+4tf}~LfEoC_YHSC^yAAhpD^V2CRBeTpEn1_0j(`Rlq&7sxfl4zy`NTic3 z<_OdtbWK%M%NGuU`~IO9B1Y)wYD#qXObnCU9-#gdUHE>OoU;}ZLq%x8MuwZtl#6M> z={`P3qsu{YX_;>(-+Hax8Ww~q3bl5*Ar$&HM-kRuZqtN4#D6uJyJJQ;bDhXj&G&o@ z8wS+bf<6$uP6NWk0LBY;S|`Yu(I3F8e8Yxn)Ed@$6s|N`Y~$pHkBzO`%T8?byL}dh zV&?e7_~Jibpce^$H5X#imt3+Qm88fn|#p^%uU**K2-w4O~Wv+N$b#-a=h(eek5 zZ_7T|J&-D-l-}-72qy=UyY41KQ`xGVW?vh&)b!)O?}ag+F!F&Hw*;_-o?y$4fD8u_ z2~Qz|KfhISZP67C7;1!|T%Q9`($VxJSN2OTNh;i*=StAi+C{c9*lIuMDl*w zyMOPm`uxH?7+>)DHq?a%`JIs6r_<1Wss9h7OIp(@IZ|5ij`Naht+*)4%b7KR2W9HT zGjyobaYIg1_cUanbz_PJh#~L#hdJa%&`h?GJghLJHta*-`OOOqXCjMLYY0G{XL7qv ziXS-+Kc>398T2ukG3nN-^d@ujk)3)U&#xM%|k`FcjtG>E%V_MZa?ia1098Iw+!k z5=kJ}&$#`vV@bNbk#qzPIK_~u_!1Mog1E4-tTxxaxV=CXz}psHfJ@Ecm4@7SAM~XW{C7Gm!`+1xpiVNl{KE1;o)Au^u45m{McpMmrBKFvLt^ zV|ecp+f%)IQ6<6)gdLC!Xl( zO(7T9KUk?QT3%bWROtP0schO{g(0ZuXYO%{xz7G`<}j6k>ePgqslEPm2dD?1ySYxt zZArmSFPhElv_reM6QsfFLh1oOC!&#nKRrW$FLK6;DLbOlCQ8WcStJ9>0wJo`gh z`g&bcJ5hIW=MOqEwSnd7L*|sJN&VfhAQh;E;D^d;JnTu1?f^}a&d5-;x8Ao4y4_`8 zmCV*OdKdG>xg_-inss57b`+Bb)SAP}ikhyCY<0STG?JW(Hd%=nPDU{ND;t}B^0Ay>y zYXMt3Z^U*#V5AiX2Zv#B&(VBhyutRc;PYQw%g7lp-XHRXRD_I+aRevB<~-eFHAQ_t zW8hL%(+fWw782o~iW6ERvU|8+y_4>OGiOzKYCwriaFfYcom=07eb31_#?3u z!xfDTGCy+Gc2vpW(|l{g#J*8;wJF_`(jxI@=E)%Idg6x%(v0&kOU8ar!9ey_<1&v@ zQNrgAxeq~t=k2nTtJQoT4GIDoQ|6MpC?UbgDsed%T&vpI`17-#D|>IlH-!M5Wo?+U z;_U@pU~!bTXFB~j{emYOPi5`V8;3W%5ND0CX_N9WQ4w)8l(7+P4AEHU-Z)H+MJ1vOL zw8xKDCidfWvuyv47|gUu$y$-D)Y60n*YwzPgzLSADyqiW5EDPab(zME8Aw>!HJaLS zkcGnpwPANeUhOJu9eS=>51B|6C0Z3ZQq3l^EAXM3rmW` zRfI0r>~+%6Q~mLVhd8+FUO`*YXwYsR8@8tf;rmlfSlLKH0le^cQ91=mW+gjpeq>^0 zKOtaj&PByYH-EA5rDE8;T4zRql54b{x~lSX<*!jmqHzn+NY;9TEM0^qEWw)yQpF`T zVq~_SXkNP?q$T(u`Rk0paNY~8Qan#bUI?EiuAJk@>W80STnY((#p3?ju70|oVwMW7 z*&GjX(HgF$3P(>(zY5b_MakK*p^#IfNbq{_jJ0svkPB}#N3B0!Ux#LtyEiW@TVuhw zOJm>nAYwc=B=wgfujnzW@Rc|lBzHB!%tH+DHQb*Ye37TcaBv~eMRuoyrv?Q1*MI7l z3PN|uj+Yamif_Vtdh9zf?iv`fp<=i{w6TV!e>8!>Mg#KuWkaJ-?vnOpUd-TqjF);u z^FJx^0`zWGHuBm~F-?lY^g5MZ4QoAqhH+gRqjn_rG`<4>P+DE|kN1x`>ybum^^u_l z+*EX#ha+{sqI7u#DETz~7_PM4DXvZi!v0Xmnd|xZ0LC*T4Pt zE-9zG`Fp?=r^IGJefGzkfvOW_ZQIjgcZEltK96i{0N=I<6{pao){x0sHhe|(mvmN7UNiJd1=0GY}9a|$XTo6aS^4+Dbv%c6!d^bQ|fzqkXB47X*&|!IF0^s z!3B%w5uE))fu$>q=8|%t^ii*#U~XTj!%NURk*M`OpSYG(nZD)d6MPBfI3YHDX&?3z zgI*{+>mdPrIv$zZXGSlFia!EeXi9mmw z)W1xk;s7fJd;HR*5v-G_eZ*VpuDACCH+JLjiTru>*DwgZ=;zRGs&6vuR@M@9X=YEl zryl}GJ=bKPGR~jq+dPIHhw&4fzw4O^>%|3KEIxAb($1WDmpJJlGrGtB)_pYJHb@5# z=GnxPel(CEZkb(Hoh_c-d}H9D%=VV>J6iZ0G96XJ=<^8cy6*k*-&X{VJ@>=M`k07% zEY6IKd}rS;tnY<%BI+q5agORl$f@_FGOWUthSI1(Zd7e4oE;hV3}kOE_~sSbLiNd0 z56-J#aVH&^cLY5LAfWa9`>@ZwF>2K@pU-jYzbcx3QD;~-yRP1;sQgw7Cgi>Q`iy>v zHKBbZ?Nec%Qm_FZmHWSOak#h#iE)45@%|0N?cJou52pQmTiP+~9QI~T)Su|Zbq>i< zMy=(WStv&`po*vbY#UM14OWio!!;ok6-)aOh1@UV2Dx{Nn(+SKSogbi0azWsU8eix z06ej7=P^t9*R|~_FhFd-g3L!#EEMKyb4-qUbB!k3?$PHXx^Zi-5&S~9+)kj>y)-P@*AR#YPzGe=>e^fj&Eh0!9)Mo`@8(SOYx|;FugG2jB=(2wb7+1p z+NlHc{As;l;Ez@{7s8N~5UiGaOUBQ|&hm}JmA)RT*vvNynT(8GVLDSBUdu|m0PPgx^u;ka3>or zAjRCZzLG46TI?Kk8(Et|KmB;tSA(YOj#%}$`t?%+!e$@|yYl}dk$&N0<31t89XmK7 zK{cRj@2aWq^7tC{h9BkUCRr|Mzapl#^f;{?^|Tmqp*BI=U0X>IC?13#6m*Go?{^p> zA;wLYl$4GJW_%?z-km4FeEkPwk$r<6f5|C|I#DZbEK=s|k)Y#HI~u^li)DQSNHs7t`D0CKb5xItmmFki^6!d>c*I zSAl(8CmL8X7^}L85t37Sn_|{-Gl-|5#c%DpgAJDqAo{m0&Sy=2L{fb` zqQx{SLu0hvK&3a$aZy+u*6%>Z8vVy&=9t^#Nrn_)ASbu6{U5F|WP}#B5Zoz~?(O6Ls>JM1 zy%H=$%80gl42xih(wdi+iFbA6I_}E%K=OV$mqM26hH;44Cw2`Yo}~Zw|MS|4U#)qG zS4#1c^upjz;jZky5=Qqss}`wN%zivQ;mU^&`t%0QxT!(b;Gf=9j%p1uq7jJM$WXv@ z5cwTpbAS1}KXmR8VruY_FhB;CpHz<1>P@8ltwfY*hOFv1qIMfPH%7%`SzBcLw~fV3RI00A(m#HXq(j(W;T zBq+FrMd_GdK?boR7fi5#ps>>Otbq>9Ac^|m|JMR2wO%_w>P|5IdU+`{tOUt*K^we6 zS60-<#IB0*_GrV{)AGC0DhmEJQjmS7W$aocB2n#J=%E4=^`<*d({*3x+1y&+5W*I4 zqdJU-;(&8-tq*TcPfxJG3KT{l6uW z;JL=9gO9_$I0G&W(tFR=CRm$E!sDBG^kreWk{xk8?K2XblE2N;sL9N}N6Bd3AxKnd z38FxS+(-CT7mr8%@zAwwgP0e21PGA59VHtvjvMr}b~6exofm_mus@HbH&d41^2-v2 zYUz1TC^JV3vY`g+^W;tM+_ePft$MG<^C$I#ny@ECo)~obI+1nvh7>O12-YV-IB;+p zeBRN6HvvZ<{O?|XTtKsv7S{!71S@n%UHdfYl{=R*9F~v61(<>otVR|)d8&Qx?Q1yx zn?^~|?q$-!G2^U}giq&ZWfb&V&qdQm<+Nd>>s)n8@~D6vhiMW#3*auMJhx+JzJ$Yk zTl5hi>j~G@i-<`kZvPVf$o?Zx%lf*JR_7Qs3qeSEkvmqlZrsW3Gvf{{_(!4(^-Ogja^uaR*uc!6{Ixb$m zP69gT)fthY{(q|gTU&QNlXJt}U&vhkI~P z5C;3-?U`uX@?3{<0Wm0K{5|wsZ00Ok7>e^=*@fh_9&8qcAjfR+p?aDAEPzp+nZ$)o zwufi9%Vnh|6vE{342*z(Pu5Ha`^P!*R|v&Yt2hZDDQB-OTgmr#O@Y4`VhIIP$A>T_ zSd)cH*60Ji6TlXERB|nQbv+MU=@C7pSLt?yDr?e8pab)O^Y4py1qC`gHT@6`&a5O7x1-v0?rwVH^ zG3QC{ZiC$8spHJlwT+{tKm21~z#}r>FZ!<63YiEG>g=1xm|pYJ($f~ZrQP%n&jSsu zAaRhX;>RdJH;gYaw^{lnEVuZE9!gGCm@D9QX_6cxsp0(L==FLg!26VdN~_JwMNE#q zcC>&JncZ=cg_BA1W*qkyOc#G)4DQf(Tk2XsMJ?M2{`qiq9oR^B4*t2=FOvc_ z*tzHH^Mn0kHo!wgfQ2Q5tMIiwjNguO?90@<(SB_}$Bxg-bprH9liB5Nr_!l~|6cwK zVT*qM(Z0uEoTvf7Ik=1uLwja(n7I)RGzHCg?s0C-VkId28}T(Ts^3kj!dL+>*c!S! zqc3=ux_5ufxX!ypkE=@}H<-nZAbT15#~D&XFO?|B?%g}rD55bFhZ4+r!YPeVw}_qy z`|Uxm5zroDVx6RmuxmG#D6k)(uEC*1A(c>qOQC0L#vVRLagFl;2257I-pRGiG+A2i zKD3nU&4?R4#>|rdU$6@g)Uz6-d#SiMKvLP8F9FrL`EYKDAr`DWU5%C4fL>u5uqvk) z8puo(<$?z@T#WHmlqW>xpUfSvJE)zXE{OLTND{lVRWvley>28((PQ<6Kvs);xfbMu z2oOJ)`tHY%MWPJoN3?Fs(tPJMfBA8<@#xX;4d2BO`PAr<8;AH?^rzy`*Yzwwm*_g@ zd763oXhxz1TS+3$j(hIZ9f8{5JA7WLU!eNA!*N31Qq24MtS$;CG#i1 ztYk&a43OFVvG7kjZG6)w*jQl^*aueGef=JQ;4>_aLF-bwkzO4C?WMVzc356Jo_cce z_Pm-O4ig=1+zZVP?AeH=un`>Zdoy5((Pfxuevff>Weoy|Z(?>4C1%G$=637`!dH2JSW&PgCbBBJ8)Kzr;G z-Yg~Am*OcD$u3099gyDbpA!Wh@6a*7Ewd?qMG9p8YEe3C>Cbg;mdaU8BPh-jQ7TR+ zor}oO#Oc@p{%Ud3i%oiDX@u4?$~yR+_At2ga9BG~@Q8N*$iw=nN-Lr1H4gCv zmuX@=^qG**ig$U6k*$s@hFDWLF0X^b!cgQ_4Nff2wScBx12-#q$UNM2xlAxW*EB#& z!uB&A!zJ!xC;Vre=YKN_u|QFigkRmcjZd1lb7gw}6{#g4z)v-l@b{ud=vB4v} zlGZr?8S!NKc#d@?*^HzF|L@i4ALFZ{2#qWF(1{U;r#&yEZITWpvus~g@NJ0=A24~8 zpA{g?q%;MQiWVHTH~7v`UGQt%oeh8jBDG9IaY)PxfayiELf-Rl!0QblE5M@nO zC3gItDdIN?aUQf*?rEUWt!)UC^TlyS?zy6+2kn)1+AmQGF z24qH`J`ONdp4aBF=I-g*rR372W|mvRT(bZ9Uq6H`ga>zwx-NC?tzSPUCgidJZae6R zp5sdrjifN8Fq7?}#U0Y$2U)fGC*I>!f7Y(FW~aWONxPN~bQdkYEDwxxEAGi$NBcLhoM7DGd&^*<`+CX}mhzMFH)gzm4HP(}MhMd& z)|wL}_A-V=TEEOJQvyWls1Fh2k9s(g|Qz2~UIVu|PlfVCqA z!Sh^~lK=1DtNl2ZjP|rVPvRgiP3*s5qTYc z3{PRCwB))%6>v|;oqa(Yhq4{9Sbomm#&c{}#oBUX%eA3ihpXK=z6%${TJKRKRmaR4 zV!Ufk38nY8arBce_xJu>zfjEDb+TNJb=8vMtleN^7s1-g`MRQ*0hlUOOc*xHlk`JH zzkFEAG8Bc~4_Eh_83ez9=-2(okAmc&5KeC$ecE^N0sWi1&V?B7af;aq#be*DZ*(I& zb`La5S0C73H|_&hT%99?q>EnyX7gRD1gdPi=8%pK{y#$amN8oMZh&O3=R!X49JBWo zNj?AZBf0CqPU#KZ$1gi}+!gOUga4U*u<)4^gr!@V(kn+*s~D_&iehjEoX(3p%CNg)7VZe=mRVgY4gvGD)^W@t@R&_|Arw6kXcj3;i+hQoC z@1JFAtCfEZ*yYI+Q@Ix$S6@6-0gdBf4+qJUDA&;*eVcX;1#-TU2dt^4n0P1gLFea=3a z%sI2qv!7?{P(=17zOhHiW2dqkF5`5UlOXq~kpkgTavRELe#oF1=XP%VwXJFTkAAs=0;x z%+~fz-y5zuHdvrq33L8=dt)fgfhX_DO&zy9?o4=F5nn+M)AvjnR9~zN!>O2uL%e&g z+x&4;VDQ$DIf%mu9gEd7gw%%xEk~B--ssH)l z5!KL$dl+=Q6X1AZLhIK-iU3nh`zW}65w%fbh6`kILS<6dkm*MmxPJtvj3~$#ug8j# z5jHyezKtU_7Xwc4GU?-y2v$IeT%bPPtIRZ}<#CRif z5F-L95jzJ^+0`q=S<&Wn zc1-3~D|%V6c)QnoL6bEi!pI{cgG#zmw@>?8ApCuL=L{4R(&;0mo2s+=9;vx{1qc@I zQtR*RbFAouZE3tM$CZoAV)kjg@m7z$Ng(ETT9LhDlJs9efZ8jXpqp-EAh zM?_ru;_7=~!%<-VH}~U^oN4)wQ1^~hg-R(5F%$7;*8qrShyMg=KCxMyhs!}1;}1^O z^kJgcqP(wZhfQSzFNfj3)-P$s+N~KMu`q29I~OLceeTdWtv-CRn5sET`97-Uv3-tx zGO-OnP+Wkp|LAe_^c_}}1p|VPr3tl_lWl8nz6I@t_`-J7R^r+@%+A&oiq9}dwT5Et zBN~$#o&i-by_}AQ4-dSe>AQ4|k4usjZJo7N{*XuNL8Q;A_1-(m#hYo?@CgjJt+Lfbv^DAQSTW2W;@LL_U}p71Mj!+_SQ5WHvnsjW#?`=7!hEvPfl|K zNX7TwmqXS4(bLPoeb}ydvv{i7pUcSz0Db_oYxVb%^oyne>!44^XKOdV^$M}gc;p3x#!iPbrmlk`NE3Q|G{_!0d&&^3g$ z*Vee{-e0Q$-#G!=)eUsj^wK--KY;IZVnmvXD&n&mQMn%b0mA@x$Ov*o9< zWlZgFsrGQ!h5Sn%ZZ894NdQ+okXDf zXXXeVa-kLUc|P%K_Q(A$XTiRE=6NynM`nN5D7`?#{xysqwZ>|aVMGI&;( zy+VH?z~(;xS{R%cCiDkrf)depe+Vf%4Fa18fE}nqEIBLok=8`?+FEuY30}KCtY*0S z;c?bn%$Sgx&w^L@1`~#35EcXS3k;aZK3lT$B8M7jfcq8bMC{s@r2P5tLo^VH_cGg0 ztxkM(-!s=6O`dZOqBB&^zOhIrn|{T#0)Xrb$@7)7AR)#^ zm4&8#-kH??XT`5X6Z8MZ0*`%>q=c>~s5JdVq5<~}7XyRd&qL|*>}KuzkE5mifh0-T8c664mF3eMq}_pQa?Mo0QITrqDf`8d)7J z-I8f<71VvUI`p@w6>nZxN*MbOFC@YONw^!Bk@pnBMnmG?8JPn5@m&%ryRs1AXXcNM z6Bdc+@dYoN8}d4E(8iG59_i-;TODNmc_iWmkWHjeSgPN~+STh$0J3-6*F~VQiP}L6 zV-H&(q4@}aQ;fB*CJ@?m(U|q~Y$ueAYNA7}t=KDC>*g+l%P4c9Wm1%+Cmol|qKUx0 z`v>wE;mO*0P{Dbs`8v77wW_p6o6YMXwLf78>#^Wv#kdnY)*n}hP9)DQD7d9DsKFm{ zvYoK!OQls7IzoZ4%aS}|I+HinPbx$Uog|-a&KTAjYHCvP6ep$2p&$u^JN8y|XCwl= zW{j_|DqIRa|ExPpj^dUTSTAg@KA&cd=wvB50j#*c14+;1Hb+d3B1hLxa_$rRw?hs56PjbRCD;6A->)ir}Vh6*kdPD_I~soJ!qf-jyq>y zY676q10^0hH|<1cQUFh=hAFFkuqe{8*s zI$(>#)|yBjFXLu-zTSU5)rDH1}|{Np{wPBR~1lPqHH6@{1U|` zpa9T3{r4>KFvDdGH@m&c!B$9z2>KVIpXlZn#b6HggleZ9K2BntD#K6gh>PA%- zCPz{iz~O75+0;PL^`z=)EdwW*jRvaPeAry4QoW+KnY%-!Z=}bR0U0YvQ6ZhihThP; z)UAmKJy8$NV>~7S$_!%dK(TUl32RL2Ru+n637X)z+M@k(C@L>{_uiE;g&WAl8>vfF{P$nP*Xs%^6JO;XkD?Q7xj7rBr&%q%ZE~NNjbEv zLi8t5iUw0bn|3BO4(hsg7n(|{u-*6SH$FBJ!3{x*UyGt#3bJ1Po++dQ5lbP}os_un zQP~WITgrWqO3lwoRfKjIVM&H}*QFPM4TD38BxR(kXpl}wVXGC~-6Kc!{$-Av(h8S{ z-(D7qMBx3lSdMkh5X1_py?0(SMn|?B8`5IytL9)9$$F4ar3Lh)DbCg9YcK;-uH>JA z0R;tEhqS(xm!FyI=!lJoa^NldIjJ(fPHFVx%}a_{;Yh+RFD^gOyqQvho}lQFb_XFF z)){DC8qxks_!o!{g%wr(64kfhT8rY;3$UzS7Mf^H>7IC~&HBOR-jv8M*A->MrsO^y zoaCYhzD@f{O^w2@@0nNarDoG)%qk0)az6vu!D>Y*093H6?@U^V$G(jR)_UCT-j8rf z%bI6p8DH|4YMpo^f=!iqt&9@M19-1Oelj!&}u?2o#h=`|ei z!lJHNv41G+K9{_Efi&^CgghtR`&3GdY>4Y47G5NeNTDDtJQ8X;vx#pNs{BV@Y9Fk> zHl$F}ZUs0oS}?l23JeQ=`Fww22W{V!)VKej`JlyFh<5I+Ixj`YyNK0R$OZ2mkRR;J zn#wQf!@f00B9H96jsob?mbS>N_()u<<9d{!$f^VhYR|Q>W8oAcy*Yey^zQ}MoCxd(Haq0karP0HZbM0jLo^`ssp8W;(w`w~mmBXIlPj0q`DR^nk=Bg8!2hMpS!zgJMn!wqR_i{pVLpuU(T0Z$iKdEKj8jUn>5GwxLmp2xsH!$ z-ypij?3-^5L-D6S%ymp>wAak#zTYEW=d1KE3wXfZQu8tz+vlqy3zBVdVyv+{@d_ls z`SjbXbXE0Bwrw{;$^vHBVEdgjuu#7(E&i0VWUN8Vwbf)pM3oL7?Ng4%@Ov`OZA@b) z)WKSZ?*G{_<7ue$ecH2q)$c5^q_5$V)~}ZI8TF@6^V~{&n2Nk9sQE#ZODwZkk@w3e zvxzI|s%53{+V{+*@wq>NAtAoaYq+0jbq%cpa>Kj`^md}-){8iLBF#{@OXB%?j)v|J*^eu1Mb7uO=gb+o-yo9m zwmLaj9{}Y|;2lBN&MsZIYhdZJv=b>|i>nw>gZHvk6pWbSdqq3JQZfccMtJSSVEadI zgOii4gF?BcYVNiE>|B7_a=Eud+-jZozVmD_fc64&HjBGMTY#%l{q}yPopLM1b|eM)FF zbNq`~*5*@mT)gMJ{igF_Bs%|BQPq!lvAn|q<%*|6>Q;G$)V({)(yE7C#|?07Y@FvL z;D@)WTrE94 z8C2q+jLz492u%e_CAM6$t zrQ0QO)ocjYXX-iZy8FK*=oN4qqOFKRK$V!Dpb37yH3sZEIkpIy#I`191j2KMNR2s= zNr+x4Bdy9Rq`xCMFuCy)|U5e9(* zhl-6NR6_}-p{ybYg=#>dgpSXO|EqzozdP15;{R^2p6{9lG`M`^A;{k|ILtNB1BCK* z_3|)v^}@ROh^xxO<<%hS;($w)BbSOw;+Bf4;@-i*0U8PlVPRqN*duG;3s}qh2YS&G zmVgdH0!KaCc!&66G&J=6-9mgl{DL(!ECACWkYf2^tHOWS{Owai!`lPv+2C55#Ry(dsGSmeqK8Dwr2m)kEsn{ z1yWQ|hQgI$ipnbA{yJR;n4J8#eyp*P9w4ZmURKGaMc|U{zg@a&g#vkE@gBg>SMc=< z#`}9Ic)EsS-TeK66#nOrQBZ}!ph{2}Mp4}p+E3W-3vYw#SE-Y8U`8wP*|TsvK;#aGF53RiDXoN~96tcpTge}JaR`nC98T(jD}u-6yr@Idl1!jOZ7GeM_>G6DvVt*B2(A9*?d zUYIBY5u#^r4D@c5G~8&}6o!MxQ^p~?r`N++v6s6i;<0R|nz`NlADq1G( zXX0=l{CNFv9R1eR#LSXM=-$iKoDd->NJG)gRLsRIylGQd_%!v!OAIn|YZSx6r(;9k z-4zDySauh5z2iwbL^a(%v_i@QGLVIkm=~iOSrA)3H}* z+K+~cWubY)QC2!PF_(3FMr1_seh8b(9B;GzhkcYDAqgps z8#GL3Xak;~Cc8}vKP$+`_)d?rYiwH)%t%w#Q6)&Ep)e}e^(!4V^dP= z|2(;%{7dUiMv^T&uR7<>LOO@OQfxOHzs(C|wYa0;YqCQ7 zR1T#tIq3Us(#a_Tc;7eeV@%L)x$jNOf8(>4iItL!|cI;>mR((>sDTNn* z-0x3218^Owysz0I<4IHMGf6|c&{A2chq}wd+1=5(`g_{YpNSrMW6B0eR%2%48fwhs(-(Wj8sZFyMBbzhsDk~u|sYGPp4;K-+>EFt#3jS8J zcU&?&>P;0B6Il}?gtpNccu0ad4kRdseOO4`T^M5QY&hl)4iP1ekM!gR|04W6Kn+OI z4#ExOSEi>Q4j`8wsVoss>w%eWvKVG9v z{ug?nqp=2(S+%jOx_uxUmVZ?obb{!6D!J5*YyLxf!0`kQ(Fn?-ZW{aq zG~z(=#9WSB*o;nb*|+l5mBe$Mx4{f+UMyD}^lZd{gZ#2ae8uI;Aq6Z~P_} zhab%oXLSvt@CK@Ods4V{VA!ZOUb<;}uZWdpt~8|1kB|i7jg;!~raiQ-He6fGyZ1f< zs&?gUCxxqe zcx+K>1@t>VF3mu*Ls-0Q5;JjMo!%Rs5UkSX3eCkojT$DoiNU_PfI7dkk@&6*1iZLM z8REnn%zKKDV^}_b4ae>-4juH-?;S|7vv=&)K3(t{)j&S{R7X1h&4mSW1#+HqVFAhi zOxHT?hjlgKNvwTZZ9(c&{)BkbA_#3!4Ha)wR}*c2Dl>mAD43A*sV+|72Hk8BP%D0` z`Ps{4xj@pO%IgE>bv=BL>!L2!l~YUlpfVT{qd$D3MUCfLo(LoR@zeQ65i_kLG`_xQ zMaT7529vY>LKf-XHtltOPJ$Fm=`3U?;X?OZifq|zs@O1c_50}hfdSoeEQ#EwDkeBs zO%*iWru=?x(7554(_vKB80{hgCvWXToTuL1{ew|wA8Qp#Bn7ARp6D%~Eh{gxzkxQ& zz3bG`f3C@UKY#YK(iDf4>@lsX%Sh$cGaa}c?sTq+pwYP!E56d&g4vDn(5>q6os5iG z8&zWSi=Rt!kHlsurM$x~t}^IA%RcV-De?ghc(Q$@(JGs(JjSFYU)otfE`wB2JH zXgVhbDUt4s zp`6_nzu=V}oEv(SkNE*i08FTXE7$75;gc=9zaBi+&u2c&NvoO*F+^PdGP>e+Fk20E z?d7^!qM?qao_8fndVf$=vx?iwL2{UG>~%)euh`f+|4$~DO-BXz|L&)nsvNo#a-sCp zYjwE))yl|Kxz-|ZPZ?;>@=`_gEg7^sh_PVl|hd z*-LChuk;UvM68z9HFVhv51{XMRt+#@s-fe)c9Ki}uG3Ql7wA#zXUWq(toe-*ry_44 zGm4H;9OnA?No%uwOSPAdU_As_=eMZJZkt1;Y5tNU-MbN`C-R1bYr8nku-sw6H(q^F zlD;VGfq9NMXOE4E9a$S61URzI?Z243C%E=1nD<+dqtZ4fl{#s&)(=Y;VR#tWh^La& z{gu5x5@OJMOa;vVyef}eKBko(a?vm(K+PP zLXX1`<&v(h8-x3PTY?P1SR)?q$u{Ad)!@?+>Dx3T4$cvMMoFeUcO+DGtK*IoH-GB< zo}6QYokgI49aC}7&;9TtLNmz;oco@h^f0~ByCqEs|NPR|z5UHj8~qKcRC{dxX)l8j zo&kC0u3`H~HTh^E5|G?BiKwc8(Ik&=0h{;Y;Ki!7PlvoL{la0uTO>Imb14 z?3X|r&8Lw&n|a}8U#&kGJ3?WdHUpseqOA|*{@FJPZK&JF*l5QcOaiXK;hDKDgzs&S zL)+Eprbg^JSsh*z1N=SP0vFCbs95Cb0x^b-0&`-s&O$dw^2i%QjB%>}Z*_u7o@qJ&!$UB5+( z(*b=@KiKh*$^2Sx#UMe+c)5u?>5wF-JAy>VB~D^%O!_SiB<4Xu4g~m>_WgyECsWCS z5k}!Am*e4KS}y(Pk=#`eDTKx8d!=sIpWWvn);@gwXsMrzX$v3 z56d*^)<{D+Up@1e9pa;9>2sarLq<7J$;<#KAdt%E5KL1tMIulN$|yx8Sp))wKt#S6 zMgE@xH;?NS$H4!;U?ZE9%q-CU+ritz(Z`=eB?G!{Bqy=~$%*3NBBcUXg{z%YmtwY5 z`r8tzAZ3hHk#hF&@kGhX`}_ODDSv6EGikU7)oDCtnJEZ}{#7(}_I0yIp|BnfzHVf9 z9~6qfM7;rI;h#qN|6u;R`#BkBT<6jw5H`|@t?KCT{4@{S}wii3x{xBUMejJ%4nGC~2NY>!lT sw0A%vlt~T Date: Mon, 4 May 2015 20:15:07 +0900 Subject: [PATCH 081/208] Added and fixed meta tags. --- docs/_layout.ejs | 4 ++-- website/harp.json | 2 ++ website/public/_head.ejs | 12 +++++++++++- website/public/_layout.ejs | 2 +- website/public/index.ejs | 2 +- website/public/js/main.coffee | 2 +- website/public/thumbnail.png | Bin 0 -> 13523 bytes 7 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 website/public/thumbnail.png diff --git a/docs/_layout.ejs b/docs/_layout.ejs index 744e2c00..601a77d8 100644 --- a/docs/_layout.ejs +++ b/docs/_layout.ejs @@ -1,7 +1,7 @@ - + -<%- partial("../_head", { title: title }) %> +<%- partial("../_head", { title: title, ogType: "article" }) %> diff --git a/website/harp.json b/website/harp.json index 7ae9c6ef..8b74c9fe 100644 --- a/website/harp.json +++ b/website/harp.json @@ -7,6 +7,8 @@ "githubUser": "ksoichiro", "githubRepo": "Android-ObservableScrollView", "githubUrl": "https://github.com/ksoichiro/Android-ObservableScrollView", + "ogType": "website", + "ogUrl": "http://ksoichiro.github.io/Android-ObservableScrollView", "copyrightYear": "2014", "copyrightHolder": "Soichiro Kashima" } diff --git a/website/public/_head.ejs b/website/public/_head.ejs index 98d710c8..62efc590 100644 --- a/website/public/_head.ejs +++ b/website/public/_head.ejs @@ -1,7 +1,17 @@ + <%- title %> | <%- title === name ? description : name %> + + + + + + + + + @@ -22,4 +32,4 @@ - + diff --git a/website/public/_layout.ejs b/website/public/_layout.ejs index ad1bc928..b1072230 100644 --- a/website/public/_layout.ejs +++ b/website/public/_layout.ejs @@ -1,5 +1,5 @@ - + <%- partial("_head") %> diff --git a/website/public/index.ejs b/website/public/index.ejs index 2e00626b..477bc326 100644 --- a/website/public/index.ejs +++ b/website/public/index.ejs @@ -1,5 +1,5 @@ - + <%- partial("_head") %> diff --git a/website/public/js/main.coffee b/website/public/js/main.coffee index 6f391d5a..def91a9f 100644 --- a/website/public/js/main.coffee +++ b/website/public/js/main.coffee @@ -6,7 +6,7 @@ $("a[href$='.md']").not("[href^='http']").each -> @.href = @.href.replace /\.md$/, "" # Insert subdirectory for links -base = $("x-meta[name='base']").attr('value') +base = $("meta[name='base']").attr('content') if base != "" $("a[href$='.md']").not("[href^='http']").each -> @.href = @.href.replace 'docs/', "#{base}/docs/" diff --git a/website/public/thumbnail.png b/website/public/thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd0e7729530b31de82fa42f8924d4cac7db0ce4 GIT binary patch literal 13523 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 SfL|#=>+Lt&Wv|(L^8Ww`6!EzL literal 0 HcmV?d00001 From b80c85ab7ceb94f96a474cebdb9b55546be68b8a Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 21:49:29 +0900 Subject: [PATCH 082/208] Wrote "Translating the Toolbar" topic. --- docs/advanced/sliding-up.md | 4 + docs/advanced/viewpager.md | 3 + docs/basic/filling-gap.md | 4 + docs/basic/flexible-space-toolbar.md | 4 + docs/basic/flexible-space-with-image.md | 4 + docs/basic/index.md | 3 +- docs/basic/parallax-image.md | 4 + docs/basic/translating-toolbar.md | 195 ++++++++++++++++++++++++ docs/quick-start/dependencies.md | 2 +- 9 files changed, 221 insertions(+), 2 deletions(-) diff --git a/docs/advanced/sliding-up.md b/docs/advanced/sliding-up.md index 21494d15..88e676ba 100644 --- a/docs/advanced/sliding-up.md +++ b/docs/advanced/sliding-up.md @@ -10,4 +10,8 @@ which are implemented in the following examples. * SlidingUpScrollViewActivity * SlidingUpWebViewActivity +--- + +Comming soon... + [Next: ViewPager pattern »](../../docs/advanced/viewpager.md) diff --git a/docs/advanced/viewpager.md b/docs/advanced/viewpager.md index 54a3caf9..64372d2a 100644 --- a/docs/advanced/viewpager.md +++ b/docs/advanced/viewpager.md @@ -9,3 +9,6 @@ which are implemented in the following examples. * ViewPagerTabListViewActivity * ViewPagerTabScrollViewActivity +--- + +Comming soon... diff --git a/docs/basic/filling-gap.md b/docs/basic/filling-gap.md index ae2688be..460ff44e 100644 --- a/docs/basic/filling-gap.md +++ b/docs/basic/filling-gap.md @@ -16,4 +16,8 @@ which are implemented in the following examples. * FillGap3RecyclerViewActivity * FillGap3ScrollViewActivity +--- + +Comming soon... + [Next: Advanced techniques »](../../docs/advanced/index.md) diff --git a/docs/basic/flexible-space-toolbar.md b/docs/basic/flexible-space-toolbar.md index a0439b12..d51ac08a 100644 --- a/docs/basic/flexible-space-toolbar.md +++ b/docs/basic/flexible-space-toolbar.md @@ -6,4 +6,8 @@ which are implemented in the following examples. * FlexibleSpaceToolbarScrollViewActivity * FlexibleSpaceToolbarWebViewActivity +--- + +Comming soon... + [Next: Flexible space with image »](../../docs/basic/flexible-space-with-image.md) diff --git a/docs/basic/flexible-space-with-image.md b/docs/basic/flexible-space-with-image.md index d888d95f..ffff7323 100644 --- a/docs/basic/flexible-space-with-image.md +++ b/docs/basic/flexible-space-with-image.md @@ -8,4 +8,8 @@ which are implemented in the following examples. * FlexibleSpaceWithImageScrollViewActivity * FlexibleSpaceWithImageWithViewPagerTab2Activity +--- + +Comming soon... + [Next: Filling gap on top of the Toolbar »](../../docs/basic/filling-gap.md) diff --git a/docs/basic/index.md b/docs/basic/index.md index 28739702..84896817 100644 --- a/docs/basic/index.md +++ b/docs/basic/index.md @@ -4,9 +4,10 @@ This section explains the basic scrolling techniques. 1. [Show and hide the ActionBar](../../docs/basic/show-hide-action-bar.md) 1. [Translating the Toolbar](../../docs/basic/translating-toolbar.md) -1. [Translating itself](../../docs/basic/translating-itself.md) 1. [Parallax image](../../docs/basic/parallax-image.md) 1. [Sticky header](../../docs/basic/sticky-header.md) +1. [Flexible space on the Toolbar](../../docs/basic/flexible-space-toolbar.md) +1. [Flexible space with image](../../docs/basic/flexible-space-with-image.md) 1. [Filling gap on top of the Toolbar](../../docs/basic/filling-gap.md) [Next: Show and hide the ActionBar »](../../docs/basic/show-hide-action-bar.md) diff --git a/docs/basic/parallax-image.md b/docs/basic/parallax-image.md index 22c096b5..cc3601e5 100644 --- a/docs/basic/parallax-image.md +++ b/docs/basic/parallax-image.md @@ -6,4 +6,8 @@ which are implemented in the following examples. * ParallaxToolbarListViewActivity * ParallaxToolbarScrollViewActivity +--- + +Comming soon... + [Next: Sticky header »](../../docs/basic/filling-gap.md) diff --git a/docs/basic/translating-toolbar.md b/docs/basic/translating-toolbar.md index b2f7ce23..64d81186 100644 --- a/docs/basic/translating-toolbar.md +++ b/docs/basic/translating-toolbar.md @@ -10,4 +10,199 @@ which are implemented in the following examples. * ToolbarControlScrollViewActivity * ToolbarControlWebViewActivity +--- + +## About the Toolbar + +In this section we learn how to translate the Toolbar. +Toolbar was introduced on Android 5.0, and you can also use it on pre-Lollipop devices +by using [v7 appcompat library](http://developer.android.com/tools/support-library/features.html#v7-appcompat) +of the Android Support Library package. + +## Design of the examples + +The existing examples above, `ToolbarControlBaseActivity` has most of the codes to avoid writing duplicate codes. +If you use one of them, you don't have to use this structure: extending Activity is not required to achieve this effect. + +## Create layout file + +In this topic, we use `ObservableListView` and `Toolbar`, and wrap them with `FrameLayout`. +`FrameLayout` and `RelativeLayout` are useful to translate views inside of it separately. + +```xml + + + + + + +``` + +## How to translate the Toolbar + +The basic idea about showing/hiding the Toolbar is exactly the same as the ActionBar. +However, the Toolbar class does not provide any convinient methods like `show()` and `hide()` which the ActionBar class has. +Therefore we should implement such methods to translate the Toolbar. +Our goal is to make the following codes work: + +```java +@Override +public void onUpOrCancelMotionEvent(ScrollState scrollState) { + if (scrollState == ScrollState.UP) { + if (toolbarIsShown()) { // TODO Not implemented + hideToolbar(); // TODO Not implemented + } + } else if (scrollState == ScrollState.DOWN) { + if (toolbarIsHidden()) { // TODO Not implemented + showToolbar(); // TODO Not implemented + } + } +} +``` + +## Using NineOldAndroids + +Before we begin, you should confirm whether you're going to support pre-Honeycomb devices. +To translate the Toolbar, we would like to use the [Property Animation APIs](http://developer.android.com/guide/topics/graphics/prop-animation.html) +which are introduced in API level 11, so if you are going to support pre-Honeycomb devices, +[JakeWharton/NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids/) might be useful (although it's marked as deprecated). + +In this project, all the examples use NineOldAndroids. +So if you don't support pre-Honeycomb devices, please replace `ViewHelper.methodName(viewObject)` to `viewObject.methodName()`. + +``` +NineOldAndroids: ViewHelper.getTranslationY(mToolbar) +Platform API: mToolbar.getTranslationY() +``` + +If you use NineOldAndroids, add an entry to the `dependencies` closure in your `build.gradle`: + +```gradle +dependencies { + compile 'com.nineoldandroids:library:2.4.0' +} +``` + +# toolbarIsShown()/toolbarIsHidden() + +Now let's start from the easiest part. +To avoid redundant translation, we need methods to check if the Toolbar is shown or hidden. +With the property animation APIs (or NineOldAndroids), we just simply check the `translationY` property. + +```java +private boolean toolbarIsShown() { + // Toolbar is 0 in Y-axis, so we can say it's shown. + return ViewHelper.getTranslationY(mToolbar) == 0; +} + +private boolean toolbarIsHidden() { + // Toolbar is outside of the screen and absolute Y matches the height of it. + // So we can say it's hidden. + return ViewHelper.getTranslationY(mToolbar) == -mToolbar.getHeight(); +} +``` + +## Implement showToolbar()/hideToolbar() + +Next, let's implement methods to animate the Toolbar. +Before thinking about details, write some pseudocodes to simplify the problem. +To show or hide the Toolbar, we just need one method to move the Toolbar. + +```java +private void showToolbar() { + moveToolbar(0); +} + +private void hideToolbar() { + moveToolbar(-mToolbar.getHeight()); +} +``` + +This should work, if we implement the `moveToolbar` method correctly :) + +Most of the animation codes are combination of property value calculations, +and I think it's very hard to keep these information in my brain or imagine correctly. +And this approach is useful to implement the complex animation. + +## Implement moveToolbar() + +Although we named the method `moveToolbar`, it's not everything we need to handle. +In ActionBar examples, not only the ActionBar is moved but also the height of the view (`Observable*View`) is changed. +And we need to implement this behavior for the Toolbar. + +To use the changing property values, we can use `ValueAnimator`. +`ValueAnimator` has a callback method `onAnimationUpdate`, and we can get the animation progress from it. +`ValueAnimator` itself does not animate anything, we need to animate something using a parameter of the callback. + +```java +ValueAnimator animator = ValueAnimator.ofFloat(0, 100).setDuration(200); +animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float value = (float) animation.getAnimatedValue(); + // You can do whatever you want with the `value`. + } +}); +``` + +In the example code above, the local variable `value` changes from `0f` to `100f` in 200ms. +In this case, we should change the `translationY` property of the Toolbar, +and change the height of the `Observable*View` like this: + +```java +private void moveToolbar(float toTranslationY) { + ValueAnimator animator = ValueAnimator.ofFloat(ViewHelper.getTranslationY(mToolbar), toTranslationY).setDuration(200); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float translationY = (float) animation.getAnimatedValue(); + ViewHelper.setTranslationY(mToolbar, translationY); + ViewHelper.setTranslationY((View) mScrollable, translationY); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) ((View) mScrollable).getLayoutParams(); + lp.height = (int) -translationY + getScreenHeight() - lp.topMargin; + ((View) mScrollable).requestLayout(); + } + }); + animator.start(); +} +``` + +The `translationY` local variable changes from `ViewHelper.getTranslationY(mToolbar)`( == current translationY) +to `toTranslationY`. + +To translate the Toolbar, we just call `ViewHelper.setTranslationY()`. +And to change the height of the wrapper view (`FrameLayout`), set the height value of `FrameLayout.LayoutParams` +and update by calling `requestLayout()`. + +## Avoid redundant animation + +We'd better check the current `translationY` value +and if it's already equal to `toTranslationY`, stop the animation. + +```java +private void moveToolbar(float toTranslationY) { + // Check the current translationY + if (ViewHelper.getTranslationY(mToolbar) == toTranslationY) { + return; + } + // Codes after that are omitted +} +``` + +That's all. + [Next: Parallax image »](../../docs/basic/parallax-image.md) diff --git a/docs/quick-start/dependencies.md b/docs/quick-start/dependencies.md index 7d2d5208..9d6ba48f 100644 --- a/docs/quick-start/dependencies.md +++ b/docs/quick-start/dependencies.md @@ -8,7 +8,7 @@ In Quick start guide, we assume you're using Android Studio. Write the following dependency configuration to your `build.gradle`. -```groovy +```gradle repositories { mavenCentral() } From 5a5f601fc7c00b353525effd5d7eddb26c0a5da7 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 22:33:23 +0900 Subject: [PATCH 083/208] Added coming soon comment in the docs. So exhausted... I need time to complete these documentation. --- docs/advanced/sliding-up.md | 2 +- docs/advanced/viewpager.md | 2 +- docs/basic/filling-gap.md | 2 +- docs/basic/flexible-space-toolbar.md | 2 +- docs/basic/flexible-space-with-image.md | 2 +- docs/basic/parallax-image.md | 2 +- docs/basic/sticky-header.md | 4 ++++ docs/contributor/ci.md | 1 + docs/contributor/release.md | 1 + docs/contributor/update-website.md | 1 + 10 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/advanced/sliding-up.md b/docs/advanced/sliding-up.md index 88e676ba..6e2a7084 100644 --- a/docs/advanced/sliding-up.md +++ b/docs/advanced/sliding-up.md @@ -12,6 +12,6 @@ which are implemented in the following examples. --- -Comming soon... +Coming soon... [Next: ViewPager pattern »](../../docs/advanced/viewpager.md) diff --git a/docs/advanced/viewpager.md b/docs/advanced/viewpager.md index 64372d2a..a991173b 100644 --- a/docs/advanced/viewpager.md +++ b/docs/advanced/viewpager.md @@ -11,4 +11,4 @@ which are implemented in the following examples. --- -Comming soon... +Coming soon... diff --git a/docs/basic/filling-gap.md b/docs/basic/filling-gap.md index 460ff44e..f9d744c7 100644 --- a/docs/basic/filling-gap.md +++ b/docs/basic/filling-gap.md @@ -18,6 +18,6 @@ which are implemented in the following examples. --- -Comming soon... +Coming soon... [Next: Advanced techniques »](../../docs/advanced/index.md) diff --git a/docs/basic/flexible-space-toolbar.md b/docs/basic/flexible-space-toolbar.md index d51ac08a..a41effd4 100644 --- a/docs/basic/flexible-space-toolbar.md +++ b/docs/basic/flexible-space-toolbar.md @@ -8,6 +8,6 @@ which are implemented in the following examples. --- -Comming soon... +Coming soon... [Next: Flexible space with image »](../../docs/basic/flexible-space-with-image.md) diff --git a/docs/basic/flexible-space-with-image.md b/docs/basic/flexible-space-with-image.md index ffff7323..e258884f 100644 --- a/docs/basic/flexible-space-with-image.md +++ b/docs/basic/flexible-space-with-image.md @@ -10,6 +10,6 @@ which are implemented in the following examples. --- -Comming soon... +Coming soon... [Next: Filling gap on top of the Toolbar »](../../docs/basic/filling-gap.md) diff --git a/docs/basic/parallax-image.md b/docs/basic/parallax-image.md index cc3601e5..191e0e45 100644 --- a/docs/basic/parallax-image.md +++ b/docs/basic/parallax-image.md @@ -8,6 +8,6 @@ which are implemented in the following examples. --- -Comming soon... +Coming soon... [Next: Sticky header »](../../docs/basic/filling-gap.md) diff --git a/docs/basic/sticky-header.md b/docs/basic/sticky-header.md index 9a7a829a..17fcfb1e 100644 --- a/docs/basic/sticky-header.md +++ b/docs/basic/sticky-header.md @@ -8,4 +8,8 @@ which are implemented in the following examples. * StickyHeaderScrollViewActivity * StickyHeaderWebViewActivity +--- + +Coming soon... + [Next: Flexible space on the Toolbar »](../../docs/basic/flexible-space-toolbar.md) diff --git a/docs/contributor/ci.md b/docs/contributor/ci.md index 1c85bc5a..f861d522 100644 --- a/docs/contributor/ci.md +++ b/docs/contributor/ci.md @@ -1,2 +1,3 @@ # CI +Coming soon... diff --git a/docs/contributor/release.md b/docs/contributor/release.md index 8efbea5d..8f6c3fa1 100644 --- a/docs/contributor/release.md +++ b/docs/contributor/release.md @@ -1,2 +1,3 @@ # Release +Coming soon... diff --git a/docs/contributor/update-website.md b/docs/contributor/update-website.md index 8f20da5c..575838db 100644 --- a/docs/contributor/update-website.md +++ b/docs/contributor/update-website.md @@ -1,2 +1,3 @@ # Update website +Coming soon... From 8f44f632805a53b8fdf8942b74c6936b9829e85c Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 22:33:56 +0900 Subject: [PATCH 084/208] Added missing link. --- docs/reference/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/index.md b/docs/reference/index.md index 605f98b5..5e29797a 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -1,4 +1,5 @@ # Reference 1. [Supported widgets](../../docs/basic/supported-widgets.md) +1. [Environment](../../docs/reference/environment.md) 1. [Release notes](../../docs/reference/release-notes.md) From 3d48f2e0dad2feadad60a7908f188241f67585e3 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 22:34:24 +0900 Subject: [PATCH 085/208] Changed logo image to SVG and adjusted padding. --- website/public/css/_navbar.less | 16 ++++++++++--- website/public/images/logo.png | Bin 1078 -> 0 bytes website/public/images/logo.svg | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) delete mode 100644 website/public/images/logo.png create mode 100644 website/public/images/logo.svg diff --git a/website/public/css/_navbar.less b/website/public/css/_navbar.less index 667b56f2..7e031f8a 100644 --- a/website/public/css/_navbar.less +++ b/website/public/css/_navbar.less @@ -17,9 +17,19 @@ nav.navbar { .navbar-shadow(); .navbar-brand { - background-image: url('../images/logo.png'); + background-image: url('../images/logo.svg'); background-repeat: no-repeat; - background-position: 4px center; - padding-left: 56px; + background-position: 12px center; + padding-left: 64px; + } +} + +@media screen and (max-width: 767px) { + nav.navbar { + .navbar-brand { + background-position: 4px center; + padding-left: 56px; + padding-right: 8px; + } } } diff --git a/website/public/images/logo.png b/website/public/images/logo.png deleted file mode 100644 index 6c9f489badc03abffe749c40308d84ccf1bd8475..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1078 zcmV-61j+k}P)F6XU=)mkQ7{Td!6+C7 zBOCnx|9?IRlb{|9?!JC`pT9K2fD*fzsNr}Zl>ow|cmfO<85#aFGJ5fsXXy-x2ap^O z10;EZk%93aBO~JC&wl05;60HEW4gFL~=$Yd{AovTV058!b;NuFS0 zWc&|x{BNj7tb}Uwm1yb#bjP1~aQ6a?{ruC17o>QC5$plDgkCGbOU)6@^7vZ&kIIO$dxMLn&?G#VDvLgmC=1 z+={A;xs|mz91o!x8QJ9f8kwoqm`k^IMDhUBUu4Jc$|%ki<>8fyGqFsZ8XRMZ9s^9^ z7(h5)noq!6f|t+ta$a>44#$H60E8J7Cp1&X1IU0GSgOF1%ER~XKE3$-@exce!oW0r zR%o0JI60w~LU6~UnuF8vAP@h&UrZU;3TPlFg>{EM$`V>-2~ z`PrwBTd3uD8pipEpv&@zA=2GH~ zus1Q+*LC64m6SN1DjomFIc? zc>wH*gC{<6Ia+-MIzWhCj;E>zK;YYfmY`y~>!cPy!Q{4k#PwYSXh1=N{0KfOG>HxoP2ex_AHtz}-M#Pm0=( wr<(^r0Mrcx^`xljc)9~vH*gdn0e}Dl016by0^RUx#sB~S07*qoM6N<$f=q<%jsO4v diff --git a/website/public/images/logo.svg b/website/public/images/logo.svg new file mode 100644 index 00000000..3ee47e47 --- /dev/null +++ b/website/public/images/logo.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e87d95fc08e25f9908c9738c8a6d55c43d4d15e3 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 22:40:45 +0900 Subject: [PATCH 086/208] Removed unused bower component. --- website/bower.json | 4 ---- website/public/_head.ejs | 1 - 2 files changed, 5 deletions(-) diff --git a/website/bower.json b/website/bower.json index ee307f89..68a83f0c 100644 --- a/website/bower.json +++ b/website/bower.json @@ -5,7 +5,6 @@ "jquery": "1.11.2", "bootstrap": "3.3.4", "roboto-fontface": "0.4.0", - "components-font-awesome": "4.3.0", "highlightjs": "8.5.0", "respond-minmax": "1.4.2", "html5shiv": "3.7.2" @@ -20,9 +19,6 @@ "roboto-fontface": { "main": ["fonts/Roboto-Thin*", "fonts/Roboto-Light*", "fonts/Roboto-Regular*"] }, - "components-font-awesome": { - "main": ["css/*.min.css", "fonts/*"] - }, "highlightjs": { "main": ["*.js"] }, diff --git a/website/public/_head.ejs b/website/public/_head.ejs index 62efc590..7e801dd8 100644 --- a/website/public/_head.ejs +++ b/website/public/_head.ejs @@ -12,7 +12,6 @@ - From 4caccd974a1222eca40ba6c1512d06d7d7a79ac8 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 4 May 2015 22:45:54 +0900 Subject: [PATCH 087/208] Added html5shiv.js and respond.js. --- website/public/_head.ejs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/public/_head.ejs b/website/public/_head.ejs index 7e801dd8..7da4ee6c 100644 --- a/website/public/_head.ejs +++ b/website/public/_head.ejs @@ -14,6 +14,10 @@ + From 4f38842ffcc78fac1a4dcc5470be614f1eb325c5 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 5 May 2015 01:00:39 +0900 Subject: [PATCH 088/208] Adjusted font size and space. --- docs/basic/translating-toolbar.md | 2 +- website/public/css/_colors.less | 3 +++ website/public/css/_fonts.less | 17 +++++++++++++---- website/public/css/_layout.less | 17 +++++++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/docs/basic/translating-toolbar.md b/docs/basic/translating-toolbar.md index 64d81186..8d681874 100644 --- a/docs/basic/translating-toolbar.md +++ b/docs/basic/translating-toolbar.md @@ -97,7 +97,7 @@ dependencies { } ``` -# toolbarIsShown()/toolbarIsHidden() +## toolbarIsShown()/toolbarIsHidden() Now let's start from the easiest part. To avoid redundant translation, we need methods to check if the Toolbar is shown or hidden. diff --git a/website/public/css/_colors.less b/website/public/css/_colors.less index d5ccfa83..04fa3e1e 100644 --- a/website/public/css/_colors.less +++ b/website/public/css/_colors.less @@ -45,3 +45,6 @@ a:hover { a:hover { text-decoration: none; } +h1, h2 { + color: @theme-main-color; +} diff --git a/website/public/css/_fonts.less b/website/public/css/_fonts.less index 318ffb30..d7cd0c79 100644 --- a/website/public/css/_fonts.less +++ b/website/public/css/_fonts.less @@ -5,12 +5,9 @@ body, h1, h2, h3, h4, h5, h6, p { font-weight: 300; color: #212121; } -body, p { +body { font-size: 14px; } -h1, h2 { - color: #009688; -} h1 { font-size: 28px; font-weight: 400; @@ -22,3 +19,15 @@ h2 { h3 { font-size: 20px; } +#sidebar-main-content { + font-size: 16px; + h1 { + font-size: 32px; + } + h2 { + font-size: 28px; + } + h3 { + font-size: 22px; + } +} diff --git a/website/public/css/_layout.less b/website/public/css/_layout.less index 748e2c45..18932c58 100644 --- a/website/public/css/_layout.less +++ b/website/public/css/_layout.less @@ -22,3 +22,20 @@ table { .container { .override-container; } + +#sidebar-main-content { + h1 { + margin-bottom: 16px; + } + h2 { + margin-top: 34px; + margin-bottom: 18px; + } + h3 { + margin-top: 26px; + margin-bottom: 16px; + } + p, ol, ul, pre { + margin-bottom: 16px; + } +} From 81ba41d8fbd43de8f9997aadebacbeeaf0df55eb Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 5 May 2015 01:02:51 +0900 Subject: [PATCH 089/208] Fixed email link not working. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 42b7f07e..4cea8a8d 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ and [the documentation](docs/overview.md) for further more. * [My Gradle](https://play.google.com/store/apps/details?id=se.project.generic.mygradle) by Erick Chavez Alcarraz If you're using this library in your app and you'd like to list it here, -please let me know via [email](soichiro.kashima@gmail.com) or [pull requests](https://github.com/ksoichiro/Android-ObservableScrollView/pulls) or [issues](https://github.com/ksoichiro/Android-ObservableScrollView/issues). +please let me know via [email](mailto:soichiro.kashima@gmail.com) or [pull requests](https://github.com/ksoichiro/Android-ObservableScrollView/pulls) or [issues](https://github.com/ksoichiro/Android-ObservableScrollView/issues). ## Contributions From 5327c0fbc277a35eabadcbef57d78f35ecaea456 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 5 May 2015 01:38:04 +0900 Subject: [PATCH 090/208] Wrote "Release" document. --- docs/contributor/release.md | 87 ++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/docs/contributor/release.md b/docs/contributor/release.md index 8f6c3fa1..fca348d3 100644 --- a/docs/contributor/release.md +++ b/docs/contributor/release.md @@ -1,3 +1,88 @@ # Release -Coming soon... +This is just a memo for me. + +## Bump up the version + +Edit `gradle.properties` and commit. + +``` +VERSION_NAME=x.x.x +``` + +If you set the version suffix `-SNAPSHOT`, it will be handled as a snapshot. + +## Commit changes + +Before the final confirmation and release, make sure that +there are no uncommitted changes in your repository. + +## Confirm test + +Check if all tests pass at your machine. + +```sh +./gradlew clean :library:assemble :library:connectedCheck +``` + +## Upload archives + +### Set the credentials + +If this is the first time for uploading archives, +you must write credentials to `~/.gradle/gradle.properties`. + +``` +NEXUS_USERNAME=xxxx +NEXUS_PASSWORD=xxxx +``` + +### Upload + +```sh +./gradlew :library:uploadArchives +``` + +Or you can clean, test and upload at once. + +```sh +./gradlew clean :library:assemble :library:connectedCheck :library:uploadArchives +``` + +## Close the repo on Sonatype + +Open [Sonatype Nexus Professional](https://oss.sonatype.org/) on your browser, +find your repo and close it. +If there are no problems, repository will be staged to the URL like this. + +``` +https://oss.sonatype.org/content/repositories/TEMPORARY_REPO_NAME/GROUP/ARTIFACT_ID/VERSION/ARTIFACT_ID-VERSION.aar +``` + +You will receive an email with title "Nexus: Staging Completed", +you can know the appropriate URL from the email. + +Set the URL to the `repositories` in your `build.gradle`, and sync. + +```gradle +repositories { + maven { + url uri('https://oss.sonatype.org/content/repositories/TEMPORARY_REPO_NAME/') + } +} +``` + +## Release + +After that, click "Release" to promote. +If it's processed successfully, you will receive an email with title "Nexus: Promotion Completed". + +It takes 3 or 4 hours to be synced to the Maven Central Repository. + +## Create tag, update synced version and push + +If it's successfully published to the Maven Central Repository, + +* create a tag like `v1.5.0` +* update the `SYNCED_VERSION_NAME` in `gradle.properties` +* push the master branch and the tag to GitHub From e2de3b5abba9234eb65b9f3f2c5f8d1d523b4c99 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 5 May 2015 08:33:02 +0900 Subject: [PATCH 091/208] Updated secure token in .travis.yml. --- .travis.yml | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4c3a6117..94976e96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,33 +1,33 @@ language: android env: - global: - - GIT_COMMITTER_NAME=ksoichiro - - GIT_COMMITTER_EMAIL=soichiro.kashima@gmail.com - - GIT_AUTHOR_NAME=ksoichiro - - GIT_AUTHOR_EMAIL=soichiro.kashima@gmail.com - - secure: JvSTH/5Jl7x3IwCvErTKoW6AjGr03MLwmJwiRQ8sDeEITeLS45EJCnpzvOZXIaxY98Uhl9WqYLYCkUWPkM6yqFEsKUEXKYFLkDoec3ek8GKUkiGWSsV5jCEkVBx8RqLWGbpSg7U/0Ua2doZqIYkBEJ1t5wKiANrt+q/04+jpXWs= - matrix: - - TEST_TARGET=android - - TEST_TARGET=website + global: + - GIT_COMMITTER_NAME=ksoichiro + - GIT_COMMITTER_EMAIL=soichiro.kashima@gmail.com + - GIT_AUTHOR_NAME=ksoichiro + - GIT_AUTHOR_EMAIL=soichiro.kashima@gmail.com + - secure: HvFN+OoRKWHErd7V9ROy0jfoGJ/xPvyaGfhG1lsyxmq9TVAHl+I+t14+gsU8A51PJQWU5FQTG/XDYaFyuY7kDybd5VhZXijy8gT8Yge01TE0UhxHmpMTlP2HI+X9Of9P77PBNnH0oYLVYLYp1x/Nw5JUJ5SfirHnuzD8NDFuQgY= + matrix: + - TEST_TARGET=android + - TEST_TARGET=website cache: - directories: - - website/node_modules - - website/bower_components + directories: + - website/node_modules + - website/bower_components install: - - true && ([ "$TEST_TARGET" != "website" ] || (cd website && npm install && cd ..)) - - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=build-tools-22.0.1) - - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=tools) - - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-19) - - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-21) - - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-22) - - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=sys-img-armeabi-v7a-android-19) - - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=extra-android-support) - - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=extra-android-m2repository) +- true && ([ "$TEST_TARGET" != "website" ] || (cd website && npm install && cd ..)) +- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=build-tools-22.0.1) +- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=tools) +- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-19) +- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-21) +- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-22) +- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=sys-img-armeabi-v7a-android-19) +- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=extra-android-support) +- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=extra-android-m2repository) before_script: - - true && ([ "$TEST_TARGET" != "android" ] || (echo no | android create avd --force -n test -t android-19 --abi default/armeabi-v7a)) - - true && ([ "$TEST_TARGET" != "android" ] || emulator -avd test -no-skin -no-audio -no-window &) - - true && ([ "$TEST_TARGET" != "android" ] || android-wait-for-emulator) +- true && ([ "$TEST_TARGET" != "android" ] || (echo no | android create avd --force -n test -t android-19 --abi default/armeabi-v7a)) +- true && ([ "$TEST_TARGET" != "android" ] || emulator -avd test -no-skin -no-audio -no-window &) +- true && ([ "$TEST_TARGET" != "android" ] || android-wait-for-emulator) script: - - "/bin/bash .travis-script.sh" +- "/bin/bash .travis-script.sh" after_success: - - ./gradlew :library:coveralls +- "./gradlew :library:coveralls" From bddffc5db0d9d6e5b05b64c8fc476338f5c8f1cb Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 5 May 2015 08:54:21 +0900 Subject: [PATCH 092/208] (website) Changed git diff-index to git status --porcelain to also detect untracked files. --- website/gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/gulpfile.js b/website/gulpfile.js index a400c279..f9494f7e 100644 --- a/website/gulpfile.js +++ b/website/gulpfile.js @@ -112,7 +112,7 @@ gulp.task('deploy', ['git-clone'], function(cb) { .on('finish', function() { gutil.log('finished to copy'); - git.exec({args: 'diff-index HEAD --', cwd: paths.repo}, function (err, stdout) { + git.exec({args: 'status --porcelain', cwd: paths.repo}, function (err, stdout) { if (err) { gutil.log('Failed to check diff: ' + err); throw err; From 76584b113db35e5035ff4260ea4721c344196c9a Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 5 May 2015 09:09:17 +0900 Subject: [PATCH 093/208] (website) Added analytics tag. --- website/public/_head.ejs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/website/public/_head.ejs b/website/public/_head.ejs index 7da4ee6c..6be35023 100644 --- a/website/public/_head.ejs +++ b/website/public/_head.ejs @@ -36,3 +36,11 @@ + From 2163005f0d42f8bbe8712f437a4a8df7a79ee4fa Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 5 May 2015 09:10:41 +0900 Subject: [PATCH 094/208] Restricted coveralls to android test. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 94976e96..7e4aac34 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,4 +30,4 @@ before_script: script: - "/bin/bash .travis-script.sh" after_success: -- "./gradlew :library:coveralls" +- true && ([ "$TEST_TARGET" != "android" ] || "./gradlew :library:coveralls") From fe6cd9f79851786cd19a1206071303a0b2a00c17 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 5 May 2015 09:46:51 +0900 Subject: [PATCH 095/208] Add sudo false to use container-based infrastructure. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7e4aac34..261a6266 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: android +sudo: false env: global: - GIT_COMMITTER_NAME=ksoichiro From edf38f9a26ab2083d70fa6b745f6f7b5bf2afb94 Mon Sep 17 00:00:00 2001 From: Morton Fox Date: Tue, 5 May 2015 12:49:54 -0400 Subject: [PATCH 096/208] Typo in Environment URL. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4cea8a8d..c3833876 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ and [the documentation](docs/overview.md) for further more. ## Reference * [Supported widgets](docs/reference/supported-widgets.md) -* [Environment](docs/reference/enviromnent.md) +* [Environment](docs/reference/environment.md) * [Release notes](docs/reference/release-notes.md) * [FAQ](docs/faq.md) From 232f237ce41da99ddc2aa39728b8c93f48929904 Mon Sep 17 00:00:00 2001 From: Star Chu Date: Tue, 5 May 2015 17:59:17 +0800 Subject: [PATCH 097/208] ObservableRecyclerView: set mPrevScrolledChildrenHeight to 0 when scrolled to top. This should fix the issue that mScrollY becomes a negative value after fast scrolled down and up to top. --- .../android/observablescrollview/ObservableRecyclerView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java index e0787559..ea3ddf15 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java @@ -142,6 +142,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); } else if (firstVisiblePosition == 0) { mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + mPrevScrolledChildrenHeight = 0; } if (mPrevFirstVisibleChildHeight < 0) { mPrevFirstVisibleChildHeight = 0; From 1c348e029351013f9195d0e097a7e8273fe0783b Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 6 May 2015 16:50:41 +0900 Subject: [PATCH 098/208] Released a new snapshot. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 947ae31c..e82f9532 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.5.1 +VERSION_NAME=1.5.2-SNAPSHOT SYNCED_VERSION_NAME=1.5.1 GROUP=com.github.ksoichiro From 1fc97ef5a72f316cd883a0a6e5d5e58604c7bfe6 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 6 May 2015 17:03:02 +0900 Subject: [PATCH 099/208] Changed token for Travis CI. --- .travis.yml | 2 +- website/gulpfile.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 261a6266..7faee131 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ env: - GIT_COMMITTER_EMAIL=soichiro.kashima@gmail.com - GIT_AUTHOR_NAME=ksoichiro - GIT_AUTHOR_EMAIL=soichiro.kashima@gmail.com - - secure: HvFN+OoRKWHErd7V9ROy0jfoGJ/xPvyaGfhG1lsyxmq9TVAHl+I+t14+gsU8A51PJQWU5FQTG/XDYaFyuY7kDybd5VhZXijy8gT8Yge01TE0UhxHmpMTlP2HI+X9Of9P77PBNnH0oYLVYLYp1x/Nw5JUJ5SfirHnuzD8NDFuQgY= + - secure: Iw0mIseFZ6M/HGi/ERPBT4fabx0G1OVeHCbu6ANoVybO8yHBRHHuUc4pdZOOtEi+Ce/4a5TPZXbGPIVMZrN1ewS3uDAHsEFjmtehsqjk5iKXapvy04dwLsX9jCsQNl0n5679tRZ2eXUGqyVSddc5pIyWwJAGgBmnM/SHDaRy4YA= matrix: - TEST_TARGET=android - TEST_TARGET=website diff --git a/website/gulpfile.js b/website/gulpfile.js index f9494f7e..443e7413 100644 --- a/website/gulpfile.js +++ b/website/gulpfile.js @@ -124,7 +124,6 @@ gulp.task('deploy', ['git-clone'], function(cb) { .pipe(git.commit('Updated website.', {cwd: paths.repo})) .on('end', function() { gutil.log('finish'); - gutil.log('git push ' + project.gitPushUrl + ' gh-pages --quiet'); git.push(project.gitPushUrl, 'gh-pages', {args: '--quiet', /*quiet: true, */cwd: paths.repo}, function(err) { if (err) { gutil.log('Failed to push: ' + err); From 2a33b649ac8346deb9598ce22fde78461416669f Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 6 May 2015 17:26:15 +0900 Subject: [PATCH 100/208] Fixed that coveralls task was not working. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7faee131..ae5a595e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,4 +31,4 @@ before_script: script: - "/bin/bash .travis-script.sh" after_success: -- true && ([ "$TEST_TARGET" != "android" ] || "./gradlew :library:coveralls") +- true && ([ "$TEST_TARGET" != "android" ] || ./gradlew :library:coveralls) From ba21836388a0a3550c3e2e1136e0fda60baf6b46 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 6 May 2015 17:41:50 +0900 Subject: [PATCH 101/208] Fixed jacoco report file path for coveralls task. --- library/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/build.gradle b/library/build.gradle index a32d2230..1337b067 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -56,7 +56,7 @@ android { apply plugin: 'com.github.kt3k.coveralls' -coveralls.jacocoReportPath = 'build/outputs/reports/coverage/debug/report.xml' +coveralls.jacocoReportPath = 'build/reports/coverage/debug/report.xml' // This is from 'https://github.com/chrisbanes/gradle-mvn-push' apply from: 'gradle-mvn-push.gradle' From 940356ab1cb4487518988ac332f69f212772ad1d Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 6 May 2015 17:43:11 +0900 Subject: [PATCH 102/208] Limited connectedCheck task to 'library' project to make CI a little bit faster. --- .travis-script.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis-script.sh b/.travis-script.sh index 03005a81..75f99da7 100644 --- a/.travis-script.sh +++ b/.travis-script.sh @@ -8,7 +8,7 @@ if [ "$TEST_TARGET" = "android" ]; then # Release build type is only for Google Play store currently, # which resolve dependency from Maven Central. # This causes build errors while developing a new feature, so disable release build. - ./gradlew --full-stacktrace assembleDevDebug connectedCheck + ./gradlew --full-stacktrace assembleDevDebug :library:connectedCheck elif [ "$TEST_TARGET" = "website" ]; then if [ "$TRAVIS_PULL_REQUEST" = "false" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ ! -z "$GH_TOKEN" ]; then echo "Update website..." From 10fbfae60eec62c6e6aa0506bb7bd63a41edb03e Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 6 May 2015 21:06:34 +0900 Subject: [PATCH 103/208] (website) Changed font size and margin for readability. --- website/public/css/_fonts.less | 4 ++++ website/public/css/_layout.less | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/website/public/css/_fonts.less b/website/public/css/_fonts.less index d7cd0c79..50996ef5 100644 --- a/website/public/css/_fonts.less +++ b/website/public/css/_fonts.less @@ -29,5 +29,9 @@ h3 { } h3 { font-size: 22px; + font-weight: 400; + } + h4 { + font-weight: 400; } } diff --git a/website/public/css/_layout.less b/website/public/css/_layout.less index 18932c58..3e1a6c49 100644 --- a/website/public/css/_layout.less +++ b/website/public/css/_layout.less @@ -35,6 +35,10 @@ table { margin-top: 26px; margin-bottom: 16px; } + h4 { + margin-top: 20px; + margin-bottom: 16px; + } p, ol, ul, pre { margin-bottom: 16px; } From e9e6f95e0052d48b5b74fd9a12da5d3962258452 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 6 May 2015 21:09:37 +0900 Subject: [PATCH 104/208] Simplified the calculation for the alpha value in the parallax image examples. --- .../samples/ParallaxToolbarListViewActivity.java | 2 +- .../samples/ParallaxToolbarScrollViewActivity.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java index e036f5be..89e0b63c 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java @@ -85,7 +85,7 @@ protected void onRestoreInstanceState(Bundle savedInstanceState) { @Override public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { int baseColor = getResources().getColor(R.color.primary); - float alpha = 1 - (float) Math.max(0, mParallaxImageHeight - scrollY) / mParallaxImageHeight; + float alpha = Math.min(1, (float) scrollY / mParallaxImageHeight); mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, baseColor)); ViewHelper.setTranslationY(mImageView, -scrollY / 2); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarScrollViewActivity.java index 5d3a3f0a..05e79955 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarScrollViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarScrollViewActivity.java @@ -59,7 +59,7 @@ protected void onRestoreInstanceState(Bundle savedInstanceState) { @Override public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { int baseColor = getResources().getColor(R.color.primary); - float alpha = 1 - (float) Math.max(0, mParallaxImageHeight - scrollY) / mParallaxImageHeight; + float alpha = Math.min(1, (float) scrollY / mParallaxImageHeight); mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, baseColor)); ViewHelper.setTranslationY(mImageView, scrollY / 2); } From 362f5a48d3ab414c793bc6fcd4b1b1ff370403dd Mon Sep 17 00:00:00 2001 From: Sam Wolfand Date: Wed, 6 May 2015 10:41:11 -0400 Subject: [PATCH 105/208] Fixed relative links to layout.md & index.md --- docs/quick-start/animation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/quick-start/animation.md b/docs/quick-start/animation.md index f061da76..979ab1e1 100644 --- a/docs/quick-start/animation.md +++ b/docs/quick-start/animation.md @@ -4,7 +4,7 @@ This time, we implement ActionBar animation using `AppCompatActivity` in the sup ## Apply layout to the activity -At first, let `Activity` extend the `AppCompatActivity` and set [the layout we wrote](../../../docs/quick-start/layout.md) to it. +At first, let `Activity` extend the `AppCompatActivity` and set [the layout we wrote](../../docs/quick-start/layout.md) to it. ```java import android.support.v7.app.AppCompatActivity; @@ -103,7 +103,7 @@ You can see the ActionBar gets hidden or shown when you swipe the ListView. As you can see, the most important codes are the animation codes in the callbacks. You can learn how to write these code in this tutorial. -In the [next section](../../../docs/example/index.md), we'll check the existing examples to see what we can do with this library. +In the [next section](../../docs/example/index.md), we'll check the existing examples to see what we can do with this library. ## Program list From bf37af87b3f71d974cb7768c244f61ce9a417d51 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Thu, 7 May 2015 01:34:57 +0900 Subject: [PATCH 106/208] Added "Parallax image" document for ScrollView. --- docs/basic/parallax-image.md | 271 +++++++++++++++++++++++++++++++- website/public/css/_fonts.less | 5 + website/public/css/_layout.less | 4 + 3 files changed, 279 insertions(+), 1 deletion(-) diff --git a/docs/basic/parallax-image.md b/docs/basic/parallax-image.md index 191e0e45..b66b24af 100644 --- a/docs/basic/parallax-image.md +++ b/docs/basic/parallax-image.md @@ -3,11 +3,280 @@ This topic describes how to create parallax effect, which are implemented in the following examples. -* ParallaxToolbarListViewActivity * ParallaxToolbarScrollViewActivity +* ParallaxToolbarListViewActivity + +--- + +## Overview + +In this topic, "parallax" means the following layout and behavior: + +* The layout has an image on the top of the layout. +* The image will move with half the speed of that of the ScrollView. +* ScrollView itself has a big padding, which is like a "window" to see the image. + +To make the image "parallax", we need to do some tricks on the layout. + +`ObservableScrollView` and `ObservableListView` are a little different +around handling paddings. +I'll explain from `ObservableScrollView`. --- +## ScrollView + +### Layout + +#### Basic structure + +At first, let's see the following basic structure of the layout. + +```xml + + + + + + + + + + +``` + +Please note that in this XML, I intentionally omitted attributes(`android:XXX`) +and package name (`com.github.XXX`) for readability. + +##### Why should we use FrameLayout? + +As you can see on the example app, Toolbar is overlaid to the ObservableScrollView. +To do this, we need to use `FrameLayout` or `RelativeLayout`. + +##### What's inside of the ObservableScrollView? + +`ObservableScrollView` extends `ScrollView`, so it can have no more than 1 child. +However we need more children, so placing a `ViewGroup` as the child of `ObservableScrollView` is required. + +`ImageView` is the `View` which is going to have "parallax" effect. +You can replace it to other type of `View` if you want. + +`TextView` is the main content of the screen, you can also replace it to other type of `View`. + +`View` is an "anchor", I'll explain it later. + +We need to move the content and the image separately, +so the parent of them — child of `ObservableScrollView` — +should be `RelativeLayout` or `FrameLayout`. +This time, we use `RelativeLayout` for that purpose. + +#### Don't move the content when its parent is scrolled + +How do you place the main content (a `TextView` for this time) under the `ImageView`? + +Suppose you define the position with `android:layout_below` attribute: + +```xml + + + + + +``` + +We need to move `ImageView` but if we do this, +the `TextView` moves with the same speed as `ImageView` +because its layout is defined with `android:layout_below="@id/image"`. +So we should define the `TextView`'s position with another "anchor" view: + +```xml + + + + + + +``` + +With this anchor view, we can move only `ImageView`. +The anchor `View` and `TextView` will remain in their position. + +#### Set the content color explicitly + +We need to set the background color of the main content explicitly, +because the image is underlying. + +```xml + +``` + +#### Complete the layout + +Now set the rest of the attributes of the layout, +such as `android:layout_width`, `android:padding`, etc. +Please see the folloing codes for details. + +* `res/layout/activity_parallaxtoolbarscrollview.java` + +### Animation + +#### Basic structure of Activity + +We use `AppCompatActivity` of the v7 appcompat library for the base `Activity` class, +and implement `ObservableScrollViewCallbacks`. + +```java +public class ParallaxToolbarScrollViewActivity + extends AppCompatActivity implements ObservableScrollViewCallbacks { +``` + +#### Initialize views + +Then initialize the views like this. + +```java +// Fields +private View mImageView; +private View mToolbarView; +private ObservableScrollView mScrollView; +private int mParallaxImageHeight; + +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_parallaxtoolbarscrollview); + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + + mImageView = findViewById(R.id.image); + mToolbarView = findViewById(R.id.toolbar); + mToolbarView.setBackgroundColor( + ScrollUtils.getColorWithAlpha(0, getResources().getColor(R.color.primary))); + + mScrollView = (ObservableScrollView) findViewById(R.id.scroll); + mScrollView.setScrollViewCallbacks(this); + + mParallaxImageHeight = getResources().getDimensionPixelSize( + R.dimen.parallax_image_height); +} +``` + +The Toolbar should be transparent at the beginning, so set the alpha of the background color to 0 +by using the `ScrollUtils` utility class. +This is optional and you can omit this if you don't use the Toolbar. + +#### Change the position on scrolling + +We use `onScrollChanged()` method, one of `ObservableScrollViewCallbacks`, to animate the view. +What we need to do in this method is: + +1. translate the `ImageView` in Y-axis using `scrollY` parameter +1. change the alpha value of the background color of the `Toolbar` using `scrollY` parameter + +The second one is optional. You can omit this if you don't use the Toolbar. + +##### Translate the ImageView + +Just set the `translateY` property to half of `scrollY`. +If you want to change the "depth" of the parallax effect, adjust this value (`scrollY / 2`). + +```java +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + ViewHelper.setTranslationY(mImageView, scrollY / 2); +} +``` + +##### Change the alpha of the Toolbar background color + +We should change the alpha value of the background color of the Toolbar, +so we can write like this. + +```java +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + int baseColor = getResources().getColor(R.color.primary); + float alpha = 0; // TODO Fix this value + mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, baseColor)); +``` + +Changing alpha is a little complicated, so I wrote `float alpha = 0` temporarily. +Let's confirm the conditions of the colors and fix the `alpha` value. + +* If the `ObservableScrollView` is not scrolled, Toolbar is transparent. + (If `scrollY` equals to 0, alpha of the Toolbar is 0.) +* If the `ObservableScrollView` is scrolled, it becomes opaque gradually, + and when it's scrolled to a certain point, Toolbar is completely opaque. + (If `scrollY` equals to `mParallaxImageHeight`, alpha of the Toolbar is 1.) + +We need to express these conditions as a formula. + +`alpha` should changes from 0 to 1, but `scrollY` changes from 0 to thousands, +so `scrollY` should be scaled. +We should divide `scrollY` with `mParallaxImageHeight` because +when `alpha` becomes 1, `scrollY` should be equal to `mParallaxImageHeight`. + +```java +float alpha = (float) scrollY / mParallaxImageHeight; +``` + +Please note that `scrollY` and `mParallaxImageHeight` are both type `int`, +so you need to cast one of them to `float`. + +But how is it when `scrollY` becomes more than `mParallaxImageHeight`? +Let's simulate the result values: + +| `scrollY` | `mParallaxImageHeight` | `alpha` | Valid alpha? | +| ---------:| ----------------------:| ---------:| ------------- | +| 0 | 300 | 0 | Valid | +| 150 | 300 | 0.5 | Valid | +| 300 | 300 | 1.0 | Valid | +| 450 | 300 | _**1.5**_ | _**Invalid**_ | + +As we can see in the 4th row (`scrollY == 450`), +we need to control `alpha` so that it will not exceed 1.0. +This time we use `Math.min()` to limit the value from 0 to 1. + +```java +float alpha = Math.min(1, (float) scrollY / mParallaxImageHeight); +``` + +Now it's done. +`onScrollChanged` will be like this: + +```java +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + int baseColor = getResources().getColor(R.color.primary); + float alpha = Math.min(1, (float) scrollY / mParallaxImageHeight); + mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, baseColor)); + ViewHelper.setTranslationY(mImageView, scrollY / 2); +} +``` + +#### Restore scroll state + +We need to handle one more thing: restoring scroll state when the Activity is restored. +`ObservableScrollView` itself stores its scroll position, +so you just need to update the view +in the `onRestoreInstanceState()` method. + +```java +@Override +protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + onScrollChanged(mScrollView.getCurrentScrollY(), false, false); +} +``` + +--- + +## ListView + Coming soon... [Next: Sticky header »](../../docs/basic/filling-gap.md) diff --git a/website/public/css/_fonts.less b/website/public/css/_fonts.less index 50996ef5..177635de 100644 --- a/website/public/css/_fonts.less +++ b/website/public/css/_fonts.less @@ -32,6 +32,11 @@ h3 { font-weight: 400; } h4 { + font-size: 18px; + font-weight: 400; + } + h5 { + font-size: 16px; font-weight: 400; } } diff --git a/website/public/css/_layout.less b/website/public/css/_layout.less index 3e1a6c49..d1fe0402 100644 --- a/website/public/css/_layout.less +++ b/website/public/css/_layout.less @@ -36,6 +36,10 @@ table { margin-bottom: 16px; } h4 { + margin-top: 22px; + margin-bottom: 16px; + } + h5 { margin-top: 20px; margin-bottom: 16px; } From e05451a172c164c21b20785e68de17ef7b5e0fde Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 8 May 2015 01:23:15 +0900 Subject: [PATCH 107/208] Fixed invalid link. --- docs/basic/parallax-image.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basic/parallax-image.md b/docs/basic/parallax-image.md index b66b24af..94c2bad6 100644 --- a/docs/basic/parallax-image.md +++ b/docs/basic/parallax-image.md @@ -279,4 +279,4 @@ protected void onRestoreInstanceState(Bundle savedInstanceState) { Coming soon... -[Next: Sticky header »](../../docs/basic/filling-gap.md) +[Next: Sticky header »](../../docs/basic/sticky-header.md) From 8254f5688bf5bb6048492a15fe4fe089e85b3119 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 8 May 2015 01:38:48 +0900 Subject: [PATCH 108/208] Removed redundant codes that adjusts view height. --- .../samples/FillGapBaseActivity.java | 8 -------- .../FlexibleSpaceWithImageListViewActivity.java | 10 ---------- .../FlexibleSpaceWithImageRecyclerViewActivity.java | 10 ---------- .../samples/ParallaxToolbarListViewActivity.java | 10 ---------- 4 files changed, 38 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java index c7a9386e..346109b2 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java @@ -75,14 +75,6 @@ protected void onCreate(Bundle savedInstanceState) { ScrollUtils.addOnGlobalLayoutListener((View) scrollable, new Runnable() { @Override public void run() { - // mListBackgroundView makes ListView's background except header view. - if (mListBackgroundView != null) { - final View contentView = getWindow().getDecorView().findViewById(android.R.id.content); - // mListBackgroundView's should fill its parent vertically - // but the height of the content view is 0 on 'onCreate'. - mListBackgroundView.getLayoutParams().height = contentView.getHeight(); - } - mReady = true; updateViews(scrollable.getCurrentScrollY(), false); } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java index 7e285fb1..bd3d2fcb 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java @@ -101,16 +101,6 @@ public void onClick(View v) { // mListBackgroundView makes ListView's background except header view. mListBackgroundView = findViewById(R.id.list_background); - final View contentView = getWindow().getDecorView().findViewById(android.R.id.content); - contentView.post(new Runnable() { - @Override - public void run() { - // mListBackgroundView's should fill its parent vertically - // but the height of the content view is 0 on 'onCreate'. - // So we should get it with post(). - mListBackgroundView.getLayoutParams().height = contentView.getHeight(); - } - }); } @Override diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java index 4a20e464..712cab4f 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java @@ -69,16 +69,6 @@ public void run() { // mRecyclerViewBackground makes RecyclerView's background except header view. mRecyclerViewBackground = findViewById(R.id.list_background); - final View contentView = getWindow().getDecorView().findViewById(android.R.id.content); - contentView.post(new Runnable() { - @Override - public void run() { - // mRecylcerViewBackground's should fill its parent vertically - // but the height of the content view is 0 on 'onCreate'. - // So we should get it with post(). - mRecyclerViewBackground.getLayoutParams().height = contentView.getHeight(); - } - }); //since you cannot programatically add a headerview to a recyclerview we added an empty view as the header // in the adapter and then are shifting the views OnCreateView to compensate diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java index 89e0b63c..d060cd30 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarListViewActivity.java @@ -64,16 +64,6 @@ protected void onCreate(Bundle savedInstanceState) { // mListBackgroundView makes ListView's background except header view. mListBackgroundView = findViewById(R.id.list_background); - final View contentView = getWindow().getDecorView().findViewById(android.R.id.content); - contentView.post(new Runnable() { - @Override - public void run() { - // mListBackgroundView's should fill its parent vertically - // but the height of the content view is 0 on 'onCreate'. - // So we should get it with post(). - mListBackgroundView.getLayoutParams().height = contentView.getHeight(); - } - }); } @Override From a28c672c1db780a95e1244bc7ccfd31bd0c75450 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 8 May 2015 22:34:22 +0900 Subject: [PATCH 109/208] Wrote about parallax image with ListView. --- docs/basic/parallax-image.md | 169 +++++++++++++++++++++++++++++++++-- docs/images/basic_1.png | Bin 0 -> 208208 bytes 2 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 docs/images/basic_1.png diff --git a/docs/basic/parallax-image.md b/docs/basic/parallax-image.md index 94c2bad6..b942d779 100644 --- a/docs/basic/parallax-image.md +++ b/docs/basic/parallax-image.md @@ -36,12 +36,12 @@ At first, let's see the following basic structure of the layout. - - - + + + - + ``` @@ -277,6 +277,165 @@ protected void onRestoreInstanceState(Bundle savedInstanceState) { ## ListView -Coming soon... +Let's see the difference of the implementation between ListView version and ScrollView version. + +### Layout + +#### Basic structure + +```xml + + + + + + +``` + +We use `FrameLayout` to the root view, just like ScrollView pattern. +`FrameLayout` can be used to move children views separately. + +`ImageView` is the view which should have "parallax" effect. + +The next `View` is used for different purpose from that of ScrollView. +I'll explain this later. + + +#### Why do we use different layout? + +Unlike ScrollView, ListView cannot have children views, +so `ImageView` should be outside of the scrollable view (ListView) +and we should move the `ImageView` manually. + +#### How do we place ImageView and ListView? + +`ImageView` is going to be scrolled slower than ListView +(because we're going to make "parallax" effect), +so `ImageView` should be underneath the ListView. +Otherwise, the bottom of the `ImageView` overlaps with the top of the ListView. + +Also, ListView should have a big padding +at the top of the ListView to make `ImageView` visible. +We achieve this by adding a transparent header view to the ListView. + +#### Why do we need a View? + +As I mentioned above, ListView should have a transparent header, +so its background color should be also transparent. +But if we do this, not only the header view but also the items of the ListView +become transparent. + +![](../images/basic_1.png) + + +To avoid this, we set a dummy background view under the ListView. + +### Animation + +#### Basic structure of Activity + +It's same as `ParallaxToolbarScrollViewActivity` example. + +```java +public class ParallaxToolbarListViewActivity + extends BaseActivity implements ObservableScrollViewCallbacks { +``` + +#### Initialize views + +Like ScrollView, initialize the `ObservableListView`, `ImageView`, Toolbar, etc. +And as I explained, ListView should have a header view. + +```java +private View mImageView; +private View mToolbarView; +private View mListBackgroundView; +private ObservableListView mListView; +private int mParallaxImageHeight; + +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_parallaxtoolbarlistview) ; + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + + mImageView = findViewById(R.id.image); + mToolbarView = findViewById(R.id.toolbar); + mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(0, getResources().getColor(R.color.primary))); + + mParallaxImageHeight = getResources().getDimensionPixelSize(R.dimen.parallax_image_height); + + mListView = (ObservableListView) findViewById(R.id.list); + mListView.setScrollViewCallbacks(this); + // Set padding view for ListView. This is the flexible space. + View paddingView = new View(this); + AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, + mParallaxImageHeight); + paddingView.setLayoutParams(lp); + paddingView.setClickable(true); + + mListView.addHeaderView(paddingView); + setDummyData(mListView); + mListBackgroundView = findViewById(R.id.list_background); +``` + +Note that following code is necessary to disable header view's list selector effect. + +```java + paddingView.setClickable(true); +``` + +`setDummyData()` should be replaced to appropriate data population codes. + +#### Change the position on scrolling + +##### Translate the ImageView + +We use `onScrollChanged` method to translate views. + +```java +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { +} +``` + +Basically, we should just set the translateY property to half of `scrollY`. +But be careful, unlike ScrollView, when `scrollY` gets larger then `translateY` of `ImageView` should become smaller +because `ImageView` is not a child of the ListView. +So we should use `-scrollY / 2` as `translationY` (and you can adjust "`/ 2`" if you want). + +```java +ViewHelper.setTranslationY(mImageView, -scrollY / 2); +``` + +##### Translate the background view + +The background should move with ListView, but it should have an offset `mParallaxImageHeight` +so we can write like this: + +```java +ViewHelper.setTranslationY(mListBackgroundView, mParallaxImageHeight - scrollY); +``` + +But how is it when `scrollY` becomes more than `mParallaxImageHeight`? +Let's simulate the result values: + +| `mParallaxImageHeight` | `scrollY` | `mParallaxImageHeight - scrollY` | TranslationY of `mListViewBackgroundView` should be | +| ----------------------:| ---------:|---------------------------------:|----------------------------------------------------:| +| 300 | 0 | 300 | 300 | +| 300 | 150 | 150 | 150 | +| 300 | 300 | 0 | 0 | +| 300 | 450 | -150 | 0 | + +The 4th `mParallaxImageHeight - scrollY` becomes negative and it's invalid. +So use `Math.max()` to avoid this. + +```java +ViewHelper.setTranslationY(mListBackgroundView, Math.max(0, -scrollY + mParallaxImageHeight)); +``` + +That's all. +The rest of the codes are the same as `ObservableScrollView` example. [Next: Sticky header »](../../docs/basic/sticky-header.md) diff --git a/docs/images/basic_1.png b/docs/images/basic_1.png new file mode 100644 index 0000000000000000000000000000000000000000..c89590976011520e16ac13305843e8f6c152c9a0 GIT binary patch literal 208208 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∨R|QgSy+U* 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

    0&X5|BV?ssr+5K`nmD;K`j3n7b?6Gcnzr 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 z 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 Date: Sat, 9 May 2015 00:59:41 +0900 Subject: [PATCH 110/208] (website) Added links for each headers (h2-h5). --- website/public/css/_misc.less | 37 +++++++++++++++++++++++++++++++++++ website/public/css/main.less | 1 + website/public/js/main.coffee | 6 ++++++ 3 files changed, 44 insertions(+) create mode 100644 website/public/css/_misc.less diff --git a/website/public/css/_misc.less b/website/public/css/_misc.less new file mode 100644 index 00000000..9a4de7ed --- /dev/null +++ b/website/public/css/_misc.less @@ -0,0 +1,37 @@ +// Showing fragment links +#sidebar-main-content { + h2, h3, h4, h5 { + position: relative; + .anchor { + visibility: hidden; + padding-left: 4px; + } + &:hover { + .anchor { + visibility: visible; + } + } + } + .marker { + position: absolute; + top: -70px; + left: 0; + padding: 0; + margin: 0; + } + h2 .anchor { + font-size: 20px; + } + h3 .anchor { + font-size: 16px; + color: #212121; + } + h4 .anchor { + font-size: 14px; + color: #212121; + } + h5 .anchor { + font-size: 12px; + color: #212121; + } +} diff --git a/website/public/css/main.less b/website/public/css/main.less index 4ca1fb55..9c7a8ebe 100644 --- a/website/public/css/main.less +++ b/website/public/css/main.less @@ -8,3 +8,4 @@ @import '_sidebar.less'; @import '_site-top.less'; @import '_code.less'; +@import '_misc.less'; diff --git a/website/public/js/main.coffee b/website/public/js/main.coffee index def91a9f..21f983c5 100644 --- a/website/public/js/main.coffee +++ b/website/public/js/main.coffee @@ -35,3 +35,9 @@ if $('#site-top') $('nav').addClass('sticky') else $('nav').removeClass('sticky') + +# Create fragment links +$(document).ready -> + $('#sidebar-main-content h2, #sidebar-main-content h3, #sidebar-main-content h4, #sidebar-main-content h5').each -> + fragment = $(@).text().toLowerCase().replace(/[ _\.\/]/g, '-').replace(/--+/g, '-').replace(/([,':()\?!]|-+ |-+$)/g, '') + $(@).html($(@).html() + '#') From 3e63e41f6e6364cc08b418de7afb59fa8c64dc5d Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 10 May 2015 01:51:07 +0900 Subject: [PATCH 111/208] Wrote "Sticky header" tutorial. --- docs/basic/sticky-header.md | 300 +++++++++++++++++++++++++++++++++++- docs/images/basic_2.png | Bin 0 -> 77092 bytes docs/images/basic_3.png | Bin 0 -> 51701 bytes 3 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 docs/images/basic_2.png create mode 100644 docs/images/basic_3.png diff --git a/docs/basic/sticky-header.md b/docs/basic/sticky-header.md index 17fcfb1e..2109d253 100644 --- a/docs/basic/sticky-header.md +++ b/docs/basic/sticky-header.md @@ -10,6 +10,304 @@ which are implemented in the following examples. --- -Coming soon... +## Overview + +This is a complex version of [Toolbar translation pattern](../../docs/basic/translating-toolbar.md). + +We add a features to keep the half of the header view to the top of the screen. +And this time I'll explain using ScrollView. +Replacing it to other type of scrollable views are not so difficult. + +## Using ScrollView + +### Layout with ScrollView + +Let's look at the layout file as always. + +Here is the basic structure of StickyHeader pattern with ScrollView. +This is a little difficult than Toolbar's one. + +```xml + + + + + + + + + + + + + +``` + +In Toolbar translation pattern, we used only `ObservableScrollView` and `Toolbar` in `FrameLayout`. +This time, we need to make each views more complex. + +#### Create header space for ScrollView with twice the size of ActionBar + +At the initial state of views, ScrollView needs to have a header view with twice the size of the ActionBar. +The half of this header view will be "sticky". +So we simply add 2 `View`s with the height `?attr/actionBarSize` above the `TextView`. + +You can also add just 1 `View` with a certain size with `dp`, +but it's better to use `?attr/actionBarSize` because it has multiple values for several size of screens, +screen rotation and OS versions, and using the standard size is good for users. + +Another way to achieve this, is to set the height of the `View` programmatically. +You can resolve the value of `?attr/actionBarSize` in `Activity#onCreate()`, multiply it by 2 and set it to the `View`. + +And please note that `TextView` is the real content of the ScrollView, so you can replace it to other view if you want. + +#### Create sticky part for Toolbar + +Toolbar is replaced to `LinearLayout`, and it contains a Toolbar and a `TextView`. +`TextView` will be the "sticky" view. +You can replace it to some complex views. + +### Animate the views with ScrollView callbacks + +This time, we use two callbacks: `onScrollChanged()` and `onUpOrCancelMotionEvent()` to animate views. +We are going to implement the following animation. + +1. Move the Toolbar and the sticky view (we call "header views") when the ScrollView is scrolled. +1. When we scroll the ScrollView, the Toolbar will go out of the screen. + But when we scroll it more, sticky view must keep its position to the top of the screen. +1. When the Toolbar is not completely hidden and we stop scrolling (touch up the ScrollView), + * the Toolbar will be shown completely, if we were swiping down. + * the Toolbar will be hidden completely, if we were swiping up. +1. When we swipe down the ScrollView and touch up, the header view should + come out immediately. Sometimes it's called "Quick Return" pattern. + +#### Move the header view when ScrollView is scrolled + +Override the `onScrollChanged()`, and implement some codes with the condition `if (dragging)`. + +```java +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (dragging) { + // TODO implement the rest of the codes + //} else { // ScrollView is scrolled by inertia + } +} +``` + +This is because we want to move views only when it is dragged. +Without this, we cannot achieve the 3rd condition above: showing or hiding the Toolbar automatically +when the scroll ended. + +Next step, implement the header view translation. +At first, create a field with name `mHeaderView`, and initialize it in `onCreate()`: + +```java +mHeaderView = findViewById(R.id.header); +``` + +When the scrollY parameter gets increased, the translationY of `mHeaderView` should decrease. +So we can write like this: + +```java +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (dragging) { + ViewHelper.setTranslationY(mHeaderView, -scrollY); + } +} +``` + +#### Sticky view must keep its position to the top of the screen + +The header view will disappear completely, and this is not what we want. +`mHeaderView` should stop after moving the height of Toolbar. + +```java +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (dragging) { + int toolbarHeight = mToolbarView.getHeight(); + ViewHelper.setTranslationY(mHeaderView, Math.max(-toolbarHeight, -scrollY)); + } +} +``` + +You can see the sticky view keeping its position to the top of the screen. + +#### When Toolbar is not completely hidden, show or hide it completely + +To do this, we should implement `onUpOrCancelMotionEvent`. +If we swipe down, Toolbar should be shown, +and if we swipe up, Toolbar should be hidden. + +```java +@Override +public void onUpOrCancelMotionEvent(ScrollState scrollState) { + if (scrollState == ScrollState.DOWN) { + showToolbar(); + } else if (scrollState == ScrollState.UP) { + hideToolbar(); + } +} +``` + +But when we swipe up and scrolled less than Toolbar's height, +hiding the Toolbar makes white space around the top of the ScrollView. +So we should show the Toolbar if `scrollY` is less than Toolbar's height. + +```java +@Override +public void onUpOrCancelMotionEvent(ScrollState scrollState) { + if (scrollState == ScrollState.DOWN) { + showToolbar(); + } else if (scrollState == ScrollState.UP) { + int toolbarHeight = mToolbarView.getHeight(); + int scrollY = mScrollView.getCurrentScrollY(); + if (toolbarHeight <= scrollY) { + hideToolbar(); + } else { + showToolbar(); + } + } +} +``` + +And sometimes `scrollState` becomes `STOP` (or `null`). +If it becomes such values, the header view stops halfway. +To avoid this behavior, write `else` clause. + +```java +@Override +public void onUpOrCancelMotionEvent(ScrollState scrollState) { + if (scrollState == ScrollState.DOWN) { + showToolbar(); + } else if (scrollState == ScrollState.UP) { + int toolbarHeight = mToolbarView.getHeight(); + int scrollY = mScrollView.getCurrentScrollY(); + if (toolbarHeight <= scrollY) { + hideToolbar(); + } else { + showToolbar(); + } + } else { + // Even if onScrollChanged occurs without scrollY changing, toolbar should be adjusted + if (!toolbarIsShown() && !toolbarIsHidden()) { + // Toolbar is moving but doesn't know which to move: + // you can change this to hideToolbar() + showToolbar(); + } + } +} +``` + +Then write the unimplemented methods. +Unlike Toolbar translation pattern, we use `ViewPropertyAnimator.animate()` +because it's simple and we don't have to change the height of views. + +```java +private boolean toolbarIsShown() { + return ViewHelper.getTranslationY(mHeaderView) == 0; +} + +private boolean toolbarIsHidden() { + return ViewHelper.getTranslationY(mHeaderView) == -mToolbarView.getHeight(); +} + +private void showToolbar() { + float headerTranslationY = ViewHelper.getTranslationY(mHeaderView); + if (headerTranslationY != 0) { + ViewPropertyAnimator.animate(mHeaderView).cancel(); + ViewPropertyAnimator.animate(mHeaderView).translationY(0).setDuration(200).start(); + } +} + +private void hideToolbar() { + float headerTranslationY = ViewHelper.getTranslationY(mHeaderView); + int toolbarHeight = mToolbarView.getHeight(); + if (headerTranslationY != -toolbarHeight) { + ViewPropertyAnimator.animate(mHeaderView).cancel(); + ViewPropertyAnimator.animate(mHeaderView).translationY(-toolbarHeight).setDuration(200).start(); + } +} +``` + +Once `ViewPropertyAnimator.animate()` is called, animation will be running in the next 200ms. +And if the next animation(`showToolbar()` or `hideToolbar()`) is requested while the animation is running, +the current animation should be canceled. +Therefore we call `ViewPropertyAnimator.animate(mHeaderView).cancel()` +before calling `start()`. + +#### When swiping up, header view should scroll + +It's almost completed, and if you think it's OK, you don't have to write the following codes. + +When we scroll so much and swip down little, the header view will be shown. +And after that, when we drag ScrollView to upper side, +I think that the header view should move with ScrollView, but it doesn't. + +So we make the header view to scroll even when `scrollY` is larger than the Toolbar's height. + +To do this, we just calculate the distance from the first touch point and the current point. +And the distance from the first touch point become larger than Toolbar's height, +the header view should not scroll any longer. + +```java +// Add a field to keep the first scrollY +private int mBaseTranslationY; + +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (dragging) { + int toolbarHeight = mToolbarView.getHeight(); + if (firstScroll) { // Add this if clause + float currentHeaderTranslationY = ViewHelper.getTranslationY(mHeaderView); + if (-toolbarHeight < currentHeaderTranslationY) { + mBaseTranslationY = scrollY; + } + } + // Change -scrollY to -(scrollY - mBaseTranslationY) + float headerTranslationY = Math.max(-toolbarHeight, -(scrollY - mBaseTranslationY)); + ViewPropertyAnimator.animate(mHeaderView).cancel(); + ViewHelper.setTranslationY(mHeaderView, headerTranslationY); + } +} + +@Override +public void onUpOrCancelMotionEvent(ScrollState scrollState) { + // Should be cleared when scroll ends + mBaseTranslationY = 0; +``` + +It's almost done, but sometimes we can see a weird behavior: +the header view leaves the top of the screen. + +![](../../docs/images/basic_2.png) + +This is because `headerTranslationY` can become larger than 0, +so it should be limited by using `Math.min()`. + +```java +float headerTranslationY = Math.min(0, Math.max(-toolbarHeight, -(scrollY - mBaseTranslationY)); +``` + +Now it's working, but don't you think it's a little complicated expression? +Android-ObservableScrollView provides a small utility class `ScrollUtils`, +and we can replace `Math.min(max, Math.max(min, value))` to `ScrollUtils.getFloat()`. + +```java +float headerTranslationY = ScrollUtils.getFloat(-(scrollY - mBaseTranslationY), -toolbarHeight, 0); +``` + +Of course, you can confirm the meaning of each parameters easily. +Pressing `F1` key on `getFloat()` will show the javadoc window: + +![](../../docs/images/basic_3.png) [Next: Flexible space on the Toolbar »](../../docs/basic/flexible-space-toolbar.md) diff --git a/docs/images/basic_2.png b/docs/images/basic_2.png new file mode 100644 index 0000000000000000000000000000000000000000..0e50291d52d8d518cdf37f20ee9ac415c417f29d GIT binary patch literal 77092 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 Ef&?^)(EtDd literal 0 HcmV?d00001 diff --git a/docs/images/basic_3.png b/docs/images/basic_3.png new file mode 100644 index 0000000000000000000000000000000000000000..cf12590be1c8782c409f0f7d1f24702c79834f52 GIT binary patch literal 51701 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 bkih=}JIu^k+W%7400000NkvXXu0mjfZ>vdD literal 0 HcmV?d00001 From 1a83d7d336c51b1aa701a2923fac2b6a41989de7 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 10 May 2015 01:51:35 +0900 Subject: [PATCH 112/208] (website) Changed code and image styles. --- website/public/css/_code.less | 9 ++++++--- website/public/css/_misc.less | 10 ++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/website/public/css/_code.less b/website/public/css/_code.less index 4e152cf3..adf78a64 100644 --- a/website/public/css/_code.less +++ b/website/public/css/_code.less @@ -3,10 +3,13 @@ @code-bg-color: #263238; // Blue Grey 900 pre { - background: @code-bg-color; - border: 1px solid #CFD8DC; // Blue Grey 100 - border-width: 1px 4px; + border-width: 0px; border-radius: 0px; + background: @code-bg-color; + @code-bg-shadow-alpha: 0.6; + -webkit-box-shadow: 0 4px 6px 2px rgba(24, 24, 24, @code-bg-shadow-alpha) inset; + -moz-box-shadow: 0 4px 6px 2px rgba(24, 24, 24, @code-bg-shadow-alpha) inset; + box-shadow: 0 4px 6px 2px rgba(24, 24, 24, @code-bg-shadow-alpha) inset; } .nohighlight, .hljs { diff --git a/website/public/css/_misc.less b/website/public/css/_misc.less index 9a4de7ed..c2816960 100644 --- a/website/public/css/_misc.less +++ b/website/public/css/_misc.less @@ -34,4 +34,14 @@ font-size: 12px; color: #212121; } + img { + -webkit-box-shadow: 0 2px 4px 2px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 2px 4px 2px rgba(0, 0, 0, 0.3); + box-shadow: 0 2px 4px 2px rgba(0, 0, 0, 0.3); + } + a>img { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } } From 9631f21b772c44535e3d806a9440980bc7818e0f Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 10 May 2015 12:44:04 +0900 Subject: [PATCH 113/208] Replaced codes to limit scrollY value to ScrollUtils.getFloat() in FlexibleSpaceToolbar examples. --- .../samples/FlexibleSpaceToolbarScrollViewActivity.java | 7 +------ .../samples/FlexibleSpaceToolbarWebViewActivity.java | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java index 8eea12f7..66687858 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java @@ -80,12 +80,7 @@ public void onUpOrCancelMotionEvent(ScrollState scrollState) { private void updateFlexibleSpaceText(final int scrollY) { ViewHelper.setTranslationY(mFlexibleSpaceView, -scrollY); - int adjustedScrollY = scrollY; - if (scrollY < 0) { - adjustedScrollY = 0; - } else if (mFlexibleSpaceHeight < scrollY) { - adjustedScrollY = mFlexibleSpaceHeight; - } + int adjustedScrollY = (int) ScrollUtils.getFloat(scrollY, 0, mFlexibleSpaceHeight); float maxScale = (float) (mFlexibleSpaceHeight - mToolbarView.getHeight()) / mToolbarView.getHeight(); float scale = maxScale * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight; diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java index 972c2c49..4ee202b7 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java @@ -93,12 +93,7 @@ public void onUpOrCancelMotionEvent(ScrollState scrollState) { private void updateFlexibleSpaceText(final int scrollY) { ViewHelper.setTranslationY(mFlexibleSpaceView, -scrollY); - int adjustedScrollY = scrollY; - if (scrollY < 0) { - adjustedScrollY = 0; - } else if (mFlexibleSpaceHeight < scrollY) { - adjustedScrollY = mFlexibleSpaceHeight; - } + int adjustedScrollY = (int) ScrollUtils.getFloat(scrollY, 0, mFlexibleSpaceHeight); // Special logic for WebView. adjustTopMargin(mWebViewContainer, adjustedScrollY <= mFlexibleSpaceHeight ? 0 : mFlexibleSpaceHeight + getActionBarSize()); From 195e2fd53b91969617d867f7fec630c5cd91f6ef Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 11 May 2015 21:36:23 +0900 Subject: [PATCH 114/208] Removed redundant calculation codes in FlexibleSpaceToolbar examples. --- .../samples/FlexibleSpaceToolbarScrollViewActivity.java | 1 - .../samples/FlexibleSpaceToolbarWebViewActivity.java | 1 - 2 files changed, 2 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java index 66687858..04678edc 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java @@ -88,7 +88,6 @@ private void updateFlexibleSpaceText(final int scrollY) { ViewHelper.setPivotY(mTitleView, 0); ViewHelper.setScaleX(mTitleView, 1 + scale); ViewHelper.setScaleY(mTitleView, 1 + scale); - ViewHelper.setTranslationY(mTitleView, ViewHelper.getTranslationY(mFlexibleSpaceView) + mFlexibleSpaceView.getHeight() - mTitleView.getHeight() * (1 + scale)); int maxTitleTranslationY = mToolbarView.getHeight() + mFlexibleSpaceHeight - (int) (mTitleView.getHeight() * (1 + scale)); int titleTranslationY = (int) (maxTitleTranslationY * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight); ViewHelper.setTranslationY(mTitleView, titleTranslationY); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java index 4ee202b7..a2a2f976 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java @@ -105,7 +105,6 @@ private void updateFlexibleSpaceText(final int scrollY) { ViewHelper.setPivotY(mTitleView, 0); ViewHelper.setScaleX(mTitleView, 1 + scale); ViewHelper.setScaleY(mTitleView, 1 + scale); - ViewHelper.setTranslationY(mTitleView, ViewHelper.getTranslationY(mFlexibleSpaceView) + mFlexibleSpaceView.getHeight() - mTitleView.getHeight() * (1 + scale)); int maxTitleTranslationY = mToolbarView.getHeight() + mFlexibleSpaceHeight - (int) (mTitleView.getHeight() * (1 + scale)); int titleTranslationY = (int) (maxTitleTranslationY * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight); ViewHelper.setTranslationY(mTitleView, titleTranslationY); From 4aa1d72b61236f1307a5246c6c728070cfa6634d Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 11 May 2015 23:44:43 +0900 Subject: [PATCH 115/208] Wrote "Flexible space on the Toolbar" tutorial. --- docs/basic/flexible-space-toolbar.md | 277 ++++++++++++++++++++++++++- docs/images/basic_4.png | Bin 0 -> 25359 bytes docs/images/basic_5.png | Bin 0 -> 55467 bytes 3 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 docs/images/basic_4.png create mode 100644 docs/images/basic_5.png diff --git a/docs/basic/flexible-space-toolbar.md b/docs/basic/flexible-space-toolbar.md index a41effd4..100c7a62 100644 --- a/docs/basic/flexible-space-toolbar.md +++ b/docs/basic/flexible-space-toolbar.md @@ -6,8 +6,283 @@ which are implemented in the following examples. * FlexibleSpaceToolbarScrollViewActivity * FlexibleSpaceToolbarWebViewActivity +I originally tried implementing this pattern (only the title animation): +[Flexible space with image](http://material-design.storage.googleapis.com/publish/material_v_3/material_ext_publish/0B969e8h0awhvQ3lJdU9WVTh1WWM/patterns_scrolling_flexspaceimage.webm) + --- -Coming soon... +## Using ScrollView + +### Layout with ScrollView + +#### Basic structure + +```xml + + + + + + + + + + + + + + + + +``` + +The root `FrameLayout` is used for moving children separately. + +The second `FrameLayout`(`@id/body`) inside the ScrollView is the main content, +and you can put any views as you like. +This time, we'll add just a `TextView`. + +`View`(`@id/flexible_space`) is for a "flexible space" which has a opaque background. +This view will be translated vertically on scrolling. + +`Toolbar` is a normal Toolbar, but this Toolbar will not have "title". + +The next `RelativeLayout` and its children are a little tricky. +The `TextView`(`@id/title`) is the real title view, +and other views (`LinearLayout`, `View`) are padding. +In this "flexible space" pattern, `TextView`'s text should move and its font size should change, +so it needs additional space. +We'll achieve these animations by animate `TextView` itself, so paddings should be outside the `TextView`. + +To confirm other attributes, +please see `res/layout/activity_flexiblespacetoolbarscrollview.xml` in the example app. + +### Initialization + +At first, set the Toolbar as the ActionBar and show "homeAsUp" button. + +```java +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_flexiblespacetoolbarscrollview); + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); +``` + +And get the Activity's title and set it to the `TextView` which has the ID `@id/title`. + +```java + mTitleView = (TextView) findViewById(R.id.title); + mTitleView.setText(getTitle()); + setTitle(null); +``` + +And initialize other views and fields. + +```java +private View mFlexibleSpaceView; +private View mToolbarView; +private TextView mTitleView; +private int mFlexibleSpaceHeight; + +@Override +protected void onCreate(Bundle savedInstanceState) { + // Codes that are already explained above are omitted + mFlexibleSpaceView = findViewById(R.id.flexible_space); + mToolbarView = findViewById(R.id.toolbar); + + final ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll); + scrollView.setScrollViewCallbacks(this); + + mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_height); + int flexibleSpaceAndToolbarHeight = mFlexibleSpaceHeight + getActionBarSize(); + + findViewById(R.id.body).setPadding(0, flexibleSpaceAndToolbarHeight, 0, 0); + mFlexibleSpaceView.getLayoutParams().height = flexibleSpaceAndToolbarHeight; +} +``` + +You should also add `implements ObservableScrollViewCallbacks` to the Activity +and implement those methods as always. + +### Animation + +We use `onScrollChanged()` to create animation. +We must write following codes: + +* Translate the flexible space view +* Translate and scale the title view + +#### Translate the flexible space view + +This is easy, just translate it using `scrollY`: + +```java +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + ViewHelper.setTranslationY(mFlexibleSpaceView, -scrollY); +} +``` + +#### Scale the title view + +How do you change the size of the font? +At first I tried just changing the size of the font, but it didn't work. It should be scaled. + +The scale should change from `1` to `1.x`. You can change `.x` to fit your app. +In this case, I used the height of the flexible space and the height of the Toolbar. This calculates the maximum `.x`: + +```java +float maxScale = (float) (mFlexibleSpaceHeight - mToolbarView.getHeight()) / mToolbarView.getHeight(); +``` + +The scale (we call `.x` part as "scale" from here) should change between 0 to `maxScale`, so it can be written as follows. + +```java +// scrollY should be limited. +int adjustedScrollY = (int) ScrollUtils.getFloat(scrollY, 0, mFlexibleSpaceHeight); + +// When scrollY is 0, scale equals to maxScale. +// When scrollY reaches to mFlexibleSpaceHeight, scale will be 0. +float scale = maxScale * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight; +``` + +When scaling the view, we need to set the center point of scaling. +You can handle this by using `pivotX` and `pivotY`, and we should set them to `(0, 0)` like this image: + +![](../images/basic_4.png) + +We will set `pivotX` and `pivotY` first, and then change the scale: + +```java +// Pivot the title view to (0, 0) +ViewHelper.setPivotX(mTitleView, 0); +ViewHelper.setPivotY(mTitleView, 0); + +// Scale the title view +ViewHelper.setScaleX(mTitleView, 1 + scale); +ViewHelper.setScaleY(mTitleView, 1 + scale); +``` + +#### Translate the title view + +And about `translationY`, this is a little complicated. +Let's see the following picture. + +![](../images/basic_5.png) + +The minimum `translationY` is obviously 0, and we want to know +the maximum `translationY`. +As we can see in the picture, the maximum `translationY` can be calculated with `ht1 + hf - ht2`, so we can write like this: + +```java +int maxTitleTranslationY = mToolbarView.getHeight() + mFlexibleSpaceHeight - (int) (mTitleView.getHeight() * (1 + scale)); +``` + +And we should vary this value using `scrollY`. +`scrollY` should be limited and it's already calculated as `adjustedY`: + +```java + int titleTranslationY = (int) (maxTitleTranslationY * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight); + ViewHelper.setTranslationY(mTitleView, titleTranslationY); +``` + +Finally, we've finished translation and scaling. + +```java +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + ViewHelper.setTranslationY(mFlexibleSpaceView, -scrollY); + + // Calculate scale + int adjustedScrollY = (int) ScrollUtils.getFloat(scrollY, 0, mFlexibleSpaceHeight); + float maxScale = (float) (mFlexibleSpaceHeight - mToolbarView.getHeight()) / mToolbarView.getHeight(); + float scale = maxScale * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight; + + // Pivot the title view to (0, 0) + ViewHelper.setPivotX(mTitleView, 0); + ViewHelper.setPivotY(mTitleView, 0); + + // Scale the title view + ViewHelper.setScaleX(mTitleView, 1 + scale); + ViewHelper.setScaleY(mTitleView, 1 + scale); + + // Translate the title view + int maxTitleTranslationY = mToolbarView.getHeight() + mFlexibleSpaceHeight - (int) (mTitleView.getHeight() * (1 + scale)); + int titleTranslationY = (int) (maxTitleTranslationY * ((float) mFlexibleSpaceHeight - adjustedScrollY) / mFlexibleSpaceHeight); + ViewHelper.setTranslationY(mTitleView, titleTranslationY); +} +``` + +#### Adjust the initial state of the title + +It's almost finished, but maybe you will notice +that when the screen is launched, +the title is located at the top of the screen. +It should be located at the bottom of the header view area and have larger font. + +This is because `onScrollChanged()` is not called. +You can fix that by calling `onScrollChanged()` just after the views are laied out. +And you can handle this "laid out" event by using `ViewTreeObserver#addOnGlobalLayoutListener()`. + +```java +@Override +protected void onCreate(Bundle savedInstanceState) { + // Other initialization codes are omitted + ViewTreeObserver vto = mTitleView.getViewTreeObserver(); + vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + view.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } else { + view.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } + updateFlexibleSpaceText(scrollView.getCurrentScrollY()); + } + }); +} + +@Override +public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + updateFlexibleSpaceText(scrollY); +} + +private void updateFlexibleSpaceText(scrollY) { + // Original animation codes are omitted +} +``` + +You can replace the following `ViewTreeObserver` codes + +```java +ViewTreeObserver vto = mTitleView.getViewTreeObserver(); +vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + view.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } else { + view.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } + updateFlexibleSpaceText(scrollView.getCurrentScrollY()); + } +}); +``` + +to this: + +```java +ScrollUtils.addOnGlobalLayoutListener(mTitleView, new Runnable() { + @Override + public void run() { + updateFlexibleSpaceText(scrollView.getCurrentScrollY()); + } +}); +``` + +That's all! [Next: Flexible space with image »](../../docs/basic/flexible-space-with-image.md) diff --git a/docs/images/basic_4.png b/docs/images/basic_4.png new file mode 100644 index 0000000000000000000000000000000000000000..9f5f92501f05c197f492fefc696b8dee6fa30b56 GIT binary patch literal 25359 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 z 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 literal 0 HcmV?d00001 From ded10cee3d3b3a3bdf7ecd3d5be964ec0924ca74 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 13 May 2015 22:08:46 +0900 Subject: [PATCH 116/208] (website) Replaced GitHub link to the octicon. --- website/bower.json | 6 +++++- website/public/_head.ejs | 1 + website/public/_nav.ejs | 2 +- website/public/index.ejs | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/website/bower.json b/website/bower.json index 68a83f0c..669446cf 100644 --- a/website/bower.json +++ b/website/bower.json @@ -7,7 +7,8 @@ "roboto-fontface": "0.4.0", "highlightjs": "8.5.0", "respond-minmax": "1.4.2", - "html5shiv": "3.7.2" + "html5shiv": "3.7.2", + "octicons": "2.2.2" }, "overrides": { "jquery": { @@ -27,6 +28,9 @@ }, "html5shiv": { "main": "dist/*.min.js" + }, + "octicons": { + "main": ["octicons/*.css", "octicons/*.svg", "octicons/*.woff", "octicons/*.ttf", "octicons/*.eot"] } } } diff --git a/website/public/_head.ejs b/website/public/_head.ejs index 6be35023..4ebe9af4 100644 --- a/website/public/_head.ejs +++ b/website/public/_head.ejs @@ -13,6 +13,7 @@ + diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java index e1363af7..1731823f 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java @@ -51,6 +51,12 @@ public void run() { } }); } + + // TouchInterceptionViewGroup should be a parent view other than ViewPager. + // This is a workaround for the issue #117: + // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 + listView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.root)); + listView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); } return view; diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewFragment.java index 28347a33..94b22e81 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewFragment.java @@ -48,6 +48,12 @@ public void run() { } }); } + + // TouchInterceptionViewGroup should be a parent view other than ViewPager. + // This is a workaround for the issue #117: + // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 + scrollView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.root)); + scrollView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); } return view; From bbae0f6493f55dfd74d8335b534f3a7a959f2d63 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 15 May 2015 21:32:52 +0900 Subject: [PATCH 120/208] #125 Added "how to build with API level 8" to FAQ. --- docs/faq.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/faq.md b/docs/faq.md index 038b2359..58f69117 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -57,3 +57,35 @@ If you're in a hurry, please send me an email. I'll release it as soon as possib 1. Release SNAPSHOT version to the Sonatype snapshot repository. 1. Release to the Sonatype repository. If it's successfully released, it will be synced to Maven Central in a couple of hours. 1. Update README to prompt to use the latest version. + +## Q. Can I use this library with API level 8? + +### A. It's not supported, but you can. + +By adding `tools:overrideLibrary` to `` tag, +you can build this library with `android:minSdkVersion="8"`. + +```xml + + + +``` + +If you have other libraries to override, separate them with comma. + +```xml + + + +``` + From fd92cb4a6da2d8c913e41d7988c006fdc78d497d Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 17 May 2015 08:30:46 +0900 Subject: [PATCH 121/208] #126 Added ViewPagerTabScrollViewWithFabActivity to show how to handle scrolls on both Activity and Fragments. --- samples/AndroidManifest.xml | 10 ++ .../res/layout/fragment_scrollviewwithfab.xml | 56 +++++++ samples/res/values/strings.xml | 1 + .../ViewPagerTabScrollViewActivity.java | 14 +- ...ViewPagerTabScrollViewWithFabActivity.java | 46 ++++++ ...ViewPagerTabScrollViewWithFabFragment.java | 138 ++++++++++++++++++ 6 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 samples/res/layout/fragment_scrollviewwithfab.xml create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewWithFabActivity.java create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewWithFabFragment.java diff --git a/samples/AndroidManifest.xml b/samples/AndroidManifest.xml index 35653c90..4c1be888 100644 --- a/samples/AndroidManifest.xml +++ b/samples/AndroidManifest.xml @@ -534,6 +534,16 @@ + + + + + + + diff --git a/samples/res/layout/fragment_scrollviewwithfab.xml b/samples/res/layout/fragment_scrollviewwithfab.xml new file mode 100644 index 00000000..f80233d7 --- /dev/null +++ b/samples/res/layout/fragment_scrollviewwithfab.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + diff --git a/samples/res/values/strings.xml b/samples/res/values/strings.xml index 1ab4179c..ca8adf91 100644 --- a/samples/res/values/strings.xml +++ b/samples/res/values/strings.xml @@ -66,6 +66,7 @@ ViewPager & Tab & Different fragments ViewPager & Tab & ListView ViewPager & Tab & ScrollView + ViewPager & Tab & ScrollView & FAB Lorem ipsum dolor sit amet, ut duis lorem provident sed felis blandit, condimentum donec lectus ipsum et mauris, morbi porttitor interdum feugiat nulla donec sodales, vestibulum nisl primis a molestie vestibulum quam, sapien mauris metus risus suspendisse magnis. Augue viverra nulla faucibus egestas eu, a etiam id congue rutrum ante, arcu tincidunt donec quam felis at ornare, iaculis ligula sodales venenatis commodo volutpat neque, suspendisse elit praesent tellus felis mi amet. Inceptos amet tempor lectus lorem est non, ac donec ac libero neque mauris, tellus ante metus eget leo consequat. Scelerisque dolor curabitur pretium blandit ut feugiat, amet lacus pulvinar justo convallis ut, sed natoque ipsum urna posuere nibh eu. Sed at sed vulputate sit orci, facilisis a aliquam tellus quam aliquam, eu aliquam donec at molestie ante, pellentesque mauris lorem ultrices libero faucibus porta, imperdiet adipiscing sit hac diam ut nulla. Lacus enim elit pulvinar donec vehicula dapibus, accumsan purus officia cursus dolor sapien, eu amet dis mauris mi nulla ut. Non accusamus etiam pede non urna tempus, vestibulum aliquam tortor eget pharetra sodales, in vestibulum ut justo orci nulla, lobortis purus sem semper consectetuer magni purus. Dolor a leo vestibulum amet ut sit, arcu ut eaque urna fusce aliquet turpis, sed fermentum sed vestibulum nisl pede, tristique enim lorem posuere in laborum ut. Vestibulum id id justo leo nulla, magna lobortis ullamcorper et dignissim pellentesque, duis suspendisse quis id lorem ante. Vivamus a nullam ante adipiscing amet, mi vel consectetuer nunc aenean pede quisque, eget rhoncus dis porttitor habitant nunc vivamus, duis cubilia blandit non donec justo dictumst, praesent vitae nulla nam pulvinar urna. Adipiscing adipiscing justo urna pulvinar imperdiet nullam, vitae fusce rhoncus proin nonummy suscipit, ullamcorper amet et non potenti platea ultrices, mauris nullam sapien nunc justo vel, eu semper pellentesque arcu fusce augue. Malesuada mauris nibh sit a a scelerisque, velit sem lectus tellus convallis consectetuer, ultricies auctor a ante eros amet sed.\n\n Risus lacus duis leo platea wisi, felis maecenas rutrum in id in donec, non id a potenti libero eget, posuere elit ea sed pellentesque quis. Sunt lacus urna lorem elit duis, nibh donec purus quisque consectetuer dolor, neque vestibulum proin ornare eros nonummy phasellus. Iaculis cras eu at egestas dolor montes, viverra quisque malesuada consectetuer semper maecenas, a sed vitae donec tempor aliqua metus, ornare mollis suscipit et erat fusce, sit orci aut auctor elementum fames aliquam. Platea dui integer magnis non metus, minus dignissimos ante massa nostra et, rutrum sapien egestas quis sapien donec donec. Erat sit a eros aenean natoque, quam libero id lorem enim proin, lorem ipsum fermentum mattis metus et. Aliquam aliquet suscipit purus conubia at neque, platea vivamus vestibulum nulla quibusdam senectus, et morbi lectus malesuada gravida donec, elementum sit convallis pellentesque velit amet. Et eveniet viverra vehicula consectetuer justo, provident sed commodo non lacinia velit, tempor phasellus vel leo nisl cras, vivamus et arcu interdum dui eu amet. Volutpat wisi rhoncus vel turpis diam quibusdam, dapibus elit est quisque cubilia mauris, nulla elit magna tempor accumsan bibendum, lorem varius sed interdum eget mattis, scelerisque egestas feugiat donec dui molestie. Leo facilisis nisl sit montes ligula sed, enim commodo consectetuer nunc est et, ut sed vehicula dolor luctus elit. Fermentum cras donec eget nibh est vel, sed justo risus et pharetra diam, eu vivamus egestas ligula risus diam, sed justo eget hac ut mauris. Vestibulum diam nec vitae mi eget suspendisse, aenean arcu purus facilisis purus class in, id aliquam sit id scelerisque sapien etiam. Ut nullam sit sed at mauris lobortis, consequat dolor autem ipsum euismod nulla, elit quis proin eget conubia varius, erat arcu massa mus in mauris, scelerisque ut eu sollicitudin libero leo urna.\n\n diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java index 5755753f..c8769436 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java @@ -55,7 +55,7 @@ protected void onCreate(Bundle savedInstanceState) { mHeaderView = findViewById(R.id.header); ViewCompat.setElevation(mHeaderView, getResources().getDimension(R.dimen.toolbar_elevation)); mToolbarView = findViewById(R.id.toolbar); - mPagerAdapter = new NavigationAdapter(getSupportFragmentManager()); + mPagerAdapter = newViewPagerAdapter(); mPager = (ViewPager) findViewById(R.id.pager); mPager.setAdapter(mPagerAdapter); @@ -184,6 +184,10 @@ private void propagateToolbarState(boolean isShown) { } } + protected NavigationAdapter newViewPagerAdapter() { + return new NavigationAdapter(getSupportFragmentManager()); + } + private boolean toolbarIsShown() { return ViewHelper.getTranslationY(mHeaderView) == 0; } @@ -210,7 +214,7 @@ private void hideToolbar() { propagateToolbarState(false); } - private static class NavigationAdapter extends CacheFragmentStatePagerAdapter { + protected static class NavigationAdapter extends CacheFragmentStatePagerAdapter { private static final String[] TITLES = new String[]{"Applepie", "Butter Cookie", "Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb", "Ice Cream Sandwich", "Jelly Bean", "KitKat", "Lollipop"}; @@ -224,9 +228,13 @@ public void setScrollY(int scrollY) { mScrollY = scrollY; } + protected Fragment newFragment() { + return new ViewPagerTabScrollViewFragment(); + } + @Override protected Fragment createItem(int position) { - Fragment f = new ViewPagerTabScrollViewFragment(); + Fragment f = newFragment(); if (0 <= mScrollY) { Bundle args = new Bundle(); args.putInt(ViewPagerTabScrollViewFragment.ARG_SCROLL_Y, mScrollY); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewWithFabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewWithFabActivity.java new file mode 100644 index 00000000..ad47c45e --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewWithFabActivity.java @@ -0,0 +1,46 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; + +/** + * This example shows how to handle scroll events on both the parent Activity and Fragments. + * (Handling FAB is not the main purpose) + * + * SlidingTabLayout and SlidingTabStrip are from google/iosched: + * https://github.com/google/iosched + */ +public class ViewPagerTabScrollViewWithFabActivity extends ViewPagerTabScrollViewActivity { + + @Override + protected NavigationAdapter newViewPagerAdapter() { + return new NavigationAdapter(getSupportFragmentManager()); + } + + private static class NavigationAdapter extends ViewPagerTabScrollViewActivity.NavigationAdapter { + public NavigationAdapter(FragmentManager fm) { + super(fm); + } + + @Override + protected Fragment newFragment() { + return new ViewPagerTabScrollViewWithFabFragment(); + } + } +} diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewWithFabFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewWithFabFragment.java new file mode 100644 index 00000000..625217e2 --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewWithFabFragment.java @@ -0,0 +1,138 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.app.Activity; +import android.os.Build; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.nineoldandroids.view.ViewHelper; +import com.nineoldandroids.view.ViewPropertyAnimator; + +/** + * This example shows how to handle scroll events on both the parent Activity and Fragments. + * (Handling FAB is not the main purpose) + */ +public class ViewPagerTabScrollViewWithFabFragment extends BaseFragment implements ObservableScrollViewCallbacks { + + public static final String ARG_SCROLL_Y = "ARG_SCROLL_Y"; + private View mFab; + private int mFabMargin; + private boolean mFabIsShown; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.fragment_scrollviewwithfab, container, false); + mFab = view.findViewById(R.id.fab); + mFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard); + mFabIsShown = true; + + // Translate FAB + ScrollUtils.addOnGlobalLayoutListener(mFab, new Runnable() { + @Override + public void run() { + float fabTranslationY = view.getHeight() - mFabMargin - mFab.getHeight(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + // On pre-honeycomb, ViewHelper.setTranslationX/Y does not set margin, + // which causes FAB's OnClickListener not working. + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFab.getLayoutParams(); + lp.leftMargin = view.getWidth() - mFabMargin - mFab.getWidth(); + lp.topMargin = (int) fabTranslationY; + mFab.requestLayout(); + } else { + ViewHelper.setTranslationX(mFab, view.getWidth() - mFabMargin - mFab.getWidth()); + ViewHelper.setTranslationY(mFab, fabTranslationY); + } + } + }); + + final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll); + Activity parentActivity = getActivity(); + if (parentActivity instanceof ObservableScrollViewCallbacks) { + // Scroll to the specified offset after layout + Bundle args = getArguments(); + if (args != null && args.containsKey(ARG_SCROLL_Y)) { + final int scrollY = args.getInt(ARG_SCROLL_Y, 0); + ScrollUtils.addOnGlobalLayoutListener(scrollView, new Runnable() { + @Override + public void run() { + scrollView.scrollTo(0, scrollY); + } + }); + } + + // TouchInterceptionViewGroup should be a parent view other than ViewPager. + // This is a workaround for the issue #117: + // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 + scrollView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.root)); + } + scrollView.setScrollViewCallbacks(this); + + return view; + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (getActivity() != null && getActivity() instanceof ObservableScrollViewCallbacks) { + ((ObservableScrollViewCallbacks) getActivity()).onScrollChanged(scrollY, firstScroll, dragging); + } + } + + @Override + public void onDownMotionEvent() { + if (getActivity() != null && getActivity() instanceof ObservableScrollViewCallbacks) { + ((ObservableScrollViewCallbacks) getActivity()).onDownMotionEvent(); + } + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + if (getActivity() != null && getActivity() instanceof ObservableScrollViewCallbacks) { + ((ObservableScrollViewCallbacks) getActivity()).onUpOrCancelMotionEvent(scrollState); + } + + if (scrollState == ScrollState.UP) { + hideFab(); + } else if (scrollState == ScrollState.DOWN) { + showFab(); + } + } + + private void showFab() { + if (!mFabIsShown) { + ViewPropertyAnimator.animate(mFab).cancel(); + ViewPropertyAnimator.animate(mFab).scaleX(1).scaleY(1).setDuration(200).start(); + mFabIsShown = true; + } + } + + private void hideFab() { + if (mFabIsShown) { + ViewPropertyAnimator.animate(mFab).cancel(); + ViewPropertyAnimator.animate(mFab).scaleX(0).scaleY(0).setDuration(200).start(); + mFabIsShown = false; + } + } +} From 8da9a81e2465e74a925bcd6e3fdbd052e89be8d0 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 18 May 2015 22:54:23 +0900 Subject: [PATCH 122/208] Removed redundant wrapper view in FillGap examples. --- samples/res/layout/activity_fillgaplistview.xml | 16 +++++----------- .../res/layout/activity_fillgaprecyclerview.xml | 16 +++++----------- .../res/layout/activity_fillgapscrollview.xml | 16 +++++----------- .../samples/FillGapBaseActivity.java | 6 +++--- 4 files changed, 18 insertions(+), 36 deletions(-) diff --git a/samples/res/layout/activity_fillgaplistview.xml b/samples/res/layout/activity_fillgaplistview.xml index 4f603737..804551b5 100644 --- a/samples/res/layout/activity_fillgaplistview.xml +++ b/samples/res/layout/activity_fillgaplistview.xml @@ -19,18 +19,12 @@ android:layout_height="match_parent" android:clipChildren="false"> - - - - + android:layout_height="@dimen/flexible_space_image_height" + android:scaleType="centerCrop" + android:src="@drawable/example" /> - - - - + android:layout_height="@dimen/flexible_space_image_height" + android:scaleType="centerCrop" + android:src="@drawable/example" /> - - - - + android:layout_height="@dimen/flexible_space_image_height" + android:scaleType="centerCrop" + android:src="@drawable/example" /> extends BaseActi protected int mActionBarSize; protected int mIntersectionHeight; - private View mImageHolder; + private View mImage; private View mHeaderBackground; private int mPrevScrollY; private boolean mGapIsChanging; @@ -61,7 +61,7 @@ protected void onCreate(Bundle savedInstanceState) { // within mIntersectionHeight. mIntersectionHeight = getResources().getDimensionPixelSize(R.dimen.intersection_height); - mImageHolder = findViewById(R.id.image_holder); + mImage = findViewById(R.id.image); mHeader = findViewById(R.id.header); mHeaderBar = findViewById(R.id.header_bar); mHeaderBackground = findViewById(R.id.header_background); @@ -105,7 +105,7 @@ protected void updateViews(int scrollY, boolean animated) { return; } // Translate image - ViewHelper.setTranslationY(mImageHolder, -scrollY / 2); + ViewHelper.setTranslationY(mImage, -scrollY / 2); // Translate header ViewHelper.setTranslationY(mHeader, getHeaderTranslationY(scrollY)); From 71be1856f8fe18db3431e063967dd2e22857463f Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 18 May 2015 23:47:45 +0900 Subject: [PATCH 123/208] Removed unused Toolbar in FillGap examples. --- samples/res/layout/activity_fillgaplistview.xml | 10 ---------- samples/res/layout/activity_fillgaprecyclerview.xml | 10 ---------- samples/res/layout/activity_fillgapscrollview.xml | 10 ---------- .../samples/FillGapBaseActivity.java | 2 -- 4 files changed, 32 deletions(-) diff --git a/samples/res/layout/activity_fillgaplistview.xml b/samples/res/layout/activity_fillgaplistview.xml index 804551b5..73b18db0 100644 --- a/samples/res/layout/activity_fillgaplistview.xml +++ b/samples/res/layout/activity_fillgaplistview.xml @@ -14,7 +14,6 @@ limitations under the License. --> @@ -69,13 +68,4 @@ android:textSize="20sp" /> - - diff --git a/samples/res/layout/activity_fillgaprecyclerview.xml b/samples/res/layout/activity_fillgaprecyclerview.xml index 9bd76d58..a883e74f 100644 --- a/samples/res/layout/activity_fillgaprecyclerview.xml +++ b/samples/res/layout/activity_fillgaprecyclerview.xml @@ -14,7 +14,6 @@ limitations under the License. --> @@ -69,13 +68,4 @@ android:textSize="20sp" /> - - diff --git a/samples/res/layout/activity_fillgapscrollview.xml b/samples/res/layout/activity_fillgapscrollview.xml index 7eb014cb..e99d8d1d 100644 --- a/samples/res/layout/activity_fillgapscrollview.xml +++ b/samples/res/layout/activity_fillgapscrollview.xml @@ -14,7 +14,6 @@ limitations under the License. --> @@ -79,13 +78,4 @@ android:textSize="20sp" /> - - diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java index d83d7635..fb5647e9 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java @@ -52,8 +52,6 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutResId()); - setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); - mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); mActionBarSize = getActionBarSize(); From da4b5b4ee31adebbf2f70f81080dc9ce59d7ba6b Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 20 May 2015 23:51:01 +0900 Subject: [PATCH 124/208] [WIP] Added "Filling gap" pattern tutorial. --- docs/basic/filling-gap.md | 61 ++++++++++++++++++++++++++++++++++++++ docs/images/basic_6.png | Bin 0 -> 194908 bytes docs/images/basic_7.png | Bin 0 -> 268009 bytes 3 files changed, 61 insertions(+) create mode 100644 docs/images/basic_6.png create mode 100644 docs/images/basic_7.png diff --git a/docs/basic/filling-gap.md b/docs/basic/filling-gap.md index f9d744c7..e8d27881 100644 --- a/docs/basic/filling-gap.md +++ b/docs/basic/filling-gap.md @@ -16,8 +16,69 @@ which are implemented in the following examples. * FillGap3RecyclerViewActivity * FillGap3ScrollViewActivity +Please note that these patterns only works for Android 4+. + --- +## Overview + +There are many examples for this pattern, but they can be classified to the following: + +* FillGap + * When swiping up, the header bar expands and fill the gap between the header and the top of the screen. +* FillGap2 + * Almost same as FillGap, but in this pattern, + after the gap is filled with primary color, the filled space is going to shrink, + and the header bar moves. +* FillGap3 + * Usually FillGap should work only when the Scrollable view can scroll. + But sometimes you may want to scroll them with few items, and you can achieve it with this pattern. + * This uses `TouchInterceptionFrameLayout` (one of the widgets in this library), + and this component does not handle "velocity" of scrolls, + so as soon as you touch up your fingers, translation will be stopped. + +## Pattern1 (FillGap) + +### ScrollView + +#### Basic structure + +```xml + + + + + + + + + + + + + + +``` + +`clipChildren` attribute is important. +Without it, part of the views are not drawn. + +Incorrect (without `android:clipChildren="false"`): + +![](../../docs/images/basic_7.png) + +Correct (with `android:clipChildren="false"`): + +![](../../docs/images/basic_6.png) + +Coming soon... + +## Pattern2 (FillGap2) + +Coming soon... + +## Pattern3 (FillGap3) + Coming soon... [Next: Advanced techniques »](../../docs/advanced/index.md) diff --git a/docs/images/basic_6.png b/docs/images/basic_6.png new file mode 100644 index 0000000000000000000000000000000000000000..c033bbc2dda6690da97e8110ddd7acba7470d16b GIT binary patch literal 194908 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

    I=0 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 z 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

    Y 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 &QqY1C(Bt6<3Z1j{9MM=4A&svp@aZom|TCwK03{ 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%0A(L7$v2l8e9>mAP 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

      P%kf>C2WGe=xd@|F84IH z*qQh)|8>YqUYoKEBz2mwFeAHB&puXvrtMu+DxA%h@;Cbx8ygE3D;rXI26Neh(Kt!o z$b!b>YF$irynS0{4G;SYcdu_Bqi6*pSv5?8^;Mn9=G)lOrK-0Sr~Ot z7uJxYBzUQ|w36c!mObFD{V3QDg}Z~v7s6RS(Sii2~q`;w`Rif;4#H5w=s@b>GHlf-V)<* zp38{PpT(UfDqs_nMM*!Bcfco+3fHR&WzBn@UaGgM0=R2g!qm{A+qc^74?Fu->&fu>uE>4~75TAD1l)hBb(^*Hf#+dTdUs>G0 zwDJxPbqkDESlP!zd1Bu(6LnCOVLpcMQ)1c=)NFh{9)4$wixuWxO*9J5zM9TPY|}iQ zM4hL&x>3X0JvB}`vy5aE9G7u%rvUiS==1ww%s(+N@9SD0*rDmU66f?XetZdIlnE!zsn&S|pV*Su|qiRF&y=L$t+ifCHh^PmVyMrNRdD>5sN zDI~i{Sggptxbu*Hac`4&ab_9!P2%v9e_iW$7iK4vOJgwF2DiI-4eqt1cDCnjxe&UqZ2%EP7@A`3og_{u}O5_2K|kOgFzoHv?_b2beE~54+LQOK{uC_ zb(FD@FAci&!)N7c2jr$`Qg9OOGpqMmj0byuO_zI3`qX%5T%0bw|8kjuI+Z4!MlmWUrMWXyR&z_FwzF6)9&Xcw+HP$*q6OD43iMFZFkiO<6IzTdMN! z6a_0{F^hQFwHnV4>+!(G3UbD*`HVKb8W*8V5wPcdA{XxzB({w^_oLOj zk7FrEygT=YpJx<2QsPk`q!D_c{sYJK_t<)vGJp2Wnr_=RK#{E4n7vm>f)Zg^`Im&RTmyG^LlXXVEwPXIJ)p#z+Ebi7)7|WB6ua;x#ZhWM|Pb&vm zv`T0*%flos04nW9Lkb0am&1^r1E)fwCn;a9b2*USDG`6)lV{TMBY+wCg z9sQx&^13Jt=1S@f8XagAOP7&15noXvIDN9ArF;pcY598zT_I5qNE62w z-=22fM&zX+Yn&R$IAP(eHYh!`*RU-~-=UQdM<`UwH_L|GT{YRS6OeR{6p=Jd@Tlpl zJdlgvAPhcv%|v}P9rjWL9iZE?TNCHD2rh^502=OCaB_C?9~CD#MjWq;XGXubQB+LY z_0K;-NOaWM7p^{CHBO&cJkVl)6{&ly{^g5c<12?J#y5`c<>tQV7nc^;LEaaZ4>;TN znW!B|sekygzFIZuLEDzgP_@fHXG^h=-s(?{R`U8+Dn-zpvE02u^|m zXA4$}(=SobITSm~g4TjKm)pizY7(5Eb^Ayz=#jtkpY>;9NUOKY)QVfQs|(rSbnVC{U9Sf-)K|Xg;ih5g|$_rbkZ(~(Otr& zSEaW|i{gq0g~CPG8wIcMPBP{0=hyF?&4^R^_Ui0JD6HOFyr?PT1Oy`Bs)z%vpiw3P zNh-z@KY~}P{GF&N<(8+$7bvf;deuvM{CXYk_rF^`XbJJG4(g(g$tPDx&y-wy6Xz); z_0DD$zZE7gMc~PI5k61RKM04Nhd=l`r^GhpD>+|&@vHpJ5Y@g<*@FzV)g|VK>V;2} zevWj}Vm~NQR`92aAF(bpCm`uIdf;@HEDZ;KLGRtCKV-{xwp`x2hsfRN0NOMU7G}cd zI^4paSZ`FaS@qZ=Ck_8W}bA^8Tf-NniFbj_m7@B!icgjin705@JZ%MWI~UR*XkoWvffn8*P_b} z%=_gC-$%9(oGrp&-~3JFjVDwr$hK;p3#r{<*#k@zoHZ3{PaQ}8Uf`1KugcYtXMdQ9 z0mWK5Ij+ZgDN}cihpAEA$6x6ZOU-@Km-zBa@Jbi3oNtv+wXJt)%P>f>ERy-f`z;jD zP14aD7o~-zxv%^)T!q68=Cdk0WP|9p<%k0WZoqr;izm|sFKK$gK##9eS{WNNu*2{zTtnamwGT9Vsyk99tFbHGb@s!0|-6_UgbN zflDYi1tQKq)|nbACOyNhdLx>UiYXz}Zaj2!YmiQF=%n|L(a&l#odQ({ zVw-CbIQdn%Zy3zW*JPB1%xjaK|JJ@8ffT}>Y%VOSQo==f<+>_}!rf=npe{=6_74EB z0I4-~K{uT~3}itA{-w1;NE+IDAC|f=r|ykxMVbN_{#mw!0N5?E^oZu}T;I~aD(0FK6>4_DG%TT~e~whaT(hGkO}AXm4B z47>Kr3z;tczdxQL8lPkt5LG3-t$9{Lxz`&kv zO^NXa|CJ-NFfoZJnHO6Pq44FC{A`v4rd>Smk}U8`=B05ed}Z*n)hlgCA@X5@SB*IP z)RKa>M3P`)#_R*T3i{!euQ8x!8-%sb3og<>%Nd3hb+=aS{CE$!y{NCw(!nYt=QcL$?yJ^#mwL&kL3GQy2br3NC6dWtF@PjSp=6iXfPzR_+D{HiwF}jA^2;Gu zOP1ejwjV;1*BRDJLTTd;p~1dR?IybsZn`|0e|P?^cy#W^WV~w7ztxFzlZSPv1}L$( zSbNe2-SMDsk2NwlmRt8r!gx<(jdJo22Y@672@4V^+4vL6{kp#q(Z{YAu_yWVt*4!h z2X(OG);6yum0i{eqOJQs%@giN+^*Ft=WBD!%{>)_3U$Kc?9WtYHch7NE3}3MF4^T@ zwaU1Pjd53Dgc(w}hbv8PKszyReFx~~gZbc~9EOFMEB%_*99F40NjH_JAU!hrr`eU#o_ z2=nP<*F5GCfOCv*$txtl@NXnn3{CPA_7zBH{wJg^jgF9cF_`;EW=cVawv{Zgx6tjq zGO3$*FZ1VQ0Yg+icdAM%gLN+MbxEB+oUZ5%8K)Ii0o| zUj4+PAldtb*`Ghq_^~Cheb~Mm(L>{Kub|dJxh#ddgw2Ggx8Lk*3gs1fq#yZ#5|xdr zaqUnyg$^=xapy-oJax3uq3Pw-dUi$z73O0Cl=y*~1&_$TS>IP0yNF}-;VAw!cU>vY z{Mz|SH_Tn9q1c57|EMb?x)7To`id!O6xHOAF`4s++fEf;+)*UWd@Tig-~3)``n&VT zSLk=0ckG8a{9uQ0TY9;G@_57&NKFv&DwzTIGRp9U06^m?nojbC_yGbD6H>0I6K3cLV(jt7hB| zoJ{`@XUVSLiIVT|`uXmt_>bzJfBfBKotl(>xkL|CXB6TXpnzg?e}2Pu-gSq(`={I%gOU3G3KphTi*h>WoB z8jR8JXU(4shy7keLx!TfrEh8;JQPVTmE!#UCQztGeKJE=RFdAA8Yn?*`KjQ=s(9(x zef3K)33bdf3Y;}-j7F8g-sOh*_Fqv(s@YOc~HVd7r!ZeD=%}c;;C|Uwd@N zz~4g{dv7V^^GIImmwwLM+mLO7?TA?2+?jph(7s|WVcsjz*O?PJ6cuyw1NW`==Hny3 z@1F{;XK_pjpE3RUf-O05ICY3^Sfu+$89xv zUf4C|YHwr*QpKqDr{NR{imgy_{Dp!;NKv~%KGaTd&cZh$eNS3`x?J;Rx*e5jja6n3 zy+zBxd`8b}va*6nohYjh5we5S9mYL|LHqo$Be^cT6({9|ZeV%rw;dH)D{!==Q*Z@b zTk$v9T@ffn%5c~@hwK3Uf&MU&aX2{pfI2&2B_{m4DMOQeq7CZO#3o!+GV^{{23P#B zqmO78*LwB2x3`2b~30)NQ#7bC-LV>%Fz7y6_lPaE^ck?4FXt9WKZ=teu|5?Ye?Da#OJBJ)3vh))Z zG}J|?*ATE_o3^R(Yz)iUOJIp0inm7!)dmnoP5r$xNSDMpD7CnG(mJe~fM&({vcA0J zuWOb5xAz9^o8K>y%w-lEKM!h!M1eKcre6IGn*e@o;cSaMD{DZfgU!?f*}jfUo2>Oa zRPK@&2af_`LXZR-nl;nMC$CG8hY#s}B}@YIfyMD?1oH>sZFZ3S?6^?}}+K6NM!5YH%SZ?gm0S6JQIaCrQj zu|#Uxttxr?&lg#P6o>AX-YqB1xvk%K*xi}=NnZv=Dzi7eozRzx|7(KYDlklC#X-(z zYc#)GQ{O#F>tEwjFam3iv8e)X6|EM=oS$jDKd+f@trCm(oRUyH=Db>BJh~W=P|C8e z(?NeV37cB~jQ`rEu%`8HjEMCxH6^yRy(+ZGhHF)zU2KMQXr`u$OEKe_P1mSw3in(I zP=RjQFgJsvH?Gk0tNaYjfxPil(XMdd!N6e#?L(v#@OB;GmXYLN%t_Y!foUMAg<(fG zAq^~va2XF$Wn}4fvKf#uPuZ!4XAVElMtiA1Yxk?j1|MUv6`DIS4U|x!Soa0jSYe4W zI!!x8D17FKTQZ$JeJ8B^nzp4^?zhNb4}xkFrZjr^9ruHkqLj3_@mqfs`Le{b%a!6;M|2BqB#K$k1dNlq=C~} zfVohYO}Nszu*3RUZQluwt)-$Y*!YEXo=!~nhst_J^-ETE-koi&6Y#-rUCOtO7= zCO&XIL$;Z? zUkD_#G2Zh7Rb))K@?j!R3mM`n=j;@HW(ec%=uvl*ZH)RTQnyZM00ajEP7Y}yT(HMM z_jn|*<3E)dijy%N?iQ0_<0F#=fL(6{?qtQF4}x>tij^$E*B>oOflJ&tI>f>nUn8!^ zqpJfY-TBGK7`f74w1=u)2=J&!3C+DV;I*p0`ibAfm1nAom1`+Kn{&#F&5svrd(%f! z@5cM#2e$4)zQTX>A!{qS?~1XrH=_Rh+M_g`3nn@=K7;ju4@(oH#WykIGb?q4G~3B$ zg3h8RV|~EelTFI#1aGmq=~~fQsrj7A))m|!tDAPn&q0!g(q_zmg16nHi`H>NJ1PMG zX5%1lcyx~J6XnO4Lf4azG!CHAWSFWuD{`@SG4F4LC}IqZWn*a*R+>4^TW5@n$nbMr7hWJ8a+5;i(zR%9$_$+w;<{HyFB}ycN@4?w z!R`$Z-4q>^8k#rx#kh~ELDmZc9>n&sx}~CkYT8r;juvc(I8K;^pfPB#868+Ea`IFz zFm+UE`k?gGSbL{V4bludXfpNgc+8tzN~Zo)5QgoeID2JOM8H*+m)=77pHM!sJi!n$sm-;;)21+hTDH&JkI;@2d&G0 zqSWrC+3s~!dsn5Rrp@1c9AkYAr@4#vavl%ht6ScsHUP*OG+YyXBKlNE{BMj0a6Cf+ zn_?!?4$HBdQxzqr0%!r$9|Zd?uJAv=W0nAcb&wTtci6K&gUpwc_A}L|`WcOrtD4OS z$V_pq;L5o0!NOC|s9RN-jj7qmq(KmD_n>>TFK_+&5Q8rvO}x zHs+V#jA0Zs4%!>K|serzfzdf32DKpHYz5II|Ht(pxX+C;FZegcnWfO5fXjK zZ-odP1L>4N<+K`4#dCoC&HOXeHs$XpOcq&gOoj&5fxK4+t*GnL@iM}l1r}ixiFpqt;gz>dtT(*bjg)lg7&H>1uQ>X*k`+DXbMc#Ot;LFYQP#`eeqAVN}I$ zhnj}Q#4FuQpnBobahE5R8Y__q&p@5IUG^gTw)kMIpivK9^R@U5$3+8>e?tqGRjpl& zA$Y!c+(9Z(^ed{i{OHA1gPPjJ(X_sHwupQv%(0K>1}+HazRt0KbKseoSM~MNUWyJ6^uBd3eDJ4aZ(Jk)EPUR5NN&qH#@ z=Gw^^Ro{Yx`36ffP-~Zn)=qY7rF+2b1@|f-D#ls)qED5Lg;BWz*>FgyzB~AtZkT6@ ztbyCcnj+gaLi4ex1B;0$NTwnj3n~xcZA}a znw+cTePOPsM_)+^_-US!-&Z0Pd%5~JqB}oE#a8YLY9Gy~T@?g~j5XqL#hoX@7;L+4-S2drf7b_j8d^A;juO@p zg6k7`@JEvPC;#x|mB4^B?ec3H%&+3i2~ z&#upqNL7r$MP{tQCg;mG0s3KTpz^7+xCha zC#t3nFGpVo{Iy~P`S#@56s`#$Y{{GDta=zGgOb58 zg-uc5$WbFlv&?nR23KBEY4aZ_=c8guAOBU_#d_bRdyG@Qwv}dg}iZ}<>u=* zClircaqcv8|X-?S#(;!RxCgk#%HaQ6 z-susRZFiVK2G0kMvZ9V_jvvbLbdopCrK0~X>I0pfU4S>--0mWeW*<;IQM>33X)aHp zr$6$jgO#<(>T~^YzOV^6=DOI3@-fWwhfTFJ!1-4vUbK2XgSxr=?h=@&-BPm|zJv4(lsUVU%H$~??&WX)4E zK)(~u;#{@_1K(HH{VRm0tHkD}W4a5aT{MzJv||-*ZRlncM1DOn4WAD<8hCu&5}t3C zS;}5M{sC-Omr+G~M1xDP#ho5tE_x+`9OcQ#0@y4Ro$caei)=VBq-(E(qPykoilfV)9z43~~@6U6yp?}m+T`81@v#dG3*>`QpSdK{6-StHs3EB1iWV_ zLY-q77P=a_1rM3{4gKB6-S6D&LSX)v$|zQu#v+E_<#D#Fm+gRU&aC|>sB-C@SW0z3D`gO zOFg{*Pi$ZVIaJfCUl97hvkinQq#du^w7US%BLltIaT^LZ*uWN-sP9Y@oUJ) z3E;w3-p-%SqFFPYF#w!j;!SgR9N6`w@kJiP&&1<5*xjmF`}-;Y66C45_&~V)aZa?{ zlk7;*Cc@OA;fc7@@T@X)-b1eFlC>YwL0SyXGY=c(whf8bo~L!iO}zY2PEQ;!irK(4 zv42Kl>=ctC?)Zt{Liw<^@jo#w2X>OA4@;inn#W_ieca!<>ro=PiUk z>?;CRmwaQro~}j%ah9m^w8S6ox`-8UtlrE`b@mnCmjS>@cU>)^%jFWjf)4<(vM#9d zOT{i0n5Z}(nLzn0PsM%r<`!hF-8!eJQ&|K5{=(>{j{h?B_dSB_D-j%Gl^$c?xVie6 zf%TV2G%(vx!pk~GZ-p?Qh|0YFwvGpzx(>f4hZvfRuW!cs&MS)&5B0-J?@7j14qMRu zDINAojJ80F+=&lZW37`8czN%^q|S8`NecvVGEo|qcJq&*k}N~gB-dq!6$%J}0$ev! z;)nt=$qt5@73rm~X?D7;`3hy@al?imPam7_2gThC#|L^0OVK+W|AAhNX17o^{vnqf z8YvMprm3?vZPa~gkXP};J?UW_GK^xL1a{u8;U|6s*HTnZz3ZWd!oU7k()r>3s;nG# zwKI#{qknV(x4k#3gNP95J6psJEb<*{%y&c&lFk4xXC}X?tf`i5y`1E0eXZbUF8U%m zEAjm?Tde111eH`z^pnivDGCqI)6L$}_gOe%qFkLE;fcpJ=aYP!rgT<3zwu%d{O*#|B~QH~cl%#?*3uJso6d(Xfoqf@*_)5e zp54%CbAA%tbqPr}c_^i;zA|)`dCOcr>HGVN=H^BwCMl?#@|T{H@Y8b#dk|gckiEDE zb>ZSg>Ed=5`x~WUD*A`U4``SFr4u+0>8emruc7{=oU~eA1YFGXuI-;YzwXzi9N?+X zx<8fe{SW=vtn)G8`#?o-%(s}h^(Z|JvRHtA45vMvUy-0E9C$I3y7$>I_Di$;L8Ojp z%sKiHIQsoDEsc)v8%4@*G>0ENzD{Uhju{=XD6yL_81-yd^w~>+-kutZLP$KbfFOKKu58x?Su2_5#TV{;TPsK(9U) z+KexP2X}q&#qVx;tWjq{@0cD#TOPkI1eT3tUy%pCGzUc?vJ)qhZ2Ze`^zAp3$mMQ^pulDPzv^xR?0YF9fi0slVd>J4;PWv=WNxAV$0jZ|nQ@6Fb9l&)b@+ zC0>g2WY%qxT~~9Te#Wt2ROPmH%@+3c2WtcV_?uU`maEKUn#TsE%bQjumYtkzpC6m! z*}T>{Fb-?B;R*=J*Cl@EKk1}}!G9a?XuS})a@jgxN6C#A9~pADuGP_5svua|@2$hXtHwiS35tt-bC#fDGy5riaL=J7NQW-R@U?NG>Orv|xfz zMlnd(=t#Z}rj=uAB2$t39ek$Cu}n?ud44l4Pk}$Ze1yE-K|VJXv9!a3q1l{$s7C3s z?Eu9wi6s6_6NkEe!O>3$3>Ea^rye^T>MZ=JoMb<-*Sx`Dx8E%FOVe5Cw_A&n1e1qq zOl`K^qj{7bV0o?@n$O1cCXf%DOeDKP7C$vcb6nt8hT#6AQb^NN(^{GqV=(gWm+*X? z?A16|fuu{|VPba1Yl#7v?E57etkZ3&J{9b^HoY)XQri*xBto0yD=%+W*989e>kFanEI;AF1AtR=a1-Ne_;-gM?1vlsc@jHV!!{f zKp?77I{dfu^~(`WXnua8ygOG^aM3I30wRQu$1Y_GHiexF522E?bb7hbLyz zO2HMA*H`#aH)SMWE<6S8dL(Vh$#82yOH7_zU_Jx}s;un4zU1ybM zV%&O{&mc8T86y&E=zp>Idn5xX!<6BlVRF}QVfk$ZQMs;eX|d<4p$F>282`mfxNw^P z)TNu)NLg*`kK7m)^%?tn*`^aU+v^*!>y66*Vqn`87&!VdeWrUl)=@JI!zA<{AEWlTFiu}RM2fL`IAXBqcrP{G-oes>?b!C-m1su zEA*?5FF>)4eFINmHbIhko-$hlOr1+5!y4C&7)sPd14w%2 zTsnJV-%I}HkiRiL#9yutUU0fSh5yor7v@J>286`|H7PQm)(z- zT;mmec2tJcHSmq24VLf9JW8#|kM*W!|IQqdvoBCCzY|*hFh}?hqGCs>WGkw(42z*O z&6K3CoVgW7rjDcCcGnOZrQIr#%YF`vRl)I*eTpgjLg~Ooa`6<|k!B|HFb$7Lo|3-t z2a$(F)!J7y+lyA!Jc&Q9CX|=UPhlguv+}HU{kd6T@CvA>;Q!(1yrPnD8!%p3j&fvH zDvDNSX0F_dI}0^eW@R}ucQ_OG&eZ%T;@)V6Gxy$_;$FD7DDJJ`LPh-WyWoOz;06wd z_kG6i>H5J*$n8mIlpB<;+H$Y)KN4g?MeQ_dVO6O}EPk}fX@Qa}ZUv-J=9$Fy<@ykn zL$mxUt!29W$($dro_NfyE_k(D!PkFl^*uD;G#(#Pok(Cf^cLW3HbSC=WKYY1?7DCO zZpdVzWA=TxS<3$EEO}_-DSB2a{$A97t+z*4-!w>~Iys)!ivi*c@*5~90rqwewza4f zC?A3!y7PLM11rtDHFue}{@95T^R49a;&n?o;8c*DNvhvhbssA8#6I)DqN@!8+3CFT zEx*StbNf{Q3sW!0xqe+3Sl#&{m?=J0qz#;AX!vo6+UqE$mPJ{hF#Rce0(-#Fe@j?2#^y9bZpB5?lWBb^Frx zILCF1@QLHG{PeednO60yv9mdMq`ZPjyuW_%s@G>?XWR6a^h`dA%LDMCT6T?_+~HOA z%L2ez59ax}DP(JL)>hXW&;H#j{cKmU7BL1?8h(U#}SfAsYS@zuyVVf5KT?r;fjynzD)?zTtRp>z4+;d}Mye^%X2<%ic!cFPnx zmXwpyo7$ZTVZZ{q93u|MVP_)HzcGian;(Z1DnUYQ5NE)+e~B(qkAbgpfP;OZlN5Bt zI;^iotfi$?ui$Ul!=z|T2e|y_6t;p0$%GTyL&myh36v`g|vjyK33m$p+ zvO>8b7Kq?4_?O0Z#W%N8VzhBx(_tf%xr|m~{2Jm3P>SuJ7+6t=)XwHGGeUnV->L?3 z7Zv!F)-0|?TAKB}&qgC|_~4r+x4JRwfOOGetWpQ-{uD<%sYT%vaDlT)O;q-&R|tET zvZ6NgUFAQK{ltH`?_a&ytti&3sWy!=i-~ zC*KzqZ)G?s_g%+&1uq3CX(wiw8K+4Lx#(s)TCB@B9C-dzbsr&umNs17RU7bs5dC7keyG4CTtU zb7VhsE9N@WHCY#jbqd>rpX6wN(z7f06dPG3&6%FbVN~y4!a$k48Hw5%L8^@KOFwsX zZn);-{QE)O9hszlpEcJX`%R17Z-TtD-caVOpM~XBZ=_LJev4pidN#2v%HK)<`KCQ@ zGu}}2?sx zII3!zUH$r_LnICsuYFEfo%SX)jx!WGKmOKvMQ4o}6^?XGX4RLpb1m~dnsZGR3XAZoB`-*6>_*_ftP%6CqhTS&F zMBd?^i4m()9z{dJa&3^6Rs7@ajcR_YJ3n=SB>Fs|tM@q`MXyCVzXUgQ)=*nN5lUUH z4L#nt>#%Mxgie2pSx!za6W?2m?$tpf4sXlhCclX<4BW2?Lqj~8mCZjQc7hlr9zk`5 zjPX(NV)lFcpIpS{rvW%Ys+i^5e@_9*c!A{BNE7H7KG`$6Hcs0G_zYa7WS)lpf*NKa zgIOicy(Q%1=<4_02L6=)$6x>Wd=NYe_SHG%XLPSP{&M0iQ$G^Cqb1~d`!o{151Huj z#)NT9J~k@tH#Hzmn28OzO+0GqH1Aa(nv8`HlD2xNKOW;?@TmVv@gc6Dx(BnH;!-{tWb(OE5oUbxjw&@$sOf8LS}h zQre{pCnJwCPv^Y3;by9LAm2z}%J+TSwz%FsZ%tar>8#<&YU5 z>a6iXnjTev-;ii`m1QW{XLpwCbzSPE&``ae&Mj~v@pZ`TzTm8PW@+A)M@&u`uY5fh z39b#&tgjW^*Amo9kaKG&u{t`vEH9kpiPF?w-!&Bqn8k#2c+Y@pBsA`{R5h`;I4j__>*)-ku@@&LQ`U zUF(09j@}7_kNkQl6sgd&w>eIh!1wB}n`0#!(}XMNK$8+j>a$T4*Vfr`skzb&-cyxXev# z*KEiURj9S!`OAz?s@-i-JhQe{lrXT#r8mJVg^(@$rFgApqArHg+)dC(-$5D?9k(vy zlMu?9h=%$z{<{h^Wzbg=Lz01aFP0+NnnnyHI>Op*kppqTpNwn2g>n2==n1iVoVK;e zxRdwtB$`1q!#2D=If$ZrI!yA6OB>10mQGsnW-qOPoFv#}aWng@#bh*4aN%33u$ zxPg&Qd2x+<9&#=7vpUEkEDj@!7Vhi%(*#Yy5=w3xGEfe{G(%A^-yx5 zU{IRUAuCdft3h~H*@JU&)guh;pyf=yJ9Y) zP6NlX`%%z9W=WDLiJha~h(xm*E`=n5Q)J=QTnxwy4QJmFnRdEuR)+%xyNO=3Dui~r zt;1&c*11vnBB$g*$H9It>7A%c3+V@GExi1W8!dYOCv(#z!U^oz(!Pf~iD>LyPifjE z|0(ZaSJ^3jkx>3n&ZwCR*M^e4)>(a3P?Tp&;=Hkm|nY z@6P=vYnbDZIf)p!%9iEQc6Zq)DHqdu8G^0?K6m zKC;^oLkTjNav{Kfn=%A;S%csQ`1<>9Sy!3aQGR=moOc&Of{!y;n>ydKH?b=0w%lO` zI%-VVCDdIgo3;KV{@9M1``L6PJ9InM`wy>eZ_iJ@H0PvdAZ~!wl>S%!TS-Va@#SoJ zyy`6U-{eP06;LYfdz^{+y{REi+{x~O&yq{H= zKlB;?7bZnepNkI5^;iQ)uV6+Xu+IpSHw@Q<+ig5PA%Xhts?|_g6?%`5=(51A8%%<> zbT2kv;~8tyW6LcYe^AMyqIVwpsHHd`7hf-2k9s|!^Yb(R{L}pP^JM$lT*+@^0DEn! zj2c6%9oz3BZQc{#$M}1;z-G`mgg4G(^UOo5ZW~c!wuRAYT3i9n zCkU|qYW3sWh!Pdk{N(WB#*(9h5jn|Kmwm(c;Zcgv^`}eD8Jn-Kx#iMRb%l3ZZ#*B? zKe2V9Z7O`d0I&*XKiA+(Mb%snkNo%X_^o8$NAq=iT7_VYbpNd0wbi55oli$@HS{50 zKI(#f=f!kOf!LcDJNeL}i0yEaLgH~=;c)gMls3WlWLy&y;x-P(Lxs}7ZN<$X;o@vXS|T&(DRgG)*3S*rRxS)SlOJo;wM z6KTUKY50vW?fWzAz#EP`0q;pnrQn0R*gZmngX zYCXQKj~Mz)H!Jeqd-nd`u=?i_s?~Q_K#7@QX1w~T2cj~<{cj$V610egv0heLJ_UI9 z59?v2Y-#9c4Z}lydg6oQksY{`a;Oy%65F4$L-5TBhgG|EtU@aN(TasGPwqxgB(uE5 z-reJW@ZWZ|71hF)I-l%>ni0>3BjHVeLqu`eys+5#QcSV3JpomxZ9I26R#j=+ratiy zu!?m9(mWMC{q5_0RF*bNZuk+wbErkvB{P?|KnnV;+Vt=XR~9D4GyTyfEl2cR>>81y zblm67!6HkYt3VK=fwBsam-L@r{TFGH;U^OT-=g=R5Oc{fVH1bU@V1LL#5#3?_@?8V zdKT3dO(iLZMe=Q^r|qrPL&Ce)*}W(E==D&oDkMQ?q5_JDSgrTVrrbdp{PL>tUHan` z>Ma;gk5M3E8IEoI#{WC+Sw57-n9@|kTjlQcw>aYeMzd0MNQ^Z;$Jsyx`I&?c(LiOl z`-TFDrZPfP*${AhN;K`Hw-VC0A!9yX$lc_A@IKl(=1a%~cF!veC?7m$w4TzA;f(Ho z$nK(^`nH1}J2%^Mas0_r>RyX*vuBOGn+q=5%#|ElArBZT*0;X*Xm1~{n*%&HZm~4n z4ad6l?oZuOTW}6S9lL~ap;i8&H4NMr;HG7Bh4l&aF{&YJX}abtVyHv8XuVCt}|bC8@&a0Dbd? zH6aT;3{XtTIQ?B`TOZ=Mb+~WH8kO4!c1<5I5)3=I!vR3bEa2vXWQJ0Q=^D&zZus@Z z%Gb5qXUMA54`(qN4}R=1SOea?#c*7|DyMB2{*bct@B zF-4*%*B}?7xin;A-R7X_OH*e&OBUrb2E?W;Y3vIOuFjqGyH)b*KBo;EbeH$KC&m2I zQJ4CrVEGB`waDo$U5`5^6t*J_CT+9`BizX*Kk%O^TEX{S&TLTM^0pF)5Wu#%<=HwXcfLgiK$M%8M_(9t=-><`!uK)WTnag~vW z*s3kXf^*oH?owWWnIh-d{b-r2deQn5Tk+6^+QnDrPVw41e||G}smvt8GuS{LaP^{5 zh1xg+bySb4D6R&_c|Mk^yZb^&N^RN;D&w;C5y33!oj;g%4n7B@4R)OY8uma#GBHJr zlI0yxS)PJwvlQW(eR)+`*FFt?&yERkvFIaJ1z)bY=H(}H_Nh!f(Dq_NiPEH3D?=0K z=|ISb6HVd(156r0fZGqL86wt(J1w-hcGD%Z#6vtdO??$$m@sHz)hhV|yR9yH+y7-8*Gry@BnuzTT*UBp9-K)OYu6o-C$WqR- zdf-0T1|QyT73MDZ0>cF*(wk8mtwd|xPPsCbm5Ezk;DRcn!~lRH*G~YcCqjFBg7O|$ zaVgjTql$l2pfq&viII2LaOau6tSleXw`EX$*A0`JELo`d^(7^Y;2#uHDy}`O()rwd zZ}GLRtgNuVQ_(W>ru@`2c5yVN(lk&kSlcM z;#a!I4%asB`lwC*apDHSn}*k(e;TAZaS}$Tul5=)Xk)@hcbC)?2Vl}Bk#mM_QH8a+ zg4eY<3@KIGiIe;`kBJfaL2y@14xCs!r_0CGUFWp3l(mH4jNUuxr|wg=KS#H^GY`NqS*w@kY4Dja-H!~AP1 zO#k6Dntpv+EU&oI;&b`fw1|Jt`*lpuo`hDxL$~V9O$ss*!Eeep?QCYX(!qO??d%hK z(EwA1B?6ObqGg~g!;4mKV2=g^>8|?k)HeNJ6BZh8v243Lce%_IGYl4xR4~e|LYUK#mO)jYy@qC+` zS?KU!@a*eJcV~`R(>`|SN0+;C=ABziX@pjqDMFq&HnX>-<81DN)Q&pdPd%<@a{5}} z$(L4GCx~)=v?WgFk$ls^CZJ^((*Y6T&KGBMegP!q{H!TQ3kdXUMlUX<0W=L`{!m=&Vg^#rIbkVPQiNyM zsAhDI3Vn;trFg7!A*0l;rMy*p&+F9SMtRW*yPwC`7!b5@r?mfP4s-{X22?=@2NptJ zlfb=>)2BmTnEllD(0nEnqwd4qIpT_Bvad^2mCMcTBeED}xOKa6sm?yai1>gHF4?fI z>86?{caJh^)t431r3kt-k*kKT4?T>R28xltA^7Y=#AP+xj+yf?M@7K94HG>n@5RZQ zA)lFo+^pL`zA=s+*j?JySRxgkwl#iotfJE49!)SmcLD#h9i-8{@nGHHE{< zpKe|jSzQ+Q0vNnM>!`v2%Qtis+oAE-L=tpT^;5+O4Ng|f)J zNcF{wOVxE`bZgi&Lc|fwxK@Eb%ZgAs%&OuD7Bzwk>!-8P@dpCY zwE!=J;j(Q#l`RJdlq0UvEb!ZYpQIbokZt+&iN;BudLJlNgnMo(t;}a?ICabyDkm#= zT}UoRVd-0sK$V!ik>P}s3;oxz-zBbHhAO&K$lmb{0BbXn`6`0d7sL)q+8DNy-%qrI z{Uf{n8a5*=ldU~oun9H>x>GgRHMH!gs_XmJj{bMn&bmi~*h`iW1OcZHQ9y)V0TzA= zD$o-06*R7|_944Hbp??tmEeu(q+FTwIcS_OBxC+!vxx*zwPRTRWp``MQMJm_Ik|t+ z__%gw?`6lM1%5*Fe^}Bk_q?dHLFAduuGN_k?q7RG-cTb4oW>*uK7TGx+=4DiO?iEz9NedUj$>QiR)`2x5;4gZ~;U#lO9LaF-ZDGT=?$B@%-VNRtQfN66!6;~^@ z9F|7=BNWohGgI$(Ha%w#u++Ac0kj1MlHr z-_7cSR8~cMMB2NY&C^Y1GEpXE%1?6RWZhCC{WxCj434mYuIsgkfw;_Xi*C5#+XTb* zU=H5M^)i|grlHY?QFGEpE)PJ3wkkPAL7?z(4pn7|g(YbbwGd?#pzeu}Xbl9Ek4ru2 zrY({Ue?F!n*4)S%4wV`zc^zVwiha%|j%Nc(Mn`z7@wwh<0)>92C417J2Uu9QN{=hH zrcNX`pTF2@xN2#gfhs{`*il#yWg!C+W4@O}4D&Y9Mq8bJvZZ+)k~QA^I^x`r z?!|ilc3Rubv}RA{oc5w)hIgh=!%g@lH3ze4KgOuUt65ZX38dN*Cp;rqAj!pS`Cl!k zIndJZ`;DzasHWWR5;AEHGXzO)`D)IagO-_JnRWgQ+x-D6bWwbR3^C@@GTss>4!3O> zBE=~3dMbRdu+j6iRGq?}Uj|!>&ZC^%3s`@KugDRO?!OCKgVl{iogmRcyS?FnOuN6L zLry2rurf{#;yhIqCrL@ZhhKSccWp#NO`%Q=dVRC|GCn6MF0xI$>6~>v#|Gz$3k`PJ zN@|{ZhaB`0ydLZ)=&{foyLN>`)%#4-cY(+E&-o)Sq4dG4GNkVgv(e-1Kd{0Z>CW~`NfopJNYMO#yZ9=)X^<#@tCvjfelR+ z)fIDKgUQA&Bi#LEn$R0jf$dmto*5aCIC9i`Z2os(l$EC zf+Rl8-`kGy!M8O5s`{uD2+bHs;D@0covLw7mpB=dN%UYU73l>h00>4sIr$UtLVIR(V_ie{2+RtV9$9YhrXYtq0HgGloW_g&y395 zx;~jx$)S59S#6eIMJK$xa(;C2Jz(~V=`OTo8e-EX73?N;=C0lGq+Q&jEg9{rixINo0C99B!cs#cG&<7JAI$Ig4&+;u(k)s$ zGgB9?@(D=RPq=^Ln%5Md5-YOtW_=k!JnqSCTDxQ1X$ynz@ivn0B&;@`;q;DOk9N(C zxr${v4cXUUuAG6y9hIy-$3ll|^yC}E1_R95S=ZJ8m=39h8@4%F$&L9B6~@OFV`W*q zlJBdio`5Cl*8XDxYOP-7T!=)v%U4{7fcVcd236xpt;mj+JE`A7`4Gw{r8vjn0WFT8 zaSb~ptL;dyUeC?cw9~6GdF3c=FAf5mOCbH(plKSA?^d!!b3RJq6JLB%5Qb2GH|#J{ zek%_SG(C-uX9Q!w7xTFtjfUwC6=4-l#fbK5OMkC2ynf0c+8HV$w(7WBl2}HfMsjYP z3r@Upi}zdG8fHUiA8zbi`sz}TJC%dP+c}-~+09;0f^Tz}iG{%2S&a(bZ)FHCm=vt$ zDV?!kbZKx#!-IXK!g4e7F9%WMK6v#6DUe^-IvY7>De=+#nSggv-_>X3?fwtF*OWPH z#j3f|_Qtvc6$XlBTD#+KARjgfK+pa3Qpbz2V+U4l)<4Ee-|fa6i!0;n_%KNqNhX9z z6$b&#nq&{Vrb(-HPG>?h<(awk(0)GFV#(#s*9H$=wm>rc+tU0JzjQYHFlNlq(j(i7 zS;%;1MC1cnb%W*fZ)gdRGO=LUt53W30scSwxfQRDizSyaY-Bq-#H6-`xRzKBQM1$a zV*jvT0q;-n#lJa&)b{_d`X>CR$Rc3pC+8o@RkOIwB>J-#&u^dR!a?70i{O>D@I!u5 zdf7^_R*NNOL7}!HWk^)MqT`QM785xBs~U3itW&RLabfcfi1op8Pf6?Ye+v5uN77jf zxL(#dc3Zwdhof#S99kH^N*1;z-X}m>f^RJpVq1mM(kLZwaLM{QF-g>B%`4q?qE!bt z1#Nq#qQIb36pF>KU3&XWxL#e-M&$ItuCpiWy#}d+2a81>?1vbpi`$ltU#}jWg`SV`H(g* z37D5YA4oi3Jr+)Lo)F$WGw1MFk_-nHT-hi#(l(kl4jP)PlNeICyLEc#mNjt$e}9EWYfuS!E@0Lh6wa zQGI89%s!2Uo%v3G&}3Stkvvl2Kz4VAoIY8S3{I9C9L)2%Q&1#1a)$%qkEC65Jb1y= zvAVP|%pXz#MVZX(i8ggyd>sG$GW$b=*ZHQ&Tu)J4DiqHjIZC<7lva+0!qpT+SrXS- zY<{LGjj!!`#NBGjkPL>Q9sN%g9lk2ONxQGH)2-z5S$nNdzkd3QiOGdkZHU?Z*p~XK z@0xHqNafIoM@0C|cP7^lJiTMM?A?1NnX(80?(5z!Pr#|e5iRLbYng+;L_&^YtA#m4 zo47P#lFyB9(&KhyoZloLOwsuc{iUnlE)rG{i1=kuFts%Xi85D`H-8$IB7SXXfnmJX z>1ZJb$}72Z%Oeo;|=%a|OyJ0`Gs4c19RuEk}2pDtP3W9Gy*6nF;`(R>izGZ-15c@Z#(CXi19A zGY&YCm~vL~3%-W^sj&ao>3SWMWp`c-lp+gljaBgO(zE-z-M|Pkf}GsE;Cnp9x4xDT zIJGoYSo&6b;N1tz`6FE@!yuU2cSyD2H)`D$`sGm4E$oj<1+RN9yj#*DU;d^jAI%q0 zGfrKe0kz5YZR$hGrVa(-U+1CXcYnh-KI$(N^c}IC?L&{gEHd$kf#XwLGj9mhk3YeN zQ%VxmCDmQid9|;EJKCryL|@E*v%*0AAyi!`HSsg75+av<+9SiQ?6;@++L ziWLstf1oVJ-zB>$jh-CgvG4JOaE)-v$2>G-6AWnAJ`HvFl--!KUR;8=iq4A!{d|Z^ z4gWwj!r2%=7X$$8`c|m>)$Chk%yu3!OSdg6$WsXw7H8aR5|&(0(fiUz{afm?h3&5qXbg+@wUeQz*}`n=VdeYz9Dl1WgTvai_glFr z*uHEI&#z3%)O{z(vn*c`@*XxtsM?$jVrP<>eBPvnY#@DPEx#ykyain`GkP5&RF37b zLtiE07&satzz8^FOq=Ej7^D90z~2~;_?^Mzw|(QRfQgAU#D43F$MvJhvx&s{Z_!%B z`fPdYG{pliJs<0g&4XQy_;-)IZ?4C*fQY&Gy2I_+EDpX5*M%)aI@7)(F5FDcZrL)F z7|k=&nKN|oE-?+MOcp-e5!X1hNysUf0mvq2v?S{&EbHoMtTvp@?(W>QcvjSi*md8d z0DB+uE@t&7PmI(&NCs)AtI%eB(v8#=ZcE&+ii;l<;;IHc$W^g1k1<-tUfo@opQTnr z*T^@`B>b_Ub;a3_xZxJsNOx}Zo!%|%d1X+0A8VW~J;%Yg^;?@xxAPrkjF|0&b`_!Qu#A$bZq<(50? z(=)&sI5RQN1$3Hgt}H?H{LBl52RlQWs;z@u4V?r+XWbWJMjtk!8$`gVJ4)9;hgvBaO5wt z{6mUbttjsN>FYJ7I$(4-<9iL^eex*pwEXO4WBt~jzPT-inhhx7`>X7T{&mBISEcJy zw7=;lK$0#TuNi)}CN5)W#yO4tTtxDE9;vdv959vmGKSSjd&@bgaTqD5G^{6>3!l9E zOX7{+`mw&zYZ5j{5*;u}AFb^3cDkzjX0&`nrJU(Sw&1KrZI_vKTWr5|-bsg$(^?W; zu3(IrD0Ij6V}(XeBj4&^7VOAa<{`SpZSrhQ6OSehvejxIm_F^FZWgpKk20+5XUR#7;`U_?EI=jC$72oqXZ#I=(;05RHm(+b zHn&h0);gy2uIo6x{@sm__($Psfo?DAERD7HYK|5hv!`4dT3J0f(UjEM{WB3o8O;SZ z8xD=6erf;{g5&l%JMC%n5WB#+L|UvtwJ0Tf#B%v?WFyMyYc2jd-L7=X)9N8MhBr6S zKyplvkjEWwXZJ-mGF+D?k7CLfdTW5~x2$up@=)HVctLd@&!Ito2U6E#_nEIBPk%o^ z_dlX`pyyp&m8H2&%GjJQ-nilbshoew;vwJA@8pFfvE)mT1?($v{AeN#cWlp!6Q{?6IP$dOgZ^|MLtbAV#tBs7BuFn$+ zCcRekM}9-FO_a}l=GkOUFUgEN1pTt(QI9pzpBo9mmB)jTHS$mko5{6~^);dI~<@NOCg++%yM+1Ei`e83b1bBop zUn1f)=p@olr(>DReePSez6IB*yi}8PszIg|x$u8d(_`lvO*|H+KS>ID^8{>Y2^JZR zAG}h79rRj?Jy=%5wAY6de?#7d1v5e#(;2i35Au_ziXPILsG7sPTGeWun1UNcqK&)j zL06J5&GPt$-ggN@UW#E!o!8|haX5we5|Mw9 z1D7Sgv9=$5g2uY^gQ)qJ^cXH@i@>>0D-ajEBer$2R;!a1cF_6DIDoN8PVgK?+6X3C z>c#M3mMQ$8FpckM@oPMg>TYgRE*~@wPM=q`F>mAI9<%xITq|iv;fcwaHIgxcsKS1=mf&=@z}yvl1#SP63cEt2l3@%T}>d>bBnS3 zv@JPIZ&WL$?8ZjDx4+NfI!gm-n6t)z8Z77X{jFSDfQ7!m4(Cm=zX!J*I=|di=E9vf>@K3-u`NZ6?TS)2ghJmT&ouN@9Qr%h#Ig@?@dg;Yngp+hH>2*{W zcr_{WN4Y^g7-Ca^Ej;1gX(TqY_E^LElT55R7k6Hp%?meVF|Ub|Iu0)^v{bpWp=9m(VcZMe~>vF9ESmzgvu<09I?dh4Kl#@&pMBngJELynZkJStPTa! zbRHs$f|R4OHc{L&U8Xi&p&)c?fg;1r_aPHS17}42P z#`=WEXUfDehkTj1uMesJ({M|{)XW_(z+e39h()Nngt-0Bl|3ya<(crc)k zYA2Jwa0dH4>~3C}y6kjqSx-iA0Mxu-MFR}sQpEOSSxz>Ebz}Gr*-*dD(X2v1KDTN* z@!{NIuM`rT@xivN?46vy@f0Ny*n#U3D}3r>78;)|=zCJ6OH#5WU-d(tp7 zrw;d2CU|mp8Z3eKh<5J|kLmu|TGM@rv%cL>bi+(>Wcx!r+)G&XHtcmnRAK6sDU7jeM_}o)ZB0@=WsnH zwP-S``|wHiK5^#o@og?stEP%i$2%V{U>8R4Md$E-?qY*$tDbcR67g&z-6JIOLNH-{ z=H%OuFOqTf+psjeN-$ubzN6N#-=~O)CWQ^YnekvZYf@@_F3)7|%)C};Ys2~5G1A@0 zP3o!kU2D7A?SzH?TD(oR7d+i3+*#sIHDK9SB4t)Chol{SaUtfr%b^cFs?n(7$*kax z{(-2|Q@J*77ZNA}Lxi22Z~WX%={8cj)-CZo&V$%L3*iZ!Z`_1mOX9Gctl{+((; z8Ft2)o`wyvdZggF8oPa1uof6`ebTLh+({{BB)4p)RR5KZRhMV-L&a=~e}|1EYt9zy z!wNM$wqCalo^BxOLjM_6$hO35Kbrl!;T_yV{yR~X6tem}9BSM=XDC$YKjJasOp}s( zz0e~>dAWBtA;pNsUNE$qCJV9-{Ie@#cYJ&6yE3lBJ-ZpWh&VbaUr(XD5UVE0-@mw> zs*@t~Y39y`Sku4?Xg)7o%0$rj%Yh2D-Oln0Yj!5`%hIcURSgeKg)|BAd$%^+nHiDc z5#RYfJTUia@#BTEO%S@eV<;#@!pHgIXL2qg-!o^}>1UeoxpK8x!^bmP?~IDglQ-l} zB0mRlcs`h9GgFr32Qcn;g2(^644~YORkr9w%Up=7espB=ql=$Ym60O4Dz$)5A@x@? z%zLg}xeHQzsq`+VRr-;4#y#IVCLhVWowyLrnN|dD3^VTjy=sNCR*ecHnEFE5eXq>~ z$p&f(Z>Hm1xM2cLt!^`)knbgk@H{Kd|bkTWvz!o zLD(I*%*r~;h1u{6$pd9cUk}b%VScownju-kz2B;Ut1rTdF7^?4MM;Bn=+JCqDeac` zJ0-d_fmOF!;CmU2q!p=Vy41bMct*}b6G!roW1kk{+x+R)EjZ+WX?Lb!^YwfZJMop% zp9go_9a1PY+*_CAbXT9nRBOSP3bFVQA^u~r-)(-uttLnY$@)GVULHR$eIoqpPLi53 zI~s@XWZzNcDIcGyko}i@9=#$g%MGmdHGkr3`TYeS*~)G0vPp&DZyMp2KascsBV8Bz zW%IGG(c6hTA5^FI2Appq*}Rq3I$p_Gl>dT`gGZYT&UQo?%L8C;#j=yR!-5(d?qWoMoT9 zrQVRTVmVuGPqp1s`&~sgW?#31(>qPAzRuY{{uKwpRid}MW{F@2A73)%{w)dBT zDMgRamT#Kq#Z3ExZT094a95+q>7fmY>RL4 zVm*zN%hppHTdATD!GPk3G<@8=HqHaJ1b-pco&Q48KTP7H~?0lDue-2VRdugtBYhn!wF zX42kDI=D(dA6jw&AW+($du7gl^SG2*ao&8Xy~i`l2oIxQx81XQ^O#SCw{AEfWlOon z5&NR23&_wN#lNlm%>z&~LKi)|eiIn4#H;T4nBMRkG%{wh5i3IoBh?V|?B8zxVu}rv zecCY)b!>*e;t;0HIdb(hprgW*hh8dM4@ zl9whV2+^aqS$WrCF?u^PYm!2*O{Jq|XC}gA95x-XW7KfXgt)+Lpttd4sFH`cd4DwS z|I+}Ku)hY*2JyYUydk(hpcBo8-#os#%!Ltpg?NeaTeT$2abs0as&O9`=2f`F;4tI2 z5$j>OQ|uSX@rp83nFMs8(g~zAdZL zE{Hp8<&hthxT1a4h6YJ>+2fwFM{Ym!a((JSORDi=j#0cfl&Hk}X4uuI5|YGsHZ5|~ zN1$r+UqjtkO_=@Sd*#8x&4{2Axpg&dkkhs|>-TmVJE1~DdeS221!4Qr0rnMI9Jgl; z4__>@XV`)yV)b6l)mlzeOk;2#B(J6ZbAdUst}%_v$1_egl80E`HPI-7LO(R1Kt5C9y7`m%z8$syrRL`#USN%7Q()>FYqH1+mHkT2&ZX zm>j~!LxkyN!$Bbo7Vp{%Jkp&b4VGqqWHo8~oetmHwP>vxO*r%d{;D@D;$ZCq8Hft1 z&umDEEoU6|yET@f{1cY6EPS=j7GvVR{8~rVHfq)}tycRrff5_=?^swnc zvWAkmAtIK(?qnr7d3P_>Q_H??>y2ab{K=e=)-}%{D@vWIjl-&I=Ur(;H8;VhA7K5J zmx-O}CNT9ahRayWbdl%9G@z#P&f89asr`HU?jte_0@7^dfAW92#Te)-pF;u8fi;B% z!$KGNwV(;dTh`sW_O~7&1;voVsN^_ck&D!2aDQuLtShTu!o6)1xdiiH+dUy!B$K> zMt~ILp7etV+XoQuv7cGXgBrDLA7esqdELLYsr)Y{a+5U zh{n@iXEh-UJQKIG+#w6J#1b26sIVOmTzcib#7!+3zrh0%V4wic?qXm&+c@-P0Y zjDJ;#wuHHW0AY|5bSon^NZ>nC4dlG<0H(kojpHrURxx$-q)2gzBU zq40e&C#TNo9Co;HnZ%Lt+gtyB$_2|v_Do0pMk)z9qBdWYb3E!8(ZRQf0v0mDH|`D~ z|CPAjK-5lFo|vgao|LXH@VdV02(_c6)WPFZR%NXQSN8cPvoU9)p^5-UuED1-)ui~Xd<2;`&}GYW}SK-Fq$=MhH%~EmU9-B5N)Lv z{qO+%Au~w{x&Mc`AxA~61>Csb>%Y)csBiL&d!~~t68GGPxzTD{I&M&Qry@SjC_bwQ zHu1c~{7lW67;%jHnk~lsV_|`@{1jHF;&sPIGrk8gf?X=ESBif_JcMmdd=`FgsUB9! zB6_=Vl`_zetR$_9Fo|bx1Dh_-?b=H+7LSN4>WKSvEbft`OX&p$d(h!2-CW8GdRE<@)S6r_~K;b95TjU%2H4+~Ya4PvQRh{gBz4 z?usJ$NooV5ag~$AIrKo~6l5!EF7fEllb&Ht{Kgz~-R8j|j-1{wt3O)dwi(PA) z(IXV$$=HBx`XKaOp;m1l8E+Zb=M8(1B9O(rMf1~keCsu(XGQ&Fb%o)$Q{VM=-zRJd4)SA73LS>qm)hrC?d5JmdeCFC2Hbe_$pDR&Tb8DCJYue{_BwzFY&U@Jvf=x3S zk}=V5KJAE4c+D(wB4BWL$yr#pW0|JAXZjM$_maH03sS(oJO7dksi zK=D%PDo5`5jB_jPQ=6X^-aI(X-^yo;hQR2(G`-XMYwvc;#{yX0NG%TXsF(36)kp9B zJNax-+~YGp*!*@bckpJF?jJe%C#^W>C8<%7sLdM61R3VfA~Ss`HvW>kUNtD)-AAj8 zbE!WV!Kb6ut^+@{9lAW?bT+OUqL1XSo`GAhN^c^;E9dr2S3v~*o?q_xw}?s2zH4^2 zM=$-D$%URGVfxAM)n>9Mda1}XE6KR2rZvMh9;t<%)3BLL`Wb zm?tApmnT5Pj3*COJ^_M`c!WHRoEz7$@!fX}O>_NIRdNQE_jN@1kZuauiy~A%>_JZfM58^1i#>+EIXAy! zw_{2bL{PTuGB3Rezk3vf!jJE0}Rz*bwjSLfO`m!9IpH z+x5@SvW-A_s4U&i&+vmsMUfYTw(XZ2=rchdj&o%`+#+84;~rBM#aHDrn13Dqx3i7& zlLjT8LqTB1K)XY?!oeuyz|K=_fch~#VB;_;Y@V{{h}YJ>c0H@_j2xrw6HUd1*|$P6 zFu*x6khtz7>^D8OuzcxN4KI8oeNU`T&FetB5!^K221LoKtl$`oK|jRt+Hb~dDm){l zoEubIf2{hXC5yafw_h7_zHOi~R`<`+{I~86+MH|Yh~wefNaw8x`ZwJkun$`U`V%#C zM#`7rjo)Gbag*CDK_epFSy`Nt6%#BSlgoef`GWtDgBn}(iTLAtcWP`Nf1PSTk5J3uv?&JZf=kD6XX>n? z_bb9if#PWcr$ZJb0FTYuU6xEFQzO%g+zFo6Efq#B$SepNoFre@)8miD_4LiAxacof z$B0q=>~X|?F?_15`?2iCnFgOzHH|K_$R=YGqtHRuQ`vCrLWTxpu4g6g)HCnsk<0Ai zjp$O^^Oz8+_~@~Oox)Xd4mWJOAG1Pt_pO`*;5_C!y^uoqjsOej(f!f{uLUzVv#Uf5AHBj-HeZyG)Ebje^_m{{m41uKYR@ zIMWVCvYDy48|sr*KU0RaVX;5S@@bhV+ZVt1#cjBa`W>R9w^|$MLZV$GF`Wb*mrNO( zg4Bu8VTjR5q%`_bV4Z=FfBfSe%t(!+c05Wi1|T%zhKA3tfVe)9+y@$ZKgPmzu%8iH z?GNZBAq}5TDRn3q)KqXZRT^pH$tjw(j@mz;gqfN*GD@6u(vION`Pj#IDy-2i`6>%C zk34t99r@pdlk|OVstimg&odksYPko5kAA7Y#LfFYmx02MWhg(N<<^%Oi_YnElIxrX zn(}RF%*+ltbH4FGU+AFD#vz^6$ut9^(|1>b*%-T;C-N@ z2Q0gS&>EldcnqJj9f=)c<8Q2G&ZN}|O@HW*X-~oDuXnNpZkszh116$a40CO=>Jcl2 z0m2wL1_oFn&@eM*ro0)8+Bno(3TN7M1Qp5m-==Ug zDwQrUVnTnH5eiM;w*&h!Oy2^X6tuaw-g;~Iw)tDSF>0^PgkBrR=)|&(1`uT0vR|gZ zrAqiQJZU&j187ISW`!7OKwjyTSrVCLd~6zKI6xYW$dI3g2_%30 zQh&+--t3?7xbjNpYC~E#I{cLnozrLnOH(QP&`TN1H^XMH09^#Z@%-Trf4Iw41I_qZ zDu9>SC%D1qC$GwTpP@we&VsSt?aRYh@6=uOt~L)g+wvRvSxX0bmv4g%GEyhNEF&F} zQAUZ|$n}?ZXSx`iSZ4gd2R^VdVWAljrHiQ0hQ*gZ#uMQ-7ID46oV|SB>k~o7+8Vu(l3|(q`T%n;v9NT{KzAA1&?dC z?%Q=1N%+lDN_|S+HX4o43l2Giwz_yj4L;eBS!QfJ2<} Date: Thu, 21 May 2015 22:49:05 +0900 Subject: [PATCH 125/208] Added comments for FillGap examples. --- .../samples/FillGap2ListViewActivity.java | 3 +++ .../samples/FillGap2RecyclerViewActivity.java | 3 +++ .../samples/FillGap2ScrollViewActivity.java | 3 +++ .../samples/FillGap3ListViewActivity.java | 3 +++ .../samples/FillGap3RecyclerViewActivity.java | 3 +++ .../samples/FillGap3ScrollViewActivity.java | 3 +++ .../observablescrollview/samples/FillGapBaseActivity.java | 5 +++++ .../samples/FillGapListViewActivity.java | 3 +++ .../samples/FillGapRecyclerViewActivity.java | 3 +++ .../samples/FillGapScrollViewActivity.java | 3 +++ 10 files changed, 32 insertions(+) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ListViewActivity.java index d1bf69dd..b7e551bd 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ListViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ListViewActivity.java @@ -20,6 +20,9 @@ import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; import com.nineoldandroids.view.ViewHelper; +/** + * Warning: This example does not work on Android 2.3. + */ public class FillGap2ListViewActivity extends FillGap2BaseActivity implements ObservableScrollViewCallbacks { @Override diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2RecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2RecyclerViewActivity.java index 07d2edfe..84309e40 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2RecyclerViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2RecyclerViewActivity.java @@ -22,6 +22,9 @@ import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; import com.nineoldandroids.view.ViewHelper; +/** + * Warning: This example does not work on Android 2.3. + */ public class FillGap2RecyclerViewActivity extends FillGap2BaseActivity implements ObservableScrollViewCallbacks { @Override diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ScrollViewActivity.java index 10fb089b..7b5af3ef 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ScrollViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap2ScrollViewActivity.java @@ -19,6 +19,9 @@ import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +/** + * Warning: This example does not work on Android 2.3. + */ public class FillGap2ScrollViewActivity extends FillGap2BaseActivity implements ObservableScrollViewCallbacks { @Override protected int getLayoutResId() { diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ListViewActivity.java index a1efc934..529d50a5 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ListViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ListViewActivity.java @@ -19,6 +19,9 @@ import com.github.ksoichiro.android.observablescrollview.ObservableListView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +/** + * Warning: This example does not work on Android 2.3. + */ public class FillGap3ListViewActivity extends FillGap3BaseActivity implements ObservableScrollViewCallbacks { @Override diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3RecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3RecyclerViewActivity.java index 6ccef2ea..69082535 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3RecyclerViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3RecyclerViewActivity.java @@ -21,6 +21,9 @@ import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +/** + * Warning: This example does not work on Android 2.3. + */ public class FillGap3RecyclerViewActivity extends FillGap3BaseActivity implements ObservableScrollViewCallbacks { @Override diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ScrollViewActivity.java index 7d8432fc..2614f678 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ScrollViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3ScrollViewActivity.java @@ -19,6 +19,9 @@ import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +/** + * Warning: This example does not work on Android 2.3. + */ public class FillGap3ScrollViewActivity extends FillGap3BaseActivity implements ObservableScrollViewCallbacks { @Override protected int getLayoutResId() { diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java index fb5647e9..90d3bb29 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java @@ -31,6 +31,11 @@ import com.nineoldandroids.view.ViewHelper; import com.nineoldandroids.view.ViewPropertyAnimator; +/** + * Warning: This example does not work on Android 2.3. + * + * @param Scrollable + */ public abstract class FillGapBaseActivity extends BaseActivity implements ObservableScrollViewCallbacks { protected View mHeader; diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapListViewActivity.java index a06f2d05..d7b7f5c9 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapListViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapListViewActivity.java @@ -20,6 +20,9 @@ import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; import com.nineoldandroids.view.ViewHelper; +/** + * Warning: This example does not work on Android 2.3. + */ public class FillGapListViewActivity extends FillGapBaseActivity implements ObservableScrollViewCallbacks { @Override diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapRecyclerViewActivity.java index 5984ef84..78c0a5e1 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapRecyclerViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapRecyclerViewActivity.java @@ -22,6 +22,9 @@ import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; import com.nineoldandroids.view.ViewHelper; +/** + * Warning: This example does not work on Android 2.3. + */ public class FillGapRecyclerViewActivity extends FillGapBaseActivity implements ObservableScrollViewCallbacks { @Override diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapScrollViewActivity.java index 4d6f2c74..2926ca0e 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapScrollViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapScrollViewActivity.java @@ -19,6 +19,9 @@ import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +/** + * Warning: This example does not work on Android 2.3. + */ public class FillGapScrollViewActivity extends FillGapBaseActivity implements ObservableScrollViewCallbacks { @Override protected int getLayoutResId() { From e21f35600a6eeb19e99dcd0b86bf379321b957c4 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 22 May 2015 23:10:41 +0900 Subject: [PATCH 126/208] [WIP] Added another example of FlexibleImage + ViewPager. Known issue: This does not work well when we select other pages. Scroll position and the translationY of tabStrip don't match. --- samples/AndroidManifest.xml | 10 + ...flexiblespacewithimagewithviewpagertab.xml | 37 ++++ ...ragment_flexiblespacewithimagelistview.xml | 69 +++++++ ...ent_flexiblespacewithimagerecyclerview.xml | 56 ++++++ ...gment_flexiblespacewithimagescrollview.xml | 85 ++++++++ samples/res/values/strings.xml | 1 + ...lexibleSpaceWithImageListViewFragment.java | 126 ++++++++++++ ...bleSpaceWithImageRecyclerViewFragment.java | 135 +++++++++++++ ...xibleSpaceWithImageScrollViewFragment.java | 108 +++++++++++ ...paceWithImageWithViewPagerTabActivity.java | 181 ++++++++++++++++++ 10 files changed, 808 insertions(+) create mode 100644 samples/res/layout/activity_flexiblespacewithimagewithviewpagertab.xml create mode 100644 samples/res/layout/fragment_flexiblespacewithimagelistview.xml create mode 100644 samples/res/layout/fragment_flexiblespacewithimagerecyclerview.xml create mode 100644 samples/res/layout/fragment_flexiblespacewithimagescrollview.xml create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java diff --git a/samples/AndroidManifest.xml b/samples/AndroidManifest.xml index 4c1be888..8b7c3978 100644 --- a/samples/AndroidManifest.xml +++ b/samples/AndroidManifest.xml @@ -207,6 +207,16 @@ + + + + + + + + + + + + + + + + diff --git a/samples/res/layout/fragment_flexiblespacewithimagelistview.xml b/samples/res/layout/fragment_flexiblespacewithimagelistview.xml new file mode 100644 index 00000000..bbc14ba9 --- /dev/null +++ b/samples/res/layout/fragment_flexiblespacewithimagelistview.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + diff --git a/samples/res/layout/fragment_flexiblespacewithimagerecyclerview.xml b/samples/res/layout/fragment_flexiblespacewithimagerecyclerview.xml new file mode 100644 index 00000000..20237725 --- /dev/null +++ b/samples/res/layout/fragment_flexiblespacewithimagerecyclerview.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + diff --git a/samples/res/layout/fragment_flexiblespacewithimagescrollview.xml b/samples/res/layout/fragment_flexiblespacewithimagescrollview.xml new file mode 100644 index 00000000..cac91d8d --- /dev/null +++ b/samples/res/layout/fragment_flexiblespacewithimagescrollview.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/res/values/strings.xml b/samples/res/values/strings.xml index ca8adf91..54a7646b 100644 --- a/samples/res/values/strings.xml +++ b/samples/res/values/strings.xml @@ -25,6 +25,7 @@ ScrollView & Action Bar WebView & Action Bar Flexible Space + Flexible Space Flexible Space Flexible Space Another implementation of Fill Gap & ListView diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java new file mode 100644 index 00000000..4814c992 --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java @@ -0,0 +1,126 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.TextView; + +import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.nineoldandroids.view.ViewHelper; + +public class FlexibleSpaceWithImageListViewFragment extends BaseFragment implements ObservableScrollViewCallbacks { + + private static final float MAX_TEXT_SCALE_DELTA = 0.3f; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagelistview, container, false); + + TextView titleView = (TextView) view.findViewById(R.id.title); + titleView.setText(R.string.title_activity_flexiblespacewithimagewithviewpagertab); + + final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll); + // Set padding view for ListView. This is the flexible space. + View paddingView = new View(getActivity()); + int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, + flexibleSpaceImageHeight); + paddingView.setLayoutParams(lp); + + // This is required to disable header's list selector effect + paddingView.setClickable(true); + + listView.addHeaderView(paddingView); + setDummyData(listView); + // TouchInterceptionViewGroup should be a parent view other than ViewPager. + // This is a workaround for the issue #117: + // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 + listView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root)); + + listView.setScrollViewCallbacks(this); + + updateFlexibleSpace(view, 0, false, false); + + return view; + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (getView() == null) { + return; + } + updateFlexibleSpace(getView(), scrollY, firstScroll, dragging); + } + + private void updateFlexibleSpace(View view, int scrollY, boolean firstScroll, boolean dragging) { + int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); + View imageView = view.findViewById(R.id.image); + View overlayView = view.findViewById(R.id.overlay); + ObservableListView scrollView = (ObservableListView) view.findViewById(R.id.scroll); + scrollView.setScrollViewCallbacks(this); + TextView titleView = (TextView) view.findViewById(R.id.title); + View listBackgroundView = view.findViewById(R.id.list_background); + + // Translate overlay and image + float flexibleRange = flexibleSpaceImageHeight - getActionBarSize(); + int minOverlayTransitionY = tabHeight - overlayView.getHeight(); + ViewHelper.setTranslationY(overlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0)); + ViewHelper.setTranslationY(imageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0)); + + // Translate list background + ViewHelper.setTranslationY(listBackgroundView, Math.max(0, -scrollY + flexibleSpaceImageHeight)); + + // Change alpha of overlay + ViewHelper.setAlpha(overlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1)); + + // Scale title text + float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY - tabHeight) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA); + ViewHelper.setPivotX(titleView, 0); + ViewHelper.setPivotY(titleView, 0); + ViewHelper.setScaleX(titleView, scale); + ViewHelper.setScaleY(titleView, scale); + + // Translate title text + int maxTitleTranslationY = flexibleSpaceImageHeight - tabHeight - getActionBarSize(); + int titleTranslationY = maxTitleTranslationY - scrollY; + ViewHelper.setTranslationY(titleView, titleTranslationY); + + // Also pass this event to parent Activity + Activity parentActivity = getActivity(); + if (parentActivity != null && parentActivity instanceof ObservableScrollViewCallbacks) { + ((ObservableScrollViewCallbacks) parentActivity).onScrollChanged(scrollY, firstScroll, dragging); + } + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } +} diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java new file mode 100644 index 00000000..c4fb6872 --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java @@ -0,0 +1,135 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.res.Configuration; +import android.os.Build; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.nineoldandroids.view.ViewHelper; + +public class FlexibleSpaceWithImageRecyclerViewFragment extends BaseFragment implements ObservableScrollViewCallbacks { + + private static final float MAX_TEXT_SCALE_DELTA = 0.3f; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagerecyclerview, container, false); + + TextView titleView = (TextView) view.findViewById(R.id.title); + titleView.setText(R.string.title_activity_flexiblespacewithimagewithviewpagertab); + + final ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll); + recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + recyclerView.setHasFixedSize(false); + final View headerView = LayoutInflater.from(getActivity()).inflate(R.layout.recycler_header, null); + final int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + headerView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, flexibleSpaceImageHeight)); + setDummyDataWithHeader(recyclerView, headerView); + + // TouchInterceptionViewGroup should be a parent view other than ViewPager. + // This is a workaround for the issue #117: + // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 + recyclerView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root)); + + recyclerView.setScrollViewCallbacks(this); + + updateFlexibleSpace(view, 0, false, false); + + return view; + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (getView() == null) { + return; + } + updateFlexibleSpace(getView(), scrollY, firstScroll, dragging); + } + + private void updateFlexibleSpace(View view, int scrollY, boolean firstScroll, boolean dragging) { + int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); + + View imageView = view.findViewById(R.id.image); + View overlayView = view.findViewById(R.id.overlay); + View recyclerViewBackground = view.findViewById(R.id.list_background); + TextView titleView = (TextView) view.findViewById(R.id.title); + + // Translate overlay and image + float flexibleRange = flexibleSpaceImageHeight - getActionBarSize(); + int minOverlayTransitionY = tabHeight - overlayView.getHeight(); + ViewHelper.setTranslationY(overlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0)); + ViewHelper.setTranslationY(imageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0)); + + // Translate list background + ViewHelper.setTranslationY(recyclerViewBackground, Math.max(0, -scrollY + flexibleSpaceImageHeight)); + + // Change alpha of overlay + ViewHelper.setAlpha(overlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1)); + + // Scale title text + float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY - tabHeight) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA); + setPivotXToTitle(view); + ViewHelper.setPivotY(titleView, 0); + ViewHelper.setScaleX(titleView, scale); + ViewHelper.setScaleY(titleView, scale); + + // Translate title text + int maxTitleTranslationY = flexibleSpaceImageHeight - tabHeight - getActionBarSize(); + int titleTranslationY = maxTitleTranslationY - scrollY; + ViewHelper.setTranslationY(titleView, titleTranslationY); + + // Also pass this event to parent Activity + Activity parentActivity = getActivity(); + if (parentActivity != null && parentActivity instanceof ObservableScrollViewCallbacks) { + ((ObservableScrollViewCallbacks) parentActivity).onScrollChanged(scrollY, firstScroll, dragging); + } + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private void setPivotXToTitle(View view) { + final TextView mTitleView = (TextView) view.findViewById(R.id.title); + Configuration config = getResources().getConfiguration(); + if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT + && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + ViewHelper.setPivotX(mTitleView, view.findViewById(android.R.id.content).getWidth()); + } else { + ViewHelper.setPivotX(mTitleView, 0); + } + } +} diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java new file mode 100644 index 00000000..882a87cd --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java @@ -0,0 +1,108 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.nineoldandroids.view.ViewHelper; + +public class FlexibleSpaceWithImageScrollViewFragment extends BaseFragment implements ObservableScrollViewCallbacks { + + private static final float MAX_TEXT_SCALE_DELTA = 0.3f; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagescrollview, container, false); + + TextView titleView = (TextView) view.findViewById(R.id.title); + titleView.setText(R.string.title_activity_flexiblespacewithimagewithviewpagertab); + + final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll); + // TouchInterceptionViewGroup should be a parent view other than ViewPager. + // This is a workaround for the issue #117: + // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 + scrollView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root)); + + scrollView.setScrollViewCallbacks(this); + + updateFlexibleSpace(view, 0, false, false); + + return view; + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (getView() == null) { + return; + } + updateFlexibleSpace(getView(), scrollY, firstScroll, dragging); + } + + private void updateFlexibleSpace(View view, int scrollY, boolean firstScroll, boolean dragging) { + int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); + View imageView = view.findViewById(R.id.image); + View overlayView = view.findViewById(R.id.overlay); + ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll); + scrollView.setScrollViewCallbacks(this); + TextView titleView = (TextView) view.findViewById(R.id.title); + + // Translate overlay and image + float flexibleRange = flexibleSpaceImageHeight - getActionBarSize(); + int minOverlayTransitionY = tabHeight - overlayView.getHeight(); + ViewHelper.setTranslationY(overlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0)); + ViewHelper.setTranslationY(imageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0)); + + // Change alpha of overlay + ViewHelper.setAlpha(overlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1)); + + // Scale title text + float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY - tabHeight) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA); + ViewHelper.setPivotX(titleView, 0); + ViewHelper.setPivotY(titleView, 0); + ViewHelper.setScaleX(titleView, scale); + ViewHelper.setScaleY(titleView, scale); + + // Translate title text + int maxTitleTranslationY = flexibleSpaceImageHeight - tabHeight - getActionBarSize(); + int titleTranslationY = maxTitleTranslationY - scrollY; + ViewHelper.setTranslationY(titleView, titleTranslationY); + + // Also pass this event to parent Activity + Activity parentActivity = getActivity(); + if (parentActivity != null && parentActivity instanceof ObservableScrollViewCallbacks) { + ((ObservableScrollViewCallbacks) parentActivity).onScrollChanged(scrollY, firstScroll, dragging); + } + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } +} diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java new file mode 100644 index 00000000..8c921201 --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java @@ -0,0 +1,181 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.view.ViewPager; +import android.view.View; + +import com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.github.ksoichiro.android.observablescrollview.Scrollable; +import com.google.samples.apps.iosched.ui.widget.SlidingTabLayout; +import com.nineoldandroids.view.ViewHelper; +import com.nineoldandroids.view.ViewPropertyAnimator; + +/** + * SlidingTabLayout and SlidingTabStrip are from google/iosched: + * https://github.com/google/iosched + */ +public class FlexibleSpaceWithImageWithViewPagerTabActivity extends BaseActivity implements ObservableScrollViewCallbacks { + + private ViewPager mPager; + private NavigationAdapter mPagerAdapter; + private SlidingTabLayout mSlidingTabLayout; + private int mFlexibleSpaceHeight; + private int mTabHeight; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_flexiblespacewithimagewithviewpagertab); + + mPagerAdapter = new NavigationAdapter(getSupportFragmentManager()); + mPager = (ViewPager) findViewById(R.id.pager); + mPager.setAdapter(mPagerAdapter); + mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + mTabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); + + mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs); + mSlidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1); + mSlidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent)); + mSlidingTabLayout.setDistributeEvenly(true); + mSlidingTabLayout.setViewPager(mPager); + ScrollUtils.addOnGlobalLayoutListener(mSlidingTabLayout, new Runnable() { + @Override + public void run() { + translateTab(0, false); + } + }); + + // When the page is selected, other fragments' scrollY should be adjusted + // according to the toolbar status(shown/hidden) + mSlidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int i, float v, int i2) { + } + + @Override + public void onPageSelected(int i) { + translateTab(); + } + + @Override + public void onPageScrollStateChanged(int i) { + } + }); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + translateTab(scrollY, false); + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + + private void translateTab() { + Scrollable scrollable = getCurrentScrollable(); + if (scrollable == null) { + return; + } + translateTab(scrollable.getCurrentScrollY(), true); + } + + private void translateTab(int scrollY, boolean animated) { + ViewPropertyAnimator.animate(mSlidingTabLayout).cancel(); + float translationY = ScrollUtils.getFloat(-scrollY + mFlexibleSpaceHeight - mTabHeight, 0, mFlexibleSpaceHeight - mTabHeight); + if (animated) { + ViewPropertyAnimator.animate(mSlidingTabLayout) + .translationY(translationY) + .setDuration(200) + .start(); + } else { + ViewHelper.setTranslationY(mSlidingTabLayout, translationY); + } + } + + private Scrollable getCurrentScrollable() { + Fragment fragment = getCurrentFragment(); + if (fragment == null) { + return null; + } + View view = fragment.getView(); + if (view == null) { + return null; + } + return (Scrollable) view.findViewById(R.id.scroll); + } + + private Fragment getCurrentFragment() { + return mPagerAdapter.getItemAt(mPager.getCurrentItem()); + } + + /** + * This adapter provides three types of fragments as an example. + * {@linkplain #createItem(int)} should be modified if you use this example for your app. + */ + private static class NavigationAdapter extends CacheFragmentStatePagerAdapter { + + private static final String[] TITLES = new String[]{"Applepie", "Butter Cookie", "Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb", "Ice Cream Sandwich", "Jelly Bean", "KitKat", "Lollipop"}; + + public NavigationAdapter(FragmentManager fm) { + super(fm); + } + + @Override + protected Fragment createItem(int position) { + Fragment f; + final int pattern = position % 3; + switch (pattern) { + case 0: { + f = new FlexibleSpaceWithImageScrollViewFragment(); + break; + } + case 1: { + f = new FlexibleSpaceWithImageListViewFragment(); + break; + } + case 2: + default: { + f = new FlexibleSpaceWithImageRecyclerViewFragment(); + break; + } + } + return f; + } + + @Override + public int getCount() { + return TITLES.length; + } + + @Override + public CharSequence getPageTitle(int position) { + return TITLES[position]; + } + } +} From 2a598547dd5770b931da33854f4e9d3c7599f82f Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 23 May 2015 18:30:29 +0900 Subject: [PATCH 127/208] Refactored. --- .../FlexibleSpaceWithImageRecyclerViewActivity.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java index c808d3ba..8744cd96 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java @@ -23,7 +23,6 @@ public class FlexibleSpaceWithImageRecyclerViewActivity extends BaseActivity imp private View mImageView; private View mOverlayView; private View mRecyclerViewBackground; - private ObservableRecyclerView mRecyclerView; private TextView mTitleView; private int mActionBarSize; private int mFlexibleSpaceImageHeight; @@ -36,10 +35,10 @@ protected void onCreate(Bundle savedInstanceState) { mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); mActionBarSize = getActionBarSize(); - mRecyclerView = (ObservableRecyclerView) findViewById(R.id.recycler); - mRecyclerView.setScrollViewCallbacks(this); - mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); - mRecyclerView.setHasFixedSize(false); + ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.recycler); + recyclerView.setScrollViewCallbacks(this); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setHasFixedSize(false); final View headerView = LayoutInflater.from(this).inflate(R.layout.recycler_header, null); headerView.post(new Runnable() { @Override @@ -47,7 +46,7 @@ public void run() { headerView.getLayoutParams().height = mFlexibleSpaceImageHeight; } }); - setDummyDataWithHeader(mRecyclerView, headerView); + setDummyDataWithHeader(recyclerView, headerView); mImageView = findViewById(R.id.image); mOverlayView = findViewById(R.id.overlay); @@ -59,7 +58,7 @@ public void run() { // mRecyclerViewBackground makes RecyclerView's background except header view. mRecyclerViewBackground = findViewById(R.id.list_background); - //since you cannot programatically add a headerview to a recyclerview we added an empty view as the header + //since you cannot programmatically add a header view to a RecyclerView we added an empty view as the header // in the adapter and then are shifting the views OnCreateView to compensate final float scale = 1 + MAX_TEXT_SCALE_DELTA; mRecyclerViewBackground.post(new Runnable() { From 814c079d8c845fcfecca0c70a6ddfe6f577612d4 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 23 May 2015 20:22:20 +0900 Subject: [PATCH 128/208] Fixed FlexibleSpaceWithImageWithViewPagerTab example to translate tabs correctly. --- .../FlexibleSpaceWithImageBaseFragment.java | 58 ++++++++++ ...lexibleSpaceWithImageListViewFragment.java | 34 ++---- ...bleSpaceWithImageRecyclerViewFragment.java | 33 ++---- ...xibleSpaceWithImageScrollViewFragment.java | 33 ++---- ...paceWithImageWithViewPagerTabActivity.java | 105 ++++++++++++------ 5 files changed, 149 insertions(+), 114 deletions(-) create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java new file mode 100644 index 00000000..7e47eb92 --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java @@ -0,0 +1,58 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.view.View; + +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.Scrollable; + +public abstract class FlexibleSpaceWithImageBaseFragment extends BaseFragment + implements ObservableScrollViewCallbacks { + + protected static final float MAX_TEXT_SCALE_DELTA = 0.3f; + + public final void updateFlexibleSpace() { + updateFlexibleSpace(getScrollable().getCurrentScrollY(), getView()); + } + + protected abstract void updateFlexibleSpace(int scrollY, View view); + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (getView() == null) { + return; + } + updateFlexibleSpace(scrollY, getView()); + } + + @Override + public final void onDownMotionEvent() { + // We don't use this callback in this pattern. + } + + @Override + public final void onUpOrCancelMotionEvent(ScrollState scrollState) { + // We don't use this callback in this pattern. + } + + protected S getScrollable() { + View view = getView(); + return view == null ? null : (S) view.findViewById(R.id.scroll); + } +} diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java index 4814c992..ec660f0a 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java @@ -16,7 +16,6 @@ package com.github.ksoichiro.android.observablescrollview.samples; -import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -25,15 +24,10 @@ import android.widget.TextView; import com.github.ksoichiro.android.observablescrollview.ObservableListView; -import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; -import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; -import com.github.ksoichiro.android.observablescrollview.ScrollState; import com.github.ksoichiro.android.observablescrollview.ScrollUtils; import com.nineoldandroids.view.ViewHelper; -public class FlexibleSpaceWithImageListViewFragment extends BaseFragment implements ObservableScrollViewCallbacks { - - private static final float MAX_TEXT_SCALE_DELTA = 0.3f; +public class FlexibleSpaceWithImageListViewFragment extends FlexibleSpaceWithImageBaseFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -62,20 +56,13 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa listView.setScrollViewCallbacks(this); - updateFlexibleSpace(view, 0, false, false); + updateFlexibleSpace(0, view); return view; } @Override - public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { - if (getView() == null) { - return; - } - updateFlexibleSpace(getView(), scrollY, firstScroll, dragging); - } - - private void updateFlexibleSpace(View view, int scrollY, boolean firstScroll, boolean dragging) { + protected void updateFlexibleSpace(int scrollY, View view) { int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); View imageView = view.findViewById(R.id.image); @@ -110,17 +97,10 @@ private void updateFlexibleSpace(View view, int scrollY, boolean firstScroll, bo ViewHelper.setTranslationY(titleView, titleTranslationY); // Also pass this event to parent Activity - Activity parentActivity = getActivity(); - if (parentActivity != null && parentActivity instanceof ObservableScrollViewCallbacks) { - ((ObservableScrollViewCallbacks) parentActivity).onScrollChanged(scrollY, firstScroll, dragging); + FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity = + (FlexibleSpaceWithImageWithViewPagerTabActivity) getActivity(); + if (parentActivity != null) { + parentActivity.onScrollChanged(scrollY, scrollView); } } - - @Override - public void onDownMotionEvent() { - } - - @Override - public void onUpOrCancelMotionEvent(ScrollState scrollState) { - } } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java index c4fb6872..7c4df581 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java @@ -17,7 +17,6 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.annotation.TargetApi; -import android.app.Activity; import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; @@ -29,14 +28,10 @@ import android.widget.TextView; import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; -import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; -import com.github.ksoichiro.android.observablescrollview.ScrollState; import com.github.ksoichiro.android.observablescrollview.ScrollUtils; import com.nineoldandroids.view.ViewHelper; -public class FlexibleSpaceWithImageRecyclerViewFragment extends BaseFragment implements ObservableScrollViewCallbacks { - - private static final float MAX_TEXT_SCALE_DELTA = 0.3f; +public class FlexibleSpaceWithImageRecyclerViewFragment extends FlexibleSpaceWithImageBaseFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -60,20 +55,13 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa recyclerView.setScrollViewCallbacks(this); - updateFlexibleSpace(view, 0, false, false); + updateFlexibleSpace(0, view); return view; } @Override - public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { - if (getView() == null) { - return; - } - updateFlexibleSpace(getView(), scrollY, firstScroll, dragging); - } - - private void updateFlexibleSpace(View view, int scrollY, boolean firstScroll, boolean dragging) { + protected void updateFlexibleSpace(int scrollY, View view) { int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); @@ -107,20 +95,13 @@ private void updateFlexibleSpace(View view, int scrollY, boolean firstScroll, bo ViewHelper.setTranslationY(titleView, titleTranslationY); // Also pass this event to parent Activity - Activity parentActivity = getActivity(); - if (parentActivity != null && parentActivity instanceof ObservableScrollViewCallbacks) { - ((ObservableScrollViewCallbacks) parentActivity).onScrollChanged(scrollY, firstScroll, dragging); + FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity = + (FlexibleSpaceWithImageWithViewPagerTabActivity) getActivity(); + if (parentActivity != null) { + parentActivity.onScrollChanged(scrollY, (ObservableRecyclerView) view.findViewById(R.id.scroll)); } } - @Override - public void onDownMotionEvent() { - } - - @Override - public void onUpOrCancelMotionEvent(ScrollState scrollState) { - } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) private void setPivotXToTitle(View view) { final TextView mTitleView = (TextView) view.findViewById(R.id.title); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java index 882a87cd..b27194ae 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java @@ -16,7 +16,6 @@ package com.github.ksoichiro.android.observablescrollview.samples; -import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -24,14 +23,10 @@ import android.widget.TextView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; -import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; -import com.github.ksoichiro.android.observablescrollview.ScrollState; import com.github.ksoichiro.android.observablescrollview.ScrollUtils; import com.nineoldandroids.view.ViewHelper; -public class FlexibleSpaceWithImageScrollViewFragment extends BaseFragment implements ObservableScrollViewCallbacks { - - private static final float MAX_TEXT_SCALE_DELTA = 0.3f; +public class FlexibleSpaceWithImageScrollViewFragment extends FlexibleSpaceWithImageBaseFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -48,20 +43,13 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa scrollView.setScrollViewCallbacks(this); - updateFlexibleSpace(view, 0, false, false); + updateFlexibleSpace(0, view); return view; } @Override - public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { - if (getView() == null) { - return; - } - updateFlexibleSpace(getView(), scrollY, firstScroll, dragging); - } - - private void updateFlexibleSpace(View view, int scrollY, boolean firstScroll, boolean dragging) { + protected void updateFlexibleSpace(int scrollY, View view) { int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); View imageView = view.findViewById(R.id.image); @@ -92,17 +80,10 @@ private void updateFlexibleSpace(View view, int scrollY, boolean firstScroll, bo ViewHelper.setTranslationY(titleView, titleTranslationY); // Also pass this event to parent Activity - Activity parentActivity = getActivity(); - if (parentActivity != null && parentActivity instanceof ObservableScrollViewCallbacks) { - ((ObservableScrollViewCallbacks) parentActivity).onScrollChanged(scrollY, firstScroll, dragging); + FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity = + (FlexibleSpaceWithImageWithViewPagerTabActivity) getActivity(); + if (parentActivity != null) { + parentActivity.onScrollChanged(scrollY, scrollView); } } - - @Override - public void onDownMotionEvent() { - } - - @Override - public void onUpOrCancelMotionEvent(ScrollState scrollState) { - } } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java index 8c921201..b40c879c 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java @@ -23,8 +23,6 @@ import android.view.View; import com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter; -import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; -import com.github.ksoichiro.android.observablescrollview.ScrollState; import com.github.ksoichiro.android.observablescrollview.ScrollUtils; import com.github.ksoichiro.android.observablescrollview.Scrollable; import com.google.samples.apps.iosched.ui.widget.SlidingTabLayout; @@ -32,10 +30,28 @@ import com.nineoldandroids.view.ViewPropertyAnimator; /** - * SlidingTabLayout and SlidingTabStrip are from google/iosched: - * https://github.com/google/iosched + *

      Another implementation of FlexibleImage pattern + ViewPager.

      + * + *

      This is a completely different approach comparing to FlexibleImageWithViewPager2Activity. + * When the current tab is changed, tabs will be translated in Y-axis + * using scrollY of the new page's Fragment.
      + * You can use this pattern only if you don't mind that the tabs overlap with + * the content of the adjacent pages when swiping pages.

      + * + *

      Descriptions of this pattern:

      + *
        + *
      • The parent Activity and children Fragments strongly depend on each other, + * so if you need to use this pattern, maybe you should extract some interfaces from them.
        + * (This is just an example, so we won't do it here.)
      • + *
      • The parent Activity and children Fragments communicate bidirectionally: + * the parent Activity will update the Fragment's state when the tab is changed, + * and Fragments will tell the parent Activity to update the tab's translationY.
      • + *
      + * + *

      SlidingTabLayout and SlidingTabStrip are from google/iosched:
      + * https://github.com/google/iosched

      */ -public class FlexibleSpaceWithImageWithViewPagerTabActivity extends BaseActivity implements ObservableScrollViewCallbacks { +public class FlexibleSpaceWithImageWithViewPagerTabActivity extends BaseActivity { private ViewPager mPager; private NavigationAdapter mPagerAdapter; @@ -59,6 +75,8 @@ protected void onCreate(Bundle savedInstanceState) { mSlidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent)); mSlidingTabLayout.setDistributeEvenly(true); mSlidingTabLayout.setViewPager(mPager); + + // Initialize the first Fragment's state when layout is completed. ScrollUtils.addOnGlobalLayoutListener(mSlidingTabLayout, new Runnable() { @Override public void run() { @@ -66,8 +84,7 @@ public void run() { } }); - // When the page is selected, other fragments' scrollY should be adjusted - // according to the toolbar status(shown/hidden) + // When the page is selected, translate the tabs using the current Fragment's scrollY. mSlidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int i, float v, int i2) { @@ -84,56 +101,74 @@ public void onPageScrollStateChanged(int i) { }); } - @Override - public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { - translateTab(scrollY, false); - } - - @Override - public void onDownMotionEvent() { - } - - @Override - public void onUpOrCancelMotionEvent(ScrollState scrollState) { + /** + * Called by children Fragments when their scrollY are changed. + * They all call this method even when they are inactive + * but this Activity should listen only the active child, + * so each Fragments will pass themselves for Activity to check if they are active. + * + * @param scrollY scroll position of Scrollable + * @param s caller Scrollable view + */ + public void onScrollChanged(int scrollY, Scrollable s) { + FlexibleSpaceWithImageBaseFragment fragment = + (FlexibleSpaceWithImageBaseFragment) mPagerAdapter.getItemAt(mPager.getCurrentItem()); + if (fragment == null) { + return; + } + View view = fragment.getView(); + if (view == null) { + return; + } + Scrollable scrollable = (Scrollable) view.findViewById(R.id.scroll); + if (scrollable == null) { + return; + } + if (scrollable == s) { + // This method is called by not only the current fragment but also other fragments + // when their scrollY is changed. + // So we need to check the caller(S) is the current fragment. + translateTab(scrollY, false); + } } private void translateTab() { - Scrollable scrollable = getCurrentScrollable(); + FlexibleSpaceWithImageBaseFragment fragment = + (FlexibleSpaceWithImageBaseFragment) mPagerAdapter.getItemAt(mPager.getCurrentItem()); + if (fragment == null) { + return; + } + View view = fragment.getView(); + if (view == null) { + return; + } + Scrollable scrollable = (Scrollable) view.findViewById(R.id.scroll); if (scrollable == null) { return; } + // We should make sure to update the current fragment's state + // because its onScrollChanged is not always called. + fragment.updateFlexibleSpace(); translateTab(scrollable.getCurrentScrollY(), true); } private void translateTab(int scrollY, boolean animated) { + // If tabs are moving, cancel it to start a new animation. ViewPropertyAnimator.animate(mSlidingTabLayout).cancel(); + // Tabs will move between the top of the screen to the bottom of the image. float translationY = ScrollUtils.getFloat(-scrollY + mFlexibleSpaceHeight - mTabHeight, 0, mFlexibleSpaceHeight - mTabHeight); if (animated) { + // Animation will be invoked only when the current tab is changed. ViewPropertyAnimator.animate(mSlidingTabLayout) .translationY(translationY) .setDuration(200) .start(); } else { + // When Fragments' scroll, translate tabs immediately (without animation). ViewHelper.setTranslationY(mSlidingTabLayout, translationY); } } - private Scrollable getCurrentScrollable() { - Fragment fragment = getCurrentFragment(); - if (fragment == null) { - return null; - } - View view = fragment.getView(); - if (view == null) { - return null; - } - return (Scrollable) view.findViewById(R.id.scroll); - } - - private Fragment getCurrentFragment() { - return mPagerAdapter.getItemAt(mPager.getCurrentItem()); - } - /** * This adapter provides three types of fragments as an example. * {@linkplain #createItem(int)} should be modified if you use this example for your app. From 199373fd469cc2fdb91844cb8f55d854ab73f595 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 23 May 2015 20:35:23 +0900 Subject: [PATCH 129/208] Added comments for FlexibleSpaceWithImage + ViewPager patterns. --- ...leSpaceWithImageWithViewPagerTab2Activity.java | 15 +++++++++++---- ...bleSpaceWithImageWithViewPagerTabActivity.java | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java index 85ab8d9e..1483f096 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java @@ -41,10 +41,17 @@ import com.nineoldandroids.view.ViewHelper; /** - * This uses TouchInterceptionFrameLayout to move Fragments. - *

      - * SlidingTabLayout and SlidingTabStrip are from google/iosched: - * https://github.com/google/iosched + *

      This uses TouchInterceptionFrameLayout to move Fragments.

      + * + *

      There is an unsolved problem: it doesn't scroll smoothly + * when the flexible space is changing because TouchInterceptionFrameLayout + * moves without velocity.
      + * If it's a big problem to you, please also check + * FlexibleSpaceWithImageWithViewPagerTabActivity.
      + * There are some differences, but it scrolls smoothly.

      + * + *

      SlidingTabLayout and SlidingTabStrip are from google/iosched:
      + * https://github.com/google/iosched

      */ public class FlexibleSpaceWithImageWithViewPagerTab2Activity extends BaseActivity implements ObservableScrollViewCallbacks { diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java index b40c879c..e3226e2d 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java @@ -33,6 +33,7 @@ *

      Another implementation of FlexibleImage pattern + ViewPager.

      * *

      This is a completely different approach comparing to FlexibleImageWithViewPager2Activity. + * Each Fragments have their own flexible space and image. * When the current tab is changed, tabs will be translated in Y-axis * using scrollY of the new page's Fragment.
      * You can use this pattern only if you don't mind that the tabs overlap with From 8013519189c6034fa91c9d3de0a66687c1d62a67 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 23 May 2015 22:51:33 +0900 Subject: [PATCH 130/208] Fixed FlexibleSpaceWithImageScrollViewFragment does not scroll correctly on Android 4.1. --- .../FlexibleSpaceWithImageBaseFragment.java | 2 +- .../FlexibleSpaceWithImageListViewFragment.java | 1 - .../FlexibleSpaceWithImageScrollViewFragment.java | 15 ++++++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java index 7e47eb92..b47a8146 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java @@ -27,7 +27,7 @@ public abstract class FlexibleSpaceWithImageBaseFragment e protected static final float MAX_TEXT_SCALE_DELTA = 0.3f; - public final void updateFlexibleSpace() { + protected void updateFlexibleSpace() { updateFlexibleSpace(getScrollable().getCurrentScrollY(), getView()); } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java index ec660f0a..3b7514d8 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java @@ -68,7 +68,6 @@ protected void updateFlexibleSpace(int scrollY, View view) { View imageView = view.findViewById(R.id.image); View overlayView = view.findViewById(R.id.overlay); ObservableListView scrollView = (ObservableListView) view.findViewById(R.id.scroll); - scrollView.setScrollViewCallbacks(this); TextView titleView = (TextView) view.findViewById(R.id.title); View listBackgroundView = view.findViewById(R.id.list_background); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java index b27194ae..f33f0b7b 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java @@ -24,6 +24,7 @@ import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.github.ksoichiro.android.observablescrollview.Scrollable; import com.nineoldandroids.view.ViewHelper; public class FlexibleSpaceWithImageScrollViewFragment extends FlexibleSpaceWithImageBaseFragment { @@ -48,6 +49,19 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa return view; } + @Override + protected void updateFlexibleSpace() { + // Sometimes scrollable.getCurrentScrollY() and the real scrollY has different values. + // As a workaround, we should call scrollVerticallyTo() to make sure that they match. + Scrollable s = getScrollable(); + s.scrollVerticallyTo(s.getCurrentScrollY()); + + // If scrollable.getCurrentScrollY() and the real scrollY has the same values, + // calling scrollVerticallyTo() won't invoke scroll (or onScrollChanged()), so we call it here. + // Calling this twice is not a problem as long as updateFlexibleSpace(int, View) has idempotence. + updateFlexibleSpace(s.getCurrentScrollY(), getView()); + } + @Override protected void updateFlexibleSpace(int scrollY, View view) { int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); @@ -55,7 +69,6 @@ protected void updateFlexibleSpace(int scrollY, View view) { View imageView = view.findViewById(R.id.image); View overlayView = view.findViewById(R.id.overlay); ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll); - scrollView.setScrollViewCallbacks(this); TextView titleView = (TextView) view.findViewById(R.id.title); // Translate overlay and image From 75377c5476589405fafde65437070fa7eed0f669 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 24 May 2015 23:00:36 +0900 Subject: [PATCH 131/208] Modified some comments. --- ...ibleSpaceWithImageWithViewPagerTabActivity.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java index e3226e2d..5dc4c7b2 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java @@ -33,14 +33,14 @@ *

      Another implementation of FlexibleImage pattern + ViewPager.

      * *

      This is a completely different approach comparing to FlexibleImageWithViewPager2Activity. - * Each Fragments have their own flexible space and image. - * When the current tab is changed, tabs will be translated in Y-axis - * using scrollY of the new page's Fragment.
      - * You can use this pattern only if you don't mind that the tabs overlap with - * the content of the adjacent pages when swiping pages.

      * *

      Descriptions of this pattern:

      *
        + *
      • Each Fragments have their own flexible space and image.
      • + *
      • When the current tab is changed, tabs will be translated in Y-axis + * using scrollY of the new page's Fragment.
      • + *
      • You can use this pattern only if you don't mind that the tabs overlap with + * the content of the adjacent pages when swiping pages.
      • *
      • The parent Activity and children Fragments strongly depend on each other, * so if you need to use this pattern, maybe you should extract some interfaces from them.
        * (This is just an example, so we won't do it here.)
      • @@ -49,6 +49,10 @@ * and Fragments will tell the parent Activity to update the tab's translationY. *
      * + *

      To share flexible space and image between Fragments, each Scrollable views + * should be able to set scroll position (in pixels) by {@code scrollTo()} or something, + * but currently they don't have such methods.

      + * *

      SlidingTabLayout and SlidingTabStrip are from google/iosched:
      * https://github.com/google/iosched

      */ From fd5f59fae789c876872be0a97ced3bd23d06c286 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 25 May 2015 23:58:07 +0900 Subject: [PATCH 132/208] [WIP] Fix FlexibleSpaceWithImageWithViewPagerTab2 to scroll smoothly. (sometimes not smooth, but better than before) --- ...aceWithImageWithViewPagerTab2Activity.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java index 1483f096..aaac2831 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java @@ -20,15 +20,20 @@ import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; +import android.support.v4.view.VelocityTrackerCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.MotionEvent; +import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.widget.FrameLayout; +import android.widget.OverScroller; import android.widget.TextView; import com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter; @@ -56,6 +61,7 @@ public class FlexibleSpaceWithImageWithViewPagerTab2Activity extends BaseActivity implements ObservableScrollViewCallbacks { private static final float MAX_TEXT_SCALE_DELTA = 0.3f; + private static final int INVALID_POINTER = -1; private View mImageView; private View mOverlayView; @@ -63,6 +69,11 @@ public class FlexibleSpaceWithImageWithViewPagerTab2Activity extends BaseActivit private TouchInterceptionFrameLayout mInterceptionLayout; private ViewPager mPager; private NavigationAdapter mPagerAdapter; + private VelocityTracker mVelocityTracker; + private OverScroller mScroller; + private float mBaseTranslationY; + private int mMaximumVelocity; + private int mActivePointerId = INVALID_POINTER; private int mSlop; private int mFlexibleSpaceHeight; private int mTabHeight; @@ -99,8 +110,10 @@ protected void onCreate(Bundle savedInstanceState) { ViewConfiguration vc = ViewConfiguration.get(this); mSlop = vc.getScaledTouchSlop(); + mMaximumVelocity = vc.getScaledMaximumFlingVelocity(); mInterceptionLayout = (TouchInterceptionFrameLayout) findViewById(R.id.container); mInterceptionLayout.setScrollInterceptionListener(mInterceptionListener); + mScroller = new OverScroller(getApplicationContext()); ScrollUtils.addOnGlobalLayoutListener(mInterceptionLayout, new Runnable() { @Override public void run() { @@ -167,21 +180,81 @@ public boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float d @Override public void onDownMotionEvent(MotionEvent ev) { + mActivePointerId = ev.getPointerId(0); + mScroller.forceFinished(true); + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } else { + mVelocityTracker.clear(); + } + mBaseTranslationY = ViewHelper.getTranslationY(mInterceptionLayout); + mVelocityTracker.addMovement(ev); + Log.e("DEBUG", "onDownMotionEvent: y: " + ev.getY()); } @Override public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) { int flexibleSpace = mFlexibleSpaceHeight - mTabHeight; float translationY = ScrollUtils.getFloat(ViewHelper.getTranslationY(mInterceptionLayout) + diffY, -flexibleSpace, 0); + MotionEvent e = MotionEvent.obtainNoHistory(ev); + e.offsetLocation(0, translationY - mBaseTranslationY); + Log.e("DEBUG", "onMoveMotionEvent: y: " + e.getY()); + mVelocityTracker.addMovement(e); updateFlexibleSpace(translationY); } @Override public void onUpOrCancelMotionEvent(MotionEvent ev) { mScrolled = false; + mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + int velocityY = (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId); + mActivePointerId = INVALID_POINTER; + mScroller.forceFinished(true); + int baseTranslationY = (int) ViewHelper.getTranslationY(mInterceptionLayout); + + int minY = -(mFlexibleSpaceHeight - mTabHeight); + int maxY = 0; + Log.e("DEBUG", "onUpOrCancelMotionEvent: fling: " + velocityY); + mScroller.fling(0, baseTranslationY, 0, velocityY, 0, 0, minY, maxY); + new Handler().post(new Runnable() { + @Override + public void run() { + updateLayout(); + } + }); } }; + private void updateLayout() { + boolean needsUpdate = false; + float translationY = 0; + if (mScroller.computeScrollOffset()) { + translationY = mScroller.getCurrY(); + Log.e("DEBUG", "updateLayout: currY: " + mScroller.getCurrY() + " velocity: " + mScroller.getCurrVelocity()); + int flexibleSpace = mFlexibleSpaceHeight - mTabHeight; + if (-flexibleSpace < translationY && translationY < 0) { + needsUpdate = true; + } else if (translationY < -flexibleSpace) { + translationY = -flexibleSpace; + needsUpdate = true; + } else if (0 < translationY) { + translationY = 0; + needsUpdate = true; + } + } + + if (needsUpdate) { + updateFlexibleSpace(translationY); + + new Handler().post(new Runnable() { + @Override + public void run() { + updateLayout(); + } + }); + } + } + private Scrollable getCurrentScrollable() { Fragment fragment = getCurrentFragment(); if (fragment == null) { From a6c434f8c894f73d809aaa16a2bad5c6654f4ed9 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 26 May 2015 00:10:59 +0900 Subject: [PATCH 133/208] [WIP] Fix wrong conditions in FlexibleSpaceWithImageWithViewPagerTab2. --- .../FlexibleSpaceWithImageWithViewPagerTab2Activity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java index aaac2831..fdce7f45 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java @@ -232,7 +232,7 @@ private void updateLayout() { translationY = mScroller.getCurrY(); Log.e("DEBUG", "updateLayout: currY: " + mScroller.getCurrY() + " velocity: " + mScroller.getCurrVelocity()); int flexibleSpace = mFlexibleSpaceHeight - mTabHeight; - if (-flexibleSpace < translationY && translationY < 0) { + if (-flexibleSpace <= translationY && translationY <= 0) { needsUpdate = true; } else if (translationY < -flexibleSpace) { translationY = -flexibleSpace; From f5f3ff82a8bb56ad1d86ec3ae926ccc00f651fc8 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 26 May 2015 21:47:47 +0900 Subject: [PATCH 134/208] Removed logs and replaced VelocityTrackerCompat to VelocityTracker. --- .../FlexibleSpaceWithImageWithViewPagerTab2Activity.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java index fdce7f45..e490b79d 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java @@ -23,11 +23,9 @@ import android.os.Handler; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v4.view.VelocityTrackerCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.support.v7.widget.Toolbar; -import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; @@ -189,7 +187,6 @@ public void onDownMotionEvent(MotionEvent ev) { } mBaseTranslationY = ViewHelper.getTranslationY(mInterceptionLayout); mVelocityTracker.addMovement(ev); - Log.e("DEBUG", "onDownMotionEvent: y: " + ev.getY()); } @Override @@ -198,7 +195,6 @@ public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) { float translationY = ScrollUtils.getFloat(ViewHelper.getTranslationY(mInterceptionLayout) + diffY, -flexibleSpace, 0); MotionEvent e = MotionEvent.obtainNoHistory(ev); e.offsetLocation(0, translationY - mBaseTranslationY); - Log.e("DEBUG", "onMoveMotionEvent: y: " + e.getY()); mVelocityTracker.addMovement(e); updateFlexibleSpace(translationY); } @@ -207,14 +203,13 @@ public void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY) { public void onUpOrCancelMotionEvent(MotionEvent ev) { mScrolled = false; mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - int velocityY = (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId); + int velocityY = (int) mVelocityTracker.getYVelocity(mActivePointerId); mActivePointerId = INVALID_POINTER; mScroller.forceFinished(true); int baseTranslationY = (int) ViewHelper.getTranslationY(mInterceptionLayout); int minY = -(mFlexibleSpaceHeight - mTabHeight); int maxY = 0; - Log.e("DEBUG", "onUpOrCancelMotionEvent: fling: " + velocityY); mScroller.fling(0, baseTranslationY, 0, velocityY, 0, 0, minY, maxY); new Handler().post(new Runnable() { @Override @@ -230,7 +225,6 @@ private void updateLayout() { float translationY = 0; if (mScroller.computeScrollOffset()) { translationY = mScroller.getCurrY(); - Log.e("DEBUG", "updateLayout: currY: " + mScroller.getCurrY() + " velocity: " + mScroller.getCurrVelocity()); int flexibleSpace = mFlexibleSpaceHeight - mTabHeight; if (-flexibleSpace <= translationY && translationY <= 0) { needsUpdate = true; From 09df6651ff0ece9c9cb05170101a0fbeb1599e8b Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 27 May 2015 23:48:10 +0900 Subject: [PATCH 135/208] Fixed FlexibleSpaceWithImageWithViewPagerTabs pattern to have just one image on Activity. This pattern can only be applied with ObservableScrollView and ObservableRecyclerView. --- ...flexiblespacewithimagewithviewpagertab.xml | 38 ++++ ...ragment_flexiblespacewithimagelistview.xml | 69 ------- ...ent_flexiblespacewithimagerecyclerview.xml | 41 +--- ...gment_flexiblespacewithimagescrollview.xml | 36 ---- .../FlexibleSpaceWithImageBaseFragment.java | 6 +- ...lexibleSpaceWithImageListViewFragment.java | 105 ----------- ...bleSpaceWithImageRecyclerViewFragment.java | 66 ++----- ...xibleSpaceWithImageScrollViewFragment.java | 54 ++---- ...paceWithImageWithViewPagerTabActivity.java | 178 +++++++++++++----- 9 files changed, 208 insertions(+), 385 deletions(-) delete mode 100644 samples/res/layout/fragment_flexiblespacewithimagelistview.xml delete mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java diff --git a/samples/res/layout/activity_flexiblespacewithimagewithviewpagertab.xml b/samples/res/layout/activity_flexiblespacewithimagewithviewpagertab.xml index eaf2fbb3..c9c9af33 100644 --- a/samples/res/layout/activity_flexiblespacewithimagewithviewpagertab.xml +++ b/samples/res/layout/activity_flexiblespacewithimagewithviewpagertab.xml @@ -18,6 +18,13 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + + + + + + - - - - - - - - - - - - - - - - - diff --git a/samples/res/layout/fragment_flexiblespacewithimagerecyclerview.xml b/samples/res/layout/fragment_flexiblespacewithimagerecyclerview.xml index 20237725..17499df7 100644 --- a/samples/res/layout/fragment_flexiblespacewithimagerecyclerview.xml +++ b/samples/res/layout/fragment_flexiblespacewithimagerecyclerview.xml @@ -3,13 +3,6 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - - - - - - - - - + android:scrollbars="none" /> diff --git a/samples/res/layout/fragment_flexiblespacewithimagescrollview.xml b/samples/res/layout/fragment_flexiblespacewithimagescrollview.xml index cac91d8d..07ea3e4d 100644 --- a/samples/res/layout/fragment_flexiblespacewithimagescrollview.xml +++ b/samples/res/layout/fragment_flexiblespacewithimagescrollview.xml @@ -18,13 +18,6 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - - - - - - - - diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java index b47a8146..aa6eb33e 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java @@ -25,10 +25,10 @@ public abstract class FlexibleSpaceWithImageBaseFragment extends BaseFragment implements ObservableScrollViewCallbacks { - protected static final float MAX_TEXT_SCALE_DELTA = 0.3f; + public static final String ARG_SCROLL_Y = "ARG_SCROLL_Y"; - protected void updateFlexibleSpace() { - updateFlexibleSpace(getScrollable().getCurrentScrollY(), getView()); + protected void updateFlexibleSpace(int scrollY) { + updateFlexibleSpace(scrollY, getView()); } protected abstract void updateFlexibleSpace(int scrollY, View view); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java deleted file mode 100644 index 3b7514d8..00000000 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2014 Soichiro Kashima - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.github.ksoichiro.android.observablescrollview.samples; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.TextView; - -import com.github.ksoichiro.android.observablescrollview.ObservableListView; -import com.github.ksoichiro.android.observablescrollview.ScrollUtils; -import com.nineoldandroids.view.ViewHelper; - -public class FlexibleSpaceWithImageListViewFragment extends FlexibleSpaceWithImageBaseFragment { - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagelistview, container, false); - - TextView titleView = (TextView) view.findViewById(R.id.title); - titleView.setText(R.string.title_activity_flexiblespacewithimagewithviewpagertab); - - final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll); - // Set padding view for ListView. This is the flexible space. - View paddingView = new View(getActivity()); - int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); - AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, - flexibleSpaceImageHeight); - paddingView.setLayoutParams(lp); - - // This is required to disable header's list selector effect - paddingView.setClickable(true); - - listView.addHeaderView(paddingView); - setDummyData(listView); - // TouchInterceptionViewGroup should be a parent view other than ViewPager. - // This is a workaround for the issue #117: - // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 - listView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root)); - - listView.setScrollViewCallbacks(this); - - updateFlexibleSpace(0, view); - - return view; - } - - @Override - protected void updateFlexibleSpace(int scrollY, View view) { - int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); - int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); - View imageView = view.findViewById(R.id.image); - View overlayView = view.findViewById(R.id.overlay); - ObservableListView scrollView = (ObservableListView) view.findViewById(R.id.scroll); - TextView titleView = (TextView) view.findViewById(R.id.title); - View listBackgroundView = view.findViewById(R.id.list_background); - - // Translate overlay and image - float flexibleRange = flexibleSpaceImageHeight - getActionBarSize(); - int minOverlayTransitionY = tabHeight - overlayView.getHeight(); - ViewHelper.setTranslationY(overlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0)); - ViewHelper.setTranslationY(imageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0)); - - // Translate list background - ViewHelper.setTranslationY(listBackgroundView, Math.max(0, -scrollY + flexibleSpaceImageHeight)); - - // Change alpha of overlay - ViewHelper.setAlpha(overlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1)); - - // Scale title text - float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY - tabHeight) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA); - ViewHelper.setPivotX(titleView, 0); - ViewHelper.setPivotY(titleView, 0); - ViewHelper.setScaleX(titleView, scale); - ViewHelper.setScaleY(titleView, scale); - - // Translate title text - int maxTitleTranslationY = flexibleSpaceImageHeight - tabHeight - getActionBarSize(); - int titleTranslationY = maxTitleTranslationY - scrollY; - ViewHelper.setTranslationY(titleView, titleTranslationY); - - // Also pass this event to parent Activity - FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity = - (FlexibleSpaceWithImageWithViewPagerTabActivity) getActivity(); - if (parentActivity != null) { - parentActivity.onScrollChanged(scrollY, scrollView); - } - } -} diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java index 7c4df581..2fe198a9 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java @@ -16,16 +16,13 @@ package com.github.ksoichiro.android.observablescrollview.samples; -import android.annotation.TargetApi; -import android.content.res.Configuration; -import android.os.Build; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; -import android.widget.TextView; import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; import com.github.ksoichiro.android.observablescrollview.ScrollUtils; @@ -37,9 +34,6 @@ public class FlexibleSpaceWithImageRecyclerViewFragment extends FlexibleSpaceWit public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagerecyclerview, container, false); - TextView titleView = (TextView) view.findViewById(R.id.title); - titleView.setText(R.string.title_activity_flexiblespacewithimagewithviewpagertab); - final ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll); recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); recyclerView.setHasFixedSize(false); @@ -53,9 +47,26 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 recyclerView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root)); - recyclerView.setScrollViewCallbacks(this); + // Scroll to the specified offset after layout + Bundle args = getArguments(); + if (args != null && args.containsKey(ARG_SCROLL_Y)) { + final int scrollY = args.getInt(ARG_SCROLL_Y, 0); + ScrollUtils.addOnGlobalLayoutListener(recyclerView, new Runnable() { + @Override + public void run() { + int offset = scrollY % flexibleSpaceImageHeight; + RecyclerView.LayoutManager lm = recyclerView.getLayoutManager(); + if (lm != null && lm instanceof LinearLayoutManager) { + ((LinearLayoutManager) lm).scrollToPositionWithOffset(0, -offset); + } + } + }); + updateFlexibleSpace(scrollY, view); + } else { + updateFlexibleSpace(0, view); + } - updateFlexibleSpace(0, view); + recyclerView.setScrollViewCallbacks(this); return view; } @@ -63,37 +74,12 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa @Override protected void updateFlexibleSpace(int scrollY, View view) { int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); - int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); - View imageView = view.findViewById(R.id.image); - View overlayView = view.findViewById(R.id.overlay); View recyclerViewBackground = view.findViewById(R.id.list_background); - TextView titleView = (TextView) view.findViewById(R.id.title); - - // Translate overlay and image - float flexibleRange = flexibleSpaceImageHeight - getActionBarSize(); - int minOverlayTransitionY = tabHeight - overlayView.getHeight(); - ViewHelper.setTranslationY(overlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0)); - ViewHelper.setTranslationY(imageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0)); // Translate list background ViewHelper.setTranslationY(recyclerViewBackground, Math.max(0, -scrollY + flexibleSpaceImageHeight)); - // Change alpha of overlay - ViewHelper.setAlpha(overlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1)); - - // Scale title text - float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY - tabHeight) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA); - setPivotXToTitle(view); - ViewHelper.setPivotY(titleView, 0); - ViewHelper.setScaleX(titleView, scale); - ViewHelper.setScaleY(titleView, scale); - - // Translate title text - int maxTitleTranslationY = flexibleSpaceImageHeight - tabHeight - getActionBarSize(); - int titleTranslationY = maxTitleTranslationY - scrollY; - ViewHelper.setTranslationY(titleView, titleTranslationY); - // Also pass this event to parent Activity FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity = (FlexibleSpaceWithImageWithViewPagerTabActivity) getActivity(); @@ -101,16 +87,4 @@ protected void updateFlexibleSpace(int scrollY, View view) { parentActivity.onScrollChanged(scrollY, (ObservableRecyclerView) view.findViewById(R.id.scroll)); } } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - private void setPivotXToTitle(View view) { - final TextView mTitleView = (TextView) view.findViewById(R.id.title); - Configuration config = getResources().getConfiguration(); - if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT - && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { - ViewHelper.setPivotX(mTitleView, view.findViewById(android.R.id.content).getWidth()); - } else { - ViewHelper.setPivotX(mTitleView, 0); - } - } } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java index f33f0b7b..f8d0ca02 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageScrollViewFragment.java @@ -20,12 +20,10 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; import com.github.ksoichiro.android.observablescrollview.ScrollUtils; import com.github.ksoichiro.android.observablescrollview.Scrollable; -import com.nineoldandroids.view.ViewHelper; public class FlexibleSpaceWithImageScrollViewFragment extends FlexibleSpaceWithImageBaseFragment { @@ -33,64 +31,48 @@ public class FlexibleSpaceWithImageScrollViewFragment extends FlexibleSpaceWithI public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagescrollview, container, false); - TextView titleView = (TextView) view.findViewById(R.id.title); - titleView.setText(R.string.title_activity_flexiblespacewithimagewithviewpagertab); - final ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll); // TouchInterceptionViewGroup should be a parent view other than ViewPager. // This is a workaround for the issue #117: // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 scrollView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root)); - scrollView.setScrollViewCallbacks(this); + // Scroll to the specified offset after layout + Bundle args = getArguments(); + if (args != null && args.containsKey(ARG_SCROLL_Y)) { + final int scrollY = args.getInt(ARG_SCROLL_Y, 0); + ScrollUtils.addOnGlobalLayoutListener(scrollView, new Runnable() { + @Override + public void run() { + scrollView.scrollTo(0, scrollY); + } + }); + updateFlexibleSpace(scrollY, view); + } else { + updateFlexibleSpace(0, view); + } - updateFlexibleSpace(0, view); + scrollView.setScrollViewCallbacks(this); return view; } @Override - protected void updateFlexibleSpace() { + protected void updateFlexibleSpace(int scrollY) { // Sometimes scrollable.getCurrentScrollY() and the real scrollY has different values. // As a workaround, we should call scrollVerticallyTo() to make sure that they match. Scrollable s = getScrollable(); - s.scrollVerticallyTo(s.getCurrentScrollY()); + s.scrollVerticallyTo(scrollY); // If scrollable.getCurrentScrollY() and the real scrollY has the same values, // calling scrollVerticallyTo() won't invoke scroll (or onScrollChanged()), so we call it here. // Calling this twice is not a problem as long as updateFlexibleSpace(int, View) has idempotence. - updateFlexibleSpace(s.getCurrentScrollY(), getView()); + updateFlexibleSpace(scrollY, getView()); } @Override protected void updateFlexibleSpace(int scrollY, View view) { - int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); - int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); - View imageView = view.findViewById(R.id.image); - View overlayView = view.findViewById(R.id.overlay); ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll); - TextView titleView = (TextView) view.findViewById(R.id.title); - - // Translate overlay and image - float flexibleRange = flexibleSpaceImageHeight - getActionBarSize(); - int minOverlayTransitionY = tabHeight - overlayView.getHeight(); - ViewHelper.setTranslationY(overlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0)); - ViewHelper.setTranslationY(imageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0)); - - // Change alpha of overlay - ViewHelper.setAlpha(overlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1)); - - // Scale title text - float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY - tabHeight) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA); - ViewHelper.setPivotX(titleView, 0); - ViewHelper.setPivotY(titleView, 0); - ViewHelper.setScaleX(titleView, scale); - ViewHelper.setScaleY(titleView, scale); - - // Translate title text - int maxTitleTranslationY = flexibleSpaceImageHeight - tabHeight - getActionBarSize(); - int titleTranslationY = maxTitleTranslationY - scrollY; - ViewHelper.setTranslationY(titleView, titleTranslationY); // Also pass this event to parent Activity FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity = diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java index 5dc4c7b2..8d1498f4 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java @@ -16,13 +16,20 @@ package com.github.ksoichiro.android.observablescrollview.samples; +import android.annotation.TargetApi; +import android.content.res.Configuration; +import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.view.ViewPager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.view.View; +import android.widget.TextView; import com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter; +import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; import com.github.ksoichiro.android.observablescrollview.ScrollUtils; import com.github.ksoichiro.android.observablescrollview.Scrollable; import com.google.samples.apps.iosched.ui.widget.SlidingTabLayout; @@ -36,28 +43,24 @@ * *

      Descriptions of this pattern:

      *
        - *
      • Each Fragments have their own flexible space and image.
      • *
      • When the current tab is changed, tabs will be translated in Y-axis * using scrollY of the new page's Fragment.
      • - *
      • You can use this pattern only if you don't mind that the tabs overlap with - * the content of the adjacent pages when swiping pages.
      • *
      • The parent Activity and children Fragments strongly depend on each other, * so if you need to use this pattern, maybe you should extract some interfaces from them.
        * (This is just an example, so we won't do it here.)
      • *
      • The parent Activity and children Fragments communicate bidirectionally: * the parent Activity will update the Fragment's state when the tab is changed, * and Fragments will tell the parent Activity to update the tab's translationY.
      • + *
      • This pattern can be used only with ObservableScrollView and ObservableRecyclerView currently.
      • *
      * - *

      To share flexible space and image between Fragments, each Scrollable views - * should be able to set scroll position (in pixels) by {@code scrollTo()} or something, - * but currently they don't have such methods.

      - * *

      SlidingTabLayout and SlidingTabStrip are from google/iosched:
      * https://github.com/google/iosched

      */ public class FlexibleSpaceWithImageWithViewPagerTabActivity extends BaseActivity { + protected static final float MAX_TEXT_SCALE_DELTA = 0.3f; + private ViewPager mPager; private NavigationAdapter mPagerAdapter; private SlidingTabLayout mSlidingTabLayout; @@ -75,6 +78,9 @@ protected void onCreate(Bundle savedInstanceState) { mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); mTabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); + TextView titleView = (TextView) findViewById(R.id.title); + titleView.setText(R.string.title_activity_flexiblespacewithimagewithviewpagertab); + mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs); mSlidingTabLayout.setCustomTabView(R.layout.tab_indicator, android.R.id.text1); mSlidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.accent)); @@ -88,22 +94,6 @@ public void run() { translateTab(0, false); } }); - - // When the page is selected, translate the tabs using the current Fragment's scrollY. - mSlidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { - @Override - public void onPageScrolled(int i, float v, int i2) { - } - - @Override - public void onPageSelected(int i) { - translateTab(); - } - - @Override - public void onPageScrollStateChanged(int i) { - } - }); } /** @@ -133,31 +123,40 @@ public void onScrollChanged(int scrollY, Scrollable s) { // This method is called by not only the current fragment but also other fragments // when their scrollY is changed. // So we need to check the caller(S) is the current fragment. - translateTab(scrollY, false); + int adjustedScrollY = Math.min(scrollY, mFlexibleSpaceHeight - mTabHeight); + translateTab(adjustedScrollY, false); + propagateScroll(adjustedScrollY); } } - private void translateTab() { - FlexibleSpaceWithImageBaseFragment fragment = - (FlexibleSpaceWithImageBaseFragment) mPagerAdapter.getItemAt(mPager.getCurrentItem()); - if (fragment == null) { - return; - } - View view = fragment.getView(); - if (view == null) { - return; - } - Scrollable scrollable = (Scrollable) view.findViewById(R.id.scroll); - if (scrollable == null) { - return; - } - // We should make sure to update the current fragment's state - // because its onScrollChanged is not always called. - fragment.updateFlexibleSpace(); - translateTab(scrollable.getCurrentScrollY(), true); - } - private void translateTab(int scrollY, boolean animated) { + int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + int tabHeight = getResources().getDimensionPixelSize(R.dimen.tab_height); + View imageView = findViewById(R.id.image); + View overlayView = findViewById(R.id.overlay); + TextView titleView = (TextView) findViewById(R.id.title); + + // Translate overlay and image + float flexibleRange = flexibleSpaceImageHeight - getActionBarSize(); + int minOverlayTransitionY = tabHeight - overlayView.getHeight(); + ViewHelper.setTranslationY(overlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0)); + ViewHelper.setTranslationY(imageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0)); + + // Change alpha of overlay + ViewHelper.setAlpha(overlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1)); + + // Scale title text + float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY - tabHeight) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA); + setPivotXToTitle(titleView); + ViewHelper.setPivotY(titleView, 0); + ViewHelper.setScaleX(titleView, scale); + ViewHelper.setScaleY(titleView, scale); + + // Translate title text + int maxTitleTranslationY = flexibleSpaceImageHeight - tabHeight - getActionBarSize(); + int titleTranslationY = maxTitleTranslationY - scrollY; + ViewHelper.setTranslationY(titleView, titleTranslationY); + // If tabs are moving, cancel it to start a new animation. ViewPropertyAnimator.animate(mSlidingTabLayout).cancel(); // Tabs will move between the top of the screen to the bottom of the image. @@ -174,6 +173,71 @@ private void translateTab(int scrollY, boolean animated) { } } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private void setPivotXToTitle(View view) { + final TextView mTitleView = (TextView) view.findViewById(R.id.title); + Configuration config = getResources().getConfiguration(); + if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT + && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + ViewHelper.setPivotX(mTitleView, view.findViewById(android.R.id.content).getWidth()); + } else { + ViewHelper.setPivotX(mTitleView, 0); + } + } + + private void propagateScroll(int scrollY) { + // Set scrollY for the fragments that are not created yet + mPagerAdapter.setScrollY(scrollY); + + // Set scrollY for the active fragments + for (int i = 0; i < mPagerAdapter.getCount(); i++) { + // Skip current item + if (i == mPager.getCurrentItem()) { + continue; + } + + // Skip destroyed or not created item + FlexibleSpaceWithImageBaseFragment f = + (FlexibleSpaceWithImageBaseFragment) mPagerAdapter.getItemAt(i); + if (f == null) { + continue; + } + + View view = f.getView(); + if (view == null) { + continue; + } + propagateScroll(view, scrollY); + f.updateFlexibleSpace(scrollY); + } + } + + private void propagateScroll(View view, int scrollY) { + Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll); + if (scrollView == null) { + return; + } + if (scrollView instanceof ObservableRecyclerView) { + final ObservableRecyclerView recyclerView = (ObservableRecyclerView) scrollView; + View firstVisibleChild = recyclerView.getChildAt(0); + if (firstVisibleChild != null) { + int offset = scrollY; + int position = 0; + if (mFlexibleSpaceHeight < scrollY) { + int baseHeight = firstVisibleChild.getHeight(); + position = scrollY / baseHeight; + offset = scrollY % baseHeight; + } + RecyclerView.LayoutManager lm = recyclerView.getLayoutManager(); + if (lm != null && lm instanceof LinearLayoutManager) { + ((LinearLayoutManager) lm).scrollToPositionWithOffset(position, -offset); + } + } + } else { + scrollView.scrollVerticallyTo(scrollY); + } + } + /** * This adapter provides three types of fragments as an example. * {@linkplain #createItem(int)} should be modified if you use this example for your app. @@ -182,26 +246,40 @@ private static class NavigationAdapter extends CacheFragmentStatePagerAdapter { private static final String[] TITLES = new String[]{"Applepie", "Butter Cookie", "Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb", "Ice Cream Sandwich", "Jelly Bean", "KitKat", "Lollipop"}; + private int mScrollY; + public NavigationAdapter(FragmentManager fm) { super(fm); } + public void setScrollY(int scrollY) { + mScrollY = scrollY; + } + @Override protected Fragment createItem(int position) { Fragment f; - final int pattern = position % 3; + final int pattern = position % 4; switch (pattern) { - case 0: { - f = new FlexibleSpaceWithImageScrollViewFragment(); - break; - } + case 0: case 1: { - f = new FlexibleSpaceWithImageListViewFragment(); + f = new FlexibleSpaceWithImageScrollViewFragment(); + if (0 <= mScrollY) { + Bundle args = new Bundle(); + args.putInt(FlexibleSpaceWithImageScrollViewFragment.ARG_SCROLL_Y, mScrollY); + f.setArguments(args); + } break; } case 2: + case 3: default: { f = new FlexibleSpaceWithImageRecyclerViewFragment(); + if (0 <= mScrollY) { + Bundle args = new Bundle(); + args.putInt(FlexibleSpaceWithImageRecyclerViewFragment.ARG_SCROLL_Y, mScrollY); + f.setArguments(args); + } break; } } From c04308a7d7f0b7a9f4995a081ca340dee3846cc2 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Thu, 28 May 2015 00:15:11 +0900 Subject: [PATCH 136/208] Added ListView to FlexibleSpaceWithImageWithViewPagerTab pattern. --- ...ragment_flexiblespacewithimagelistview.xml | 32 +++++++ ...lexibleSpaceWithImageListViewFragment.java | 94 +++++++++++++++++++ ...paceWithImageWithViewPagerTabActivity.java | 32 ++++++- 3 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 samples/res/layout/fragment_flexiblespacewithimagelistview.xml create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java diff --git a/samples/res/layout/fragment_flexiblespacewithimagelistview.xml b/samples/res/layout/fragment_flexiblespacewithimagelistview.xml new file mode 100644 index 00000000..52d222c4 --- /dev/null +++ b/samples/res/layout/fragment_flexiblespacewithimagelistview.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java new file mode 100644 index 00000000..b5c57ef6 --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java @@ -0,0 +1,94 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; + +import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.nineoldandroids.view.ViewHelper; + +public class FlexibleSpaceWithImageListViewFragment extends FlexibleSpaceWithImageBaseFragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagelistview, container, false); + + final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll); + // Set padding view for ListView. This is the flexible space. + View paddingView = new View(getActivity()); + final int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, + flexibleSpaceImageHeight); + paddingView.setLayoutParams(lp); + + // This is required to disable header's list selector effect + paddingView.setClickable(true); + + listView.addHeaderView(paddingView); + setDummyData(listView); + // TouchInterceptionViewGroup should be a parent view other than ViewPager. + // This is a workaround for the issue #117: + // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 + listView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root)); + + // Scroll to the specified offset after layout + Bundle args = getArguments(); + if (args != null && args.containsKey(ARG_SCROLL_Y)) { + final int scrollY = args.getInt(ARG_SCROLL_Y, 0); + ScrollUtils.addOnGlobalLayoutListener(listView, new Runnable() { + @SuppressLint("NewApi") + @Override + public void run() { + int offset = scrollY % flexibleSpaceImageHeight; + listView.setSelectionFromTop(0, -offset); + } + }); + updateFlexibleSpace(scrollY, view); + } else { + updateFlexibleSpace(0, view); + } + + listView.setScrollViewCallbacks(this); + + updateFlexibleSpace(0, view); + + return view; + } + + @Override + protected void updateFlexibleSpace(int scrollY, View view) { + int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + + View listBackgroundView = view.findViewById(R.id.list_background); + + // Translate list background + ViewHelper.setTranslationY(listBackgroundView, Math.max(0, -scrollY + flexibleSpaceImageHeight)); + + // Also pass this event to parent Activity + FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity = + (FlexibleSpaceWithImageWithViewPagerTabActivity) getActivity(); + if (parentActivity != null) { + parentActivity.onScrollChanged(scrollY, (ObservableListView) view.findViewById(R.id.scroll)); + } + } +} diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java index 8d1498f4..ac2ea93a 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java @@ -16,6 +16,7 @@ package com.github.ksoichiro.android.observablescrollview.samples; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.res.Configuration; import android.os.Build; @@ -29,6 +30,7 @@ import android.widget.TextView; import com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter; +import com.github.ksoichiro.android.observablescrollview.ObservableListView; import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; import com.github.ksoichiro.android.observablescrollview.ScrollUtils; import com.github.ksoichiro.android.observablescrollview.Scrollable; @@ -51,7 +53,6 @@ *
    1. The parent Activity and children Fragments communicate bidirectionally: * the parent Activity will update the Fragment's state when the tab is changed, * and Fragments will tell the parent Activity to update the tab's translationY.
    2. - *
    3. This pattern can be used only with ObservableScrollView and ObservableRecyclerView currently.
    4. * * *

      SlidingTabLayout and SlidingTabStrip are from google/iosched:
      @@ -212,6 +213,7 @@ private void propagateScroll(int scrollY) { } } + @SuppressLint("NewApi") private void propagateScroll(View view, int scrollY) { Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll); if (scrollView == null) { @@ -233,6 +235,19 @@ private void propagateScroll(View view, int scrollY) { ((LinearLayoutManager) lm).scrollToPositionWithOffset(position, -offset); } } + } else if (scrollView instanceof ObservableListView) { + ObservableListView listView = (ObservableListView) scrollView; + View firstVisibleChild = listView.getChildAt(0); + if (firstVisibleChild != null) { + int offset = scrollY; + int position = 0; + if (mFlexibleSpaceHeight < scrollY) { + int baseHeight = firstVisibleChild.getHeight(); + position = scrollY / baseHeight; + offset = scrollY % baseHeight; + } + listView.setSelectionFromTop(position, -offset); + } } else { scrollView.scrollVerticallyTo(scrollY); } @@ -259,10 +274,9 @@ public void setScrollY(int scrollY) { @Override protected Fragment createItem(int position) { Fragment f; - final int pattern = position % 4; + final int pattern = position % 3; switch (pattern) { - case 0: - case 1: { + case 0: { f = new FlexibleSpaceWithImageScrollViewFragment(); if (0 <= mScrollY) { Bundle args = new Bundle(); @@ -271,8 +285,16 @@ protected Fragment createItem(int position) { } break; } + case 1: { + f = new FlexibleSpaceWithImageListViewFragment(); + if (0 <= mScrollY) { + Bundle args = new Bundle(); + args.putInt(FlexibleSpaceWithImageListViewFragment.ARG_SCROLL_Y, mScrollY); + f.setArguments(args); + } + break; + } case 2: - case 3: default: { f = new FlexibleSpaceWithImageRecyclerViewFragment(); if (0 <= mScrollY) { From a1e4058dfeee193311166616be43c38c1af48302 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 29 May 2015 23:42:06 +0900 Subject: [PATCH 137/208] Fixed warning. --- .../samples/ViewPagerTabListViewFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java index 1731823f..c709cdd1 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewFragment.java @@ -36,7 +36,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa Activity parentActivity = getActivity(); final ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll); - setDummyDataWithHeader(listView, inflater.inflate(R.layout.padding, null)); + setDummyDataWithHeader(listView, inflater.inflate(R.layout.padding, listView, false)); if (parentActivity instanceof ObservableScrollViewCallbacks) { // Scroll to the specified position after layout From 39facdea0710c505969445f611adae4f5c5d2c45 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 30 May 2015 00:01:51 +0900 Subject: [PATCH 138/208] Fixed warning for NPE. --- .../samples/ActionBarControlGridViewActivity.java | 3 +++ .../samples/ActionBarControlListViewActivity.java | 3 +++ .../samples/ActionBarControlRecyclerViewActivity.java | 3 +++ .../samples/ActionBarControlScrollViewActivity.java | 3 +++ .../samples/ActionBarControlWebViewActivity.java | 3 +++ .../FlexibleSpaceToolbarScrollViewActivity.java | 6 +++++- .../samples/FlexibleSpaceToolbarWebViewActivity.java | 6 +++++- .../FragmentActionBarControlListViewFragment.java | 3 +++ .../samples/FragmentTransitionSecondFragment.java | 11 +++++++++-- .../samples/SlidingUpBaseActivity.java | 10 +++++++--- .../samples/ViewPagerTabListViewActivity.java | 6 +++++- 11 files changed, 49 insertions(+), 8 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlGridViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlGridViewActivity.java index a8b9dd52..70de6577 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlGridViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlGridViewActivity.java @@ -46,6 +46,9 @@ public void onDownMotionEvent() { @Override public void onUpOrCancelMotionEvent(ScrollState scrollState) { ActionBar ab = getSupportActionBar(); + if (ab == null) { + return; + } if (scrollState == ScrollState.UP) { if (ab.isShowing()) { ab.hide(); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlListViewActivity.java index 7d73f997..f798d7d1 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlListViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlListViewActivity.java @@ -63,6 +63,9 @@ public void onDownMotionEvent() { @Override public void onUpOrCancelMotionEvent(ScrollState scrollState) { ActionBar ab = getSupportActionBar(); + if (ab == null) { + return; + } if (scrollState == ScrollState.UP) { if (ab.isShowing()) { ab.hide(); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlRecyclerViewActivity.java index 5deef2eb..d5127ce4 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlRecyclerViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlRecyclerViewActivity.java @@ -49,6 +49,9 @@ public void onDownMotionEvent() { @Override public void onUpOrCancelMotionEvent(ScrollState scrollState) { ActionBar ab = getSupportActionBar(); + if (ab == null) { + return; + } if (scrollState == ScrollState.UP) { if (ab.isShowing()) { ab.hide(); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlScrollViewActivity.java index c7012e22..f7a78b1e 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlScrollViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlScrollViewActivity.java @@ -45,6 +45,9 @@ public void onDownMotionEvent() { @Override public void onUpOrCancelMotionEvent(ScrollState scrollState) { ActionBar ab = getSupportActionBar(); + if (ab == null) { + return; + } if (scrollState == ScrollState.UP) { if (ab.isShowing()) { ab.hide(); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlWebViewActivity.java index c27d91db..e193b3a2 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlWebViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ActionBarControlWebViewActivity.java @@ -46,6 +46,9 @@ public void onDownMotionEvent() { @Override public void onUpOrCancelMotionEvent(ScrollState scrollState) { ActionBar ab = getSupportActionBar(); + if (ab == null) { + return; + } if (scrollState == ScrollState.UP) { if (ab.isShowing()) { ab.hide(); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java index 04678edc..529b14ea 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarScrollViewActivity.java @@ -17,6 +17,7 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.os.Bundle; +import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.view.View; import android.widget.TextView; @@ -40,7 +41,10 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_flexiblespacetoolbarscrollview); setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); + ActionBar ab = getSupportActionBar(); + if (ab != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } mFlexibleSpaceView = findViewById(R.id.flexible_space); mTitleView = (TextView) findViewById(R.id.title); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java index a2a2f976..0ebde34a 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java @@ -17,6 +17,7 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.os.Bundle; +import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.view.View; import android.webkit.WebView; @@ -45,7 +46,10 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_flexiblespacetoolbarwebview); setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); + ActionBar ab = getSupportActionBar(); + if (ab != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } mFlexibleSpaceView = findViewById(R.id.flexible_space); mTitleView = (TextView) findViewById(R.id.title); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java index 2f9fae3b..5a175764 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentActionBarControlListViewFragment.java @@ -60,6 +60,9 @@ public void onUpOrCancelMotionEvent(ScrollState scrollState) { return; } ActionBar ab = activity.getSupportActionBar(); + if (ab == null) { + return; + } if (scrollState == ScrollState.UP) { if (ab.isShowing()) { ab.hide(); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java index 6f5e7789..7a896bb7 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FragmentTransitionSecondFragment.java @@ -17,6 +17,7 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.os.Bundle; +import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; @@ -44,14 +45,20 @@ public void onDestroyView() { private void showToolbar() { AppCompatActivity activity = (AppCompatActivity) getActivity(); if (activity != null) { - activity.getSupportActionBar().show(); + ActionBar ab = activity.getSupportActionBar(); + if (ab != null) { + ab.show(); + } } } private void hideToolbar() { AppCompatActivity activity = (AppCompatActivity) getActivity(); if (activity != null) { - activity.getSupportActionBar().hide(); + ActionBar ab = activity.getSupportActionBar(); + if (ab != null) { + ab.hide(); + } } } } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java index 06d897ac..db36a16c 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpBaseActivity.java @@ -19,6 +19,7 @@ import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; +import android.support.v7.app.ActionBar; import android.support.v7.widget.Toolbar; import android.view.MotionEvent; import android.view.View; @@ -96,9 +97,12 @@ protected void onCreate(Bundle savedInstanceState) { setSupportActionBar(mToolbar); ViewHelper.setScaleY(mToolbar, 0); - getSupportActionBar().setHomeButtonEnabled(true); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setTitle(""); + ActionBar ab = getSupportActionBar(); + if (ab != null) { + ab.setHomeButtonEnabled(true); + ab.setDisplayHomeAsUpEnabled(true); + ab.setTitle(""); + } mToolbarColor = getResources().getColor(R.color.primary); mToolbar.setBackgroundColor(Color.TRANSPARENT); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewActivity.java index 0eaca69c..cdaed4b3 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabListViewActivity.java @@ -169,7 +169,11 @@ private void propagateToolbarState(boolean isShown) { continue; } - ObservableListView listView = (ObservableListView) f.getView().findViewById(R.id.scroll); + View view = f.getView(); + if (view == null) { + continue; + } + ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll); if (isShown) { // Scroll up if (0 < listView.getCurrentScrollY()) { From d7fe58f77ddec8dea21eb79bf09978cb14730280 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 30 May 2015 00:03:10 +0900 Subject: [PATCH 139/208] Removed unused imports. --- .../samples/SlidingUpListViewActivity.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpListViewActivity.java index f355bcca..3b2790fa 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpListViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/SlidingUpListViewActivity.java @@ -18,15 +18,11 @@ import android.view.View; import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.Toast; import com.github.ksoichiro.android.observablescrollview.ObservableListView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; -import java.util.ArrayList; -import java.util.List; - public class SlidingUpListViewActivity extends SlidingUpBaseActivity implements ObservableScrollViewCallbacks { @Override From 14aa8b087de176c9a3b061f6bbb5ebeae4d7f44f Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 30 May 2015 00:11:50 +0900 Subject: [PATCH 140/208] Fixed some warnings. --- .../android/observablescrollview/samples/BaseActivity.java | 6 +++--- .../android/observablescrollview/samples/BaseFragment.java | 4 ++-- .../samples/FlexibleSpaceWithImageListViewActivity.java | 1 - .../samples/FlexibleSpaceWithImageRecyclerViewActivity.java | 1 - .../android/observablescrollview/samples/MainActivity.java | 6 +++--- .../samples/ToolbarControlRecyclerViewActivity.java | 5 ----- .../samples/ViewPagerTabScrollViewActivity.java | 6 +++++- 7 files changed, 13 insertions(+), 16 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java index 2e776dd2..d94cb195 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseActivity.java @@ -51,7 +51,7 @@ public static ArrayList getDummyData() { } public static ArrayList getDummyData(int num) { - ArrayList items = new ArrayList(); + ArrayList items = new ArrayList<>(); for (int i = 1; i <= num; i++) { items.add("Item " + i); } @@ -67,7 +67,7 @@ protected void setDummyDataFew(ListView listView) { } protected void setDummyData(ListView listView, int num) { - listView.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, getDummyData(num))); + listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, getDummyData(num))); } protected void setDummyDataWithHeader(ListView listView, int headerHeight) { @@ -89,7 +89,7 @@ protected void setDummyDataWithHeader(ListView listView, View headerView, int nu } protected void setDummyData(GridView gridView) { - gridView.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, getDummyData())); + gridView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, getDummyData())); } protected void setDummyData(RecyclerView recyclerView) { diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java index 0722d4ef..bafafa35 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java @@ -56,7 +56,7 @@ protected int getScreenHeight() { } protected void setDummyData(ListView listView) { - listView.setAdapter(new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, getDummyData())); + listView.setAdapter(new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, getDummyData())); } protected void setDummyDataWithHeader(ListView listView, View headerView) { @@ -65,7 +65,7 @@ protected void setDummyDataWithHeader(ListView listView, View headerView) { } protected void setDummyData(GridView gridView) { - gridView.setAdapter(new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, getDummyData())); + gridView.setAdapter(new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, getDummyData())); } protected void setDummyData(RecyclerView recyclerView) { diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java index de9be5a9..ed9d07b9 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewActivity.java @@ -20,7 +20,6 @@ import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; -import android.support.v7.widget.Toolbar; import android.view.View; import android.widget.AbsListView; import android.widget.FrameLayout; diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java index 8744cd96..f04a43d7 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java @@ -5,7 +5,6 @@ import android.os.Build; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.Toolbar; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java index 84408d61..db950ab3 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/MainActivity.java @@ -58,7 +58,7 @@ public int compare(Map lhs, Map rhs) { private ListView listView; // Quickly navigate through the examples. - static enum Filter { + enum Filter { All, GridView, RecyclerView, @@ -124,7 +124,7 @@ public boolean onOptionsItemSelected(final MenuItem menu) { } private List> getData() { - List> data = new ArrayList>(); + List> data = new ArrayList<>(); Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.setPackage(getApplicationContext().getPackageName()); @@ -179,7 +179,7 @@ protected Intent activityIntent(String pkg, String componentName) { protected void addItem(List> data, String className, String description, Intent intent) { - Map temp = new HashMap(); + Map temp = new HashMap<>(); temp.put(TAG_CLASS_NAME, className); temp.put(TAG_DESCRIPTION, description); temp.put(TAG_INTENT, intent); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlRecyclerViewActivity.java index 3fbfc854..02d83ec9 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlRecyclerViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ToolbarControlRecyclerViewActivity.java @@ -17,16 +17,11 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.support.v7.widget.LinearLayoutManager; -import android.util.Log; -import android.widget.AbsListView; -import com.github.ksoichiro.android.observablescrollview.ObservableListView; import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; public class ToolbarControlRecyclerViewActivity extends ToolbarControlBaseActivity { - private static final String TAG = ToolbarControlRecyclerViewActivity.class.getSimpleName(); - @Override protected int getLayoutResId() { return R.layout.activity_toolbarcontrolrecyclerview; diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java index c8769436..fb296c9d 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabScrollViewActivity.java @@ -169,7 +169,11 @@ private void propagateToolbarState(boolean isShown) { continue; } - ObservableScrollView scrollView = (ObservableScrollView) f.getView().findViewById(R.id.scroll); + View view = f.getView(); + if (view == null) { + continue; + } + ObservableScrollView scrollView = (ObservableScrollView) view.findViewById(R.id.scroll); if (isShown) { // Scroll up if (0 < scrollView.getCurrentScrollY()) { From cd38e25e772f201ee70e6b9906333de478e43ead Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 30 May 2015 00:18:46 +0900 Subject: [PATCH 141/208] Removed redundant modifiers. --- .../observablescrollview/ObservableScrollViewCallbacks.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java index 4f7e9c62..b390c4cb 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java @@ -30,17 +30,17 @@ public interface ObservableScrollViewCallbacks { * @param firstScroll true when this is called for the first time in the consecutive motion events * @param dragging true when the view is dragged and false when the view is scrolled in the inertia */ - public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging); + void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging); /** * Called when the down motion event occurred. */ - public void onDownMotionEvent(); + void onDownMotionEvent(); /** * Called when the dragging ended or canceled. * * @param scrollState state to indicate the scroll direction */ - public void onUpOrCancelMotionEvent(ScrollState scrollState); + void onUpOrCancelMotionEvent(ScrollState scrollState); } From 834263dab3b4b50854a04d51de988e090e9d5718 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 31 May 2015 22:15:50 +0900 Subject: [PATCH 142/208] Refactored. --- .../FlexibleSpaceWithImageBaseFragment.java | 9 +++++++++ ...SpaceWithImageWithViewPagerTabActivity.java | 18 ++---------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java index aa6eb33e..0c0e07cc 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java @@ -16,6 +16,7 @@ package com.github.ksoichiro.android.observablescrollview.samples; +import android.os.Bundle; import android.view.View; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; @@ -27,6 +28,14 @@ public abstract class FlexibleSpaceWithImageBaseFragment e public static final String ARG_SCROLL_Y = "ARG_SCROLL_Y"; + public void setArguments(int scrollY) { + if (0 <= scrollY) { + Bundle args = new Bundle(); + args.putInt(ARG_SCROLL_Y, scrollY); + setArguments(args); + } + } + protected void updateFlexibleSpace(int scrollY) { updateFlexibleSpace(scrollY, getView()); } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java index ac2ea93a..b54e6eb1 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java @@ -273,38 +273,24 @@ public void setScrollY(int scrollY) { @Override protected Fragment createItem(int position) { - Fragment f; + FlexibleSpaceWithImageBaseFragment f; final int pattern = position % 3; switch (pattern) { case 0: { f = new FlexibleSpaceWithImageScrollViewFragment(); - if (0 <= mScrollY) { - Bundle args = new Bundle(); - args.putInt(FlexibleSpaceWithImageScrollViewFragment.ARG_SCROLL_Y, mScrollY); - f.setArguments(args); - } break; } case 1: { f = new FlexibleSpaceWithImageListViewFragment(); - if (0 <= mScrollY) { - Bundle args = new Bundle(); - args.putInt(FlexibleSpaceWithImageListViewFragment.ARG_SCROLL_Y, mScrollY); - f.setArguments(args); - } break; } case 2: default: { f = new FlexibleSpaceWithImageRecyclerViewFragment(); - if (0 <= mScrollY) { - Bundle args = new Bundle(); - args.putInt(FlexibleSpaceWithImageRecyclerViewFragment.ARG_SCROLL_Y, mScrollY); - f.setArguments(args); - } break; } } + f.setArguments(mScrollY); return f; } From 5aa73bead242a44681485bf21a7a6ee89bb8951d Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 31 May 2015 22:31:45 +0900 Subject: [PATCH 143/208] Refactored. --- .../FlexibleSpaceWithImageBaseFragment.java | 12 ++++ ...lexibleSpaceWithImageListViewFragment.java | 24 ++++++++ ...bleSpaceWithImageRecyclerViewFragment.java | 26 +++++++++ ...paceWithImageWithViewPagerTabActivity.java | 55 ++----------------- 4 files changed, 67 insertions(+), 50 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java index 0c0e07cc..47d19fc1 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageBaseFragment.java @@ -36,6 +36,18 @@ public void setArguments(int scrollY) { } } + public void setScrollY(int scrollY, int threshold) { + View view = getView(); + if (view == null) { + return; + } + Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll); + if (scrollView == null) { + return; + } + scrollView.scrollVerticallyTo(scrollY); + } + protected void updateFlexibleSpace(int scrollY) { updateFlexibleSpace(scrollY, getView()); } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java index b5c57ef6..41916528 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageListViewFragment.java @@ -75,6 +75,30 @@ public void run() { return view; } + @SuppressWarnings("NewApi") + @Override + public void setScrollY(int scrollY, int threshold) { + View view = getView(); + if (view == null) { + return; + } + ObservableListView listView = (ObservableListView) view.findViewById(R.id.scroll); + if (listView == null) { + return; + } + View firstVisibleChild = listView.getChildAt(0); + if (firstVisibleChild != null) { + int offset = scrollY; + int position = 0; + if (threshold < scrollY) { + int baseHeight = firstVisibleChild.getHeight(); + position = scrollY / baseHeight; + offset = scrollY % baseHeight; + } + listView.setSelectionFromTop(position, -offset); + } + } + @Override protected void updateFlexibleSpace(int scrollY, View view) { int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java index 2fe198a9..eb59162c 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewFragment.java @@ -71,6 +71,32 @@ public void run() { return view; } + @Override + public void setScrollY(int scrollY, int threshold) { + View view = getView(); + if (view == null) { + return; + } + ObservableRecyclerView recyclerView = (ObservableRecyclerView) view.findViewById(R.id.scroll); + if (recyclerView == null) { + return; + } + View firstVisibleChild = recyclerView.getChildAt(0); + if (firstVisibleChild != null) { + int offset = scrollY; + int position = 0; + if (threshold < scrollY) { + int baseHeight = firstVisibleChild.getHeight(); + position = scrollY / baseHeight; + offset = scrollY % baseHeight; + } + RecyclerView.LayoutManager lm = recyclerView.getLayoutManager(); + if (lm != null && lm instanceof LinearLayoutManager) { + ((LinearLayoutManager) lm).scrollToPositionWithOffset(position, -offset); + } + } + } + @Override protected void updateFlexibleSpace(int scrollY, View view) { int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java index b54e6eb1..1bcd0956 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java @@ -16,7 +16,6 @@ package com.github.ksoichiro.android.observablescrollview.samples; -import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.res.Configuration; import android.os.Build; @@ -24,14 +23,10 @@ import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.view.ViewPager; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.TextView; import com.github.ksoichiro.android.observablescrollview.CacheFragmentStatePagerAdapter; -import com.github.ksoichiro.android.observablescrollview.ObservableListView; -import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; import com.github.ksoichiro.android.observablescrollview.ScrollUtils; import com.github.ksoichiro.android.observablescrollview.Scrollable; import com.google.samples.apps.iosched.ui.widget.SlidingTabLayout; @@ -40,9 +35,9 @@ /** *

      Another implementation of FlexibleImage pattern + ViewPager.

      - * + *

      *

      This is a completely different approach comparing to FlexibleImageWithViewPager2Activity. - * + *

      *

      Descriptions of this pattern:

      *
        *
      • When the current tab is changed, tabs will be translated in Y-axis @@ -54,7 +49,7 @@ * the parent Activity will update the Fragment's state when the tab is changed, * and Fragments will tell the parent Activity to update the tab's translationY.
      • *
      - * + *

      *

      SlidingTabLayout and SlidingTabStrip are from google/iosched:
      * https://github.com/google/iosched

      */ @@ -104,7 +99,7 @@ public void run() { * so each Fragments will pass themselves for Activity to check if they are active. * * @param scrollY scroll position of Scrollable - * @param s caller Scrollable view + * @param s caller Scrollable view */ public void onScrollChanged(int scrollY, Scrollable s) { FlexibleSpaceWithImageBaseFragment fragment = @@ -208,51 +203,11 @@ private void propagateScroll(int scrollY) { if (view == null) { continue; } - propagateScroll(view, scrollY); + f.setScrollY(scrollY, mFlexibleSpaceHeight); f.updateFlexibleSpace(scrollY); } } - @SuppressLint("NewApi") - private void propagateScroll(View view, int scrollY) { - Scrollable scrollView = (Scrollable) view.findViewById(R.id.scroll); - if (scrollView == null) { - return; - } - if (scrollView instanceof ObservableRecyclerView) { - final ObservableRecyclerView recyclerView = (ObservableRecyclerView) scrollView; - View firstVisibleChild = recyclerView.getChildAt(0); - if (firstVisibleChild != null) { - int offset = scrollY; - int position = 0; - if (mFlexibleSpaceHeight < scrollY) { - int baseHeight = firstVisibleChild.getHeight(); - position = scrollY / baseHeight; - offset = scrollY % baseHeight; - } - RecyclerView.LayoutManager lm = recyclerView.getLayoutManager(); - if (lm != null && lm instanceof LinearLayoutManager) { - ((LinearLayoutManager) lm).scrollToPositionWithOffset(position, -offset); - } - } - } else if (scrollView instanceof ObservableListView) { - ObservableListView listView = (ObservableListView) scrollView; - View firstVisibleChild = listView.getChildAt(0); - if (firstVisibleChild != null) { - int offset = scrollY; - int position = 0; - if (mFlexibleSpaceHeight < scrollY) { - int baseHeight = firstVisibleChild.getHeight(); - position = scrollY / baseHeight; - offset = scrollY % baseHeight; - } - listView.setSelectionFromTop(position, -offset); - } - } else { - scrollView.scrollVerticallyTo(scrollY); - } - } - /** * This adapter provides three types of fragments as an example. * {@linkplain #createItem(int)} should be modified if you use this example for your app. From e85cc3ff2160e805f77c3fbf6fc1f42376bd3953 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 1 Jun 2015 21:58:50 +0900 Subject: [PATCH 144/208] Refactored. --- .../samples/FlexibleSpaceToolbarWebViewActivity.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java index 0ebde34a..f75cf83d 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java @@ -36,7 +36,6 @@ public class FlexibleSpaceToolbarWebViewActivity extends BaseActivity implements private View mToolbarView; private TextView mTitleView; private int mFlexibleSpaceHeight; - private WebView mWebView; private View mWebViewContainer; @@ -57,20 +56,20 @@ protected void onCreate(Bundle savedInstanceState) { setTitle(null); mToolbarView = findViewById(R.id.toolbar); - mWebView = (WebView) findViewById(R.id.webView); mWebViewContainer = findViewById(R.id.webViewContainer); final ObservableScrollView scrollView = (ObservableScrollView) findViewById(R.id.scroll); scrollView.setScrollViewCallbacks(this); - mWebView.loadUrl("file:///android_asset/lipsum.html"); + WebView webView = (WebView) findViewById(R.id.webView); + webView.loadUrl("file:///android_asset/lipsum.html"); mFlexibleSpaceHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_height); int flexibleSpaceAndToolbarHeight = mFlexibleSpaceHeight + getActionBarSize(); - final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mWebView.getLayoutParams(); + final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) webView.getLayoutParams(); layoutParams.topMargin = flexibleSpaceAndToolbarHeight; - mWebView.setLayoutParams(layoutParams); + webView.setLayoutParams(layoutParams); mFlexibleSpaceView.getLayoutParams().height = flexibleSpaceAndToolbarHeight; From b5c711a34c49401f900e80e1902f07d8106f94a5 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 2 Jun 2015 23:15:46 +0900 Subject: [PATCH 145/208] Updated comment. --- .../FlexibleSpaceWithImageWithViewPagerTab2Activity.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java index e490b79d..43b395d8 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTab2Activity.java @@ -47,11 +47,9 @@ *

      This uses TouchInterceptionFrameLayout to move Fragments.

      * *

      There is an unsolved problem: it doesn't scroll smoothly - * when the flexible space is changing because TouchInterceptionFrameLayout - * moves without velocity.
      + * when the flexible space is changing.
      * If it's a big problem to you, please also check - * FlexibleSpaceWithImageWithViewPagerTabActivity.
      - * There are some differences, but it scrolls smoothly.

      + * FlexibleSpaceWithImageWithViewPagerTabActivity.

      * *

      SlidingTabLayout and SlidingTabStrip are from google/iosched:
      * https://github.com/google/iosched

      From 9432499752d7aa35be8303dbbb859d1aa8ab7a66 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 3 Jun 2015 22:30:56 +0900 Subject: [PATCH 146/208] Removed space. --- .../samples/FlexibleSpaceToolbarWebViewActivity.java | 1 - .../samples/FlexibleSpaceWithImageRecyclerViewActivity.java | 4 ---- 2 files changed, 5 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java index f75cf83d..6a9719f1 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceToolbarWebViewActivity.java @@ -38,7 +38,6 @@ public class FlexibleSpaceToolbarWebViewActivity extends BaseActivity implements private int mFlexibleSpaceHeight; private View mWebViewContainer; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java index f04a43d7..05aa8a65 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageRecyclerViewActivity.java @@ -77,8 +77,6 @@ public void run() { ViewHelper.setScaleY(mTitleView, scale); } }); - - } @@ -111,12 +109,10 @@ public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) @Override public void onDownMotionEvent() { - } @Override public void onUpOrCancelMotionEvent(ScrollState scrollState) { - } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) From fbb4863171bc3663558d52d21648ee1c824a2c6c Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Thu, 4 Jun 2015 22:27:35 +0900 Subject: [PATCH 147/208] Bumped version to 1.5.2. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e82f9532..fb9c6c96 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.5.2-SNAPSHOT +VERSION_NAME=1.5.2 SYNCED_VERSION_NAME=1.5.1 GROUP=com.github.ksoichiro From 854b684019642fb02cafa6284fa5c924a80149af Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 5 Jun 2015 07:00:46 +0900 Subject: [PATCH 148/208] Updated synched version. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index fb9c6c96..6ba21bc0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ VERSION_NAME=1.5.2 -SYNCED_VERSION_NAME=1.5.1 +SYNCED_VERSION_NAME=1.5.2 GROUP=com.github.ksoichiro POM_DESCRIPTION=Android library to observe scroll events on scrollable views. From 0853ab8d8595ea999ab2f496c811974ce45352da Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Thu, 11 Jun 2015 21:48:10 +0900 Subject: [PATCH 149/208] #148 Added HeaderGridView feature to ObservableGridView. --- .../ObservableGridView.java | 331 +++++++++++++++++- ...ragment_flexiblespacewithimagegridview.xml | 33 ++ ...lexibleSpaceWithImageGridViewFragment.java | 118 +++++++ ...paceWithImageWithViewPagerTabActivity.java | 10 +- 4 files changed, 488 insertions(+), 4 deletions(-) create mode 100644 samples/res/layout/fragment_flexiblespacewithimagegridview.xml create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewFragment.java diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index 7b759726..afee747f 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -1,5 +1,6 @@ /* - * Copyright 2014 Soichiro Kashima + * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2014 Soichiro Kashima * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +18,8 @@ package com.github.ksoichiro.android.observablescrollview; import android.content.Context; +import android.database.DataSetObservable; +import android.database.DataSetObserver; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -26,7 +29,15 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.Filter; +import android.widget.Filterable; +import android.widget.FrameLayout; import android.widget.GridView; +import android.widget.ListAdapter; +import android.widget.WrapperListAdapter; + +import java.util.ArrayList; /** * GridView that its scroll position can be observed. @@ -49,6 +60,7 @@ public class ObservableGridView extends GridView implements Scrollable { private boolean mIntercepted; private MotionEvent mPrevMoveEvent; private ViewGroup mTouchInterceptionViewGroup; + private ArrayList mHeaderViewInfos; private OnScrollListener mOriginalScrollListener; private OnScrollListener mScrollListener = new OnScrollListener() { @@ -229,8 +241,79 @@ public int getCurrentScrollY() { return mScrollY; } + @Override + public void setClipChildren(boolean clipChildren) { + // Ignore, since the header rows depend on not being clipped + } + + @Override + public void setAdapter(ListAdapter adapter) { + if (0 < mHeaderViewInfos.size()) { + HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos, adapter); + int numColumns = getNumColumnsCompat(); + if (1 < numColumns) { + headerViewGridAdapter.setNumColumns(numColumns); + } + super.setAdapter(headerViewGridAdapter); + } else { + super.setAdapter(adapter); + } + } + + public void addHeaderView(View v, Object data, boolean isSelectable) { + ListAdapter adapter = getAdapter(); + if (adapter != null && !(adapter instanceof HeaderViewGridAdapter)) { + throw new IllegalStateException("Cannot add header view to grid -- setAdapter has already been called."); + } + FixedViewInfo info = new FixedViewInfo(); + FrameLayout fl = new FullWidthFixedViewLayout(getContext()); + fl.addView(v); + info.view = v; + info.viewContainer = fl; + info.data = data; + info.isSelectable = isSelectable; + mHeaderViewInfos.add(info); + // in the case of re-adding a header view, or adding one later on, + // we need to notify the observer + if (adapter != null) { + ((HeaderViewGridAdapter) adapter).notifyDataSetChanged(); + } + } + + public void addHeaderView(View v) { + addHeaderView(v, null, true); + } + + public int getHeaderViewCount() { + return mHeaderViewInfos.size(); + } + + public boolean removeHeaderView(View v) { + if (mHeaderViewInfos.size() > 0) { + boolean result = false; + ListAdapter adapter = getAdapter(); + if (adapter != null && adapter instanceof HeaderViewGridAdapter && ((HeaderViewGridAdapter) adapter).removeHeader(v)) { + result = true; + } + removeFixedViewInfo(v, mHeaderViewInfos); + return result; + } + return false; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + ListAdapter adapter = getAdapter(); + if (adapter != null && adapter instanceof HeaderViewGridAdapter) { + ((HeaderViewGridAdapter) adapter).setNumColumns(getNumColumnsCompat()); + } + } + private void init() { mChildrenHeights = new SparseIntArray(); + mHeaderViewInfos = new ArrayList<>(); + super.setClipChildren(false); super.setOnScrollListener(mScrollListener); } @@ -314,6 +397,33 @@ private void onScrollChanged() { } } + private void removeFixedViewInfo(View v, ArrayList where) { + int len = where.size(); + for (int i = 0; i < len; ++i) { + FixedViewInfo info = where.get(i); + if (info.view == v) { + where.remove(i); + break; + } + } + } + + private class FullWidthFixedViewLayout extends FrameLayout { + public FullWidthFixedViewLayout(Context context) { + super(context); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int targetWidth = ObservableGridView.this.getMeasuredWidth() + - ObservableGridView.this.getPaddingLeft() + - ObservableGridView.this.getPaddingRight(); + widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth, + MeasureSpec.getMode(widthMeasureSpec)); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + static class SavedState extends BaseSavedState { int prevFirstVisiblePosition; int prevFirstVisibleChildHeight = -1; @@ -381,4 +491,223 @@ public SavedState[] newArray(int size) { } }; } + + static class FixedViewInfo { + public View view; + public ViewGroup viewContainer; + public Object data; + public boolean isSelectable; + } + + static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable { + private final DataSetObservable mDataSetObservable = new DataSetObservable(); + private final ListAdapter mAdapter; + private int mNumColumns = 1; + + ArrayList mHeaderViewInfos; + boolean mAreAllFixedViewsSelectable; + private final boolean mIsFilterable; + + public HeaderViewGridAdapter(ArrayList headerViewInfos, ListAdapter adapter) { + mAdapter = adapter; + mIsFilterable = adapter instanceof Filterable; + if (headerViewInfos == null) { + throw new IllegalArgumentException("headerViewInfos cannot be null"); + } + mHeaderViewInfos = headerViewInfos; + mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos); + } + + public int getHeadersCount() { + return mHeaderViewInfos.size(); + } + + public void setNumColumns(int numColumns) { + if (numColumns < 1) { + throw new IllegalArgumentException("Number of columns must be 1 or more"); + } + if (mNumColumns != numColumns) { + mNumColumns = numColumns; + notifyDataSetChanged(); + } + } + + public boolean removeHeader(View v) { + for (int i = 0; i < mHeaderViewInfos.size(); i++) { + FixedViewInfo info = mHeaderViewInfos.get(i); + if (info.view == v) { + mHeaderViewInfos.remove(i); + mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos); + mDataSetObservable.notifyChanged(); + return true; + } + } + return false; + } + + @Override + public ListAdapter getWrappedAdapter() { + return mAdapter; + } + + @Override + public boolean areAllItemsEnabled() { + return mAdapter == null || mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled(); + } + + @Override + public boolean isEnabled(int position) { + // Header (negative positions will throw an ArrayIndexOutOfBoundsException) + int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; + if (position < numHeadersAndPlaceholders) { + return (position % mNumColumns == 0) + && mHeaderViewInfos.get(position / mNumColumns).isSelectable; + } + // Adapter + if (mAdapter != null) { + final int adjPosition = position - numHeadersAndPlaceholders; + if (adjPosition < mAdapter.getCount()) { + return mAdapter.isEnabled(adjPosition); + } + } + throw new ArrayIndexOutOfBoundsException(position); + } + + @Override + public void registerDataSetObserver(DataSetObserver observer) { + mDataSetObservable.registerObserver(observer); + if (mAdapter != null) { + mAdapter.registerDataSetObserver(observer); + } + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + mDataSetObservable.unregisterObserver(observer); + if (mAdapter != null) { + mAdapter.unregisterDataSetObserver(observer); + } + } + + @Override + public int getCount() { + if (mAdapter != null) { + return getHeadersCount() * mNumColumns + mAdapter.getCount(); + } else { + return getHeadersCount() * mNumColumns; + } + } + + @Override + public Object getItem(int position) { + // Header (negative positions will throw an ArrayIndexOutOfBoundsException) + int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; + if (position < numHeadersAndPlaceholders) { + if (position % mNumColumns == 0) { + return mHeaderViewInfos.get(position / mNumColumns).data; + } + return null; + } + // Adapter + if (mAdapter != null) { + final int adjPosition = position - numHeadersAndPlaceholders; + if (adjPosition < mAdapter.getCount()) { + return mAdapter.getItem(adjPosition); + } + } + throw new ArrayIndexOutOfBoundsException(position); + } + + @Override + public long getItemId(int position) { + int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; + if (mAdapter != null && numHeadersAndPlaceholders <= position) { + int adjPosition = position - numHeadersAndPlaceholders; + if (adjPosition < mAdapter.getCount()) { + return mAdapter.getItemId(adjPosition); + } + } + return -1; + } + + @Override + public boolean hasStableIds() { + return mAdapter != null && mAdapter.hasStableIds(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + // Header (negative positions will throw an ArrayIndexOutOfBoundsException) + int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; + if (position < numHeadersAndPlaceholders) { + View headerViewContainer = mHeaderViewInfos.get(position / mNumColumns).viewContainer; + if (position % mNumColumns == 0) { + return headerViewContainer; + } else { + if (convertView == null) { + convertView = new View(parent.getContext()); + } + // We need to do this because GridView uses the height of the last item + // in a row to determine the height for the entire row. + convertView.setVisibility(View.INVISIBLE); + convertView.setMinimumHeight(headerViewContainer.getHeight()); + return convertView; + } + } + // Adapter + if (mAdapter != null) { + final int adjPosition = position - numHeadersAndPlaceholders; + if (adjPosition < mAdapter.getCount()) { + return mAdapter.getView(adjPosition, convertView, parent); + } + } + throw new ArrayIndexOutOfBoundsException(position); + } + + @Override + public int getItemViewType(int position) { + int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; + if (position < numHeadersAndPlaceholders && (position % mNumColumns != 0)) { + // Placeholders get the last view type number + return mAdapter != null ? mAdapter.getViewTypeCount() : 1; + } + if (mAdapter != null && position >= numHeadersAndPlaceholders) { + int adjPosition = position - numHeadersAndPlaceholders; + if (adjPosition < mAdapter.getCount()) { + return mAdapter.getItemViewType(adjPosition); + } + } + return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER; + } + + @Override + public int getViewTypeCount() { + return mAdapter == null ? 2 : (mAdapter.getViewTypeCount() + 1); + } + + @Override + public boolean isEmpty() { + return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0; + } + + @Override + public Filter getFilter() { + return mIsFilterable ? ((Filterable) mAdapter).getFilter() : null; + } + + public void notifyDataSetChanged() { + mDataSetObservable.notifyChanged(); + } + + private boolean areAllListInfosSelectable(ArrayList infos) { + if (infos != null) { + for (FixedViewInfo info : infos) { + if (!info.isSelectable) { + return false; + } + } + } + return true; + } + } } diff --git a/samples/res/layout/fragment_flexiblespacewithimagegridview.xml b/samples/res/layout/fragment_flexiblespacewithimagegridview.xml new file mode 100644 index 00000000..7d323b88 --- /dev/null +++ b/samples/res/layout/fragment_flexiblespacewithimagegridview.xml @@ -0,0 +1,33 @@ + + + + + + + diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewFragment.java new file mode 100644 index 00000000..84864680 --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewFragment.java @@ -0,0 +1,118 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.nineoldandroids.view.ViewHelper; + +public class FlexibleSpaceWithImageGridViewFragment extends FlexibleSpaceWithImageBaseFragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_flexiblespacewithimagegridview, container, false); + + final ObservableGridView gridView = (ObservableGridView) view.findViewById(R.id.scroll); + // Set padding view for GridView. This is the flexible space. + View paddingView = new View(getActivity()); + final int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, + flexibleSpaceImageHeight); + paddingView.setLayoutParams(lp); + + // This is required to disable header's list selector effect + paddingView.setClickable(true); + + gridView.addHeaderView(paddingView); + setDummyData(gridView); + // TouchInterceptionViewGroup should be a parent view other than ViewPager. + // This is a workaround for the issue #117: + // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 + gridView.setTouchInterceptionViewGroup((ViewGroup) view.findViewById(R.id.fragment_root)); + + // Scroll to the specified offset after layout + Bundle args = getArguments(); + if (args != null && args.containsKey(ARG_SCROLL_Y)) { + final int scrollY = args.getInt(ARG_SCROLL_Y, 0); + ScrollUtils.addOnGlobalLayoutListener(gridView, new Runnable() { + @SuppressLint("NewApi") + @Override + public void run() { + int offset = scrollY % flexibleSpaceImageHeight; + gridView.setSelectionFromTop(0, -offset); + } + }); + updateFlexibleSpace(scrollY, view); + } else { + updateFlexibleSpace(0, view); + } + + gridView.setScrollViewCallbacks(this); + + updateFlexibleSpace(0, view); + + return view; + } + + @SuppressWarnings("NewApi") + @Override + public void setScrollY(int scrollY, int threshold) { + View view = getView(); + if (view == null) { + return; + } + ObservableGridView gridView = (ObservableGridView) view.findViewById(R.id.scroll); + if (gridView == null) { + return; + } + View firstVisibleChild = gridView.getChildAt(0); + if (firstVisibleChild != null) { + int offset = scrollY; + int position = 0; + if (threshold < scrollY) { + int baseHeight = firstVisibleChild.getHeight(); + position = scrollY / baseHeight; + offset = scrollY % baseHeight; + } + gridView.setSelectionFromTop(position, -offset); + } + } + + @Override + protected void updateFlexibleSpace(int scrollY, View view) { + int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + + View listBackgroundView = view.findViewById(R.id.list_background); + + // Translate list background + ViewHelper.setTranslationY(listBackgroundView, Math.max(0, -scrollY + flexibleSpaceImageHeight)); + + // Also pass this event to parent Activity + FlexibleSpaceWithImageWithViewPagerTabActivity parentActivity = + (FlexibleSpaceWithImageWithViewPagerTabActivity) getActivity(); + if (parentActivity != null) { + parentActivity.onScrollChanged(scrollY, (ObservableGridView) view.findViewById(R.id.scroll)); + } + } +} diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java index 1bcd0956..c32c38f8 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageWithViewPagerTabActivity.java @@ -229,7 +229,7 @@ public void setScrollY(int scrollY) { @Override protected Fragment createItem(int position) { FlexibleSpaceWithImageBaseFragment f; - final int pattern = position % 3; + final int pattern = position % 4; switch (pattern) { case 0: { f = new FlexibleSpaceWithImageScrollViewFragment(); @@ -239,11 +239,15 @@ protected Fragment createItem(int position) { f = new FlexibleSpaceWithImageListViewFragment(); break; } - case 2: - default: { + case 2: { f = new FlexibleSpaceWithImageRecyclerViewFragment(); break; } + case 3: + default: { + f = new FlexibleSpaceWithImageGridViewFragment(); + break; + } } f.setArguments(mScrollY); return f; From 31cab6563f097b957f0dc34a36dc604741494c2c Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Thu, 11 Jun 2015 23:58:16 +0900 Subject: [PATCH 150/208] Added tests. --- library/src/androidTest/AndroidManifest.xml | 1 + .../test/HeaderGridViewActivity.java | 57 +++++++ .../test/HeaderGridViewActivityTest.java | 143 ++++++++++++++++++ library/src/androidTest/res/values/dimens.xml | 1 + .../ObservableGridView.java | 9 +- 5 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivity.java create mode 100644 library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java diff --git a/library/src/androidTest/AndroidManifest.xml b/library/src/androidTest/AndroidManifest.xml index 0bbcb891..d8476e95 100644 --- a/library/src/androidTest/AndroidManifest.xml +++ b/library/src/androidTest/AndroidManifest.xml @@ -20,6 +20,7 @@ + diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivity.java new file mode 100644 index 00000000..587b328d --- /dev/null +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivity.java @@ -0,0 +1,57 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.AbsListView; +import android.widget.FrameLayout; + +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; + +public class HeaderGridViewActivity extends Activity implements ObservableScrollViewCallbacks { + + public View headerView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_gridview); + ObservableGridView scrollable = (ObservableGridView) findViewById(R.id.scrollable); + // Set padding view for GridView. This is the flexible space. + headerView = new View(this); + final int flexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, + flexibleSpaceImageHeight); + headerView.setLayoutParams(lp); + + // This is required to disable header's list selector effect + headerView.setClickable(true); + + scrollable.addHeaderView(headerView); + scrollable.setScrollViewCallbacks(this); + UiTestUtils.setDummyData(this, scrollable); + scrollable.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + } + }); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } +} diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java new file mode 100644 index 00000000..1c593c8d --- /dev/null +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java @@ -0,0 +1,143 @@ +package com.github.ksoichiro.android.observablescrollview.test; + +import android.test.ActivityInstrumentationTestCase2; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ListAdapter; + +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; + +import java.util.ArrayList; + +public class HeaderGridViewActivityTest extends ActivityInstrumentationTestCase2 { + + private HeaderGridViewActivity activity; + private ObservableGridView scrollable; + + public HeaderGridViewActivityTest() { + super(HeaderGridViewActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + setActivityInitialTouchMode(true); + activity = getActivity(); + scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable); + } + + public void testScroll() throws Throwable { + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + getInstrumentation().waitForIdleSync(); + + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.DOWN); + getInstrumentation().waitForIdleSync(); + } + + public void testSaveAndRestoreInstanceState() throws Throwable { + UiTestUtils.saveAndRestoreInstanceState(this, activity); + testScroll(); + } + + public void testScrollVerticallyTo() throws Throwable { + final DisplayMetrics metrics = activity.getResources().getDisplayMetrics(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.scrollVerticallyTo((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, metrics)); + } + }); + getInstrumentation().waitForIdleSync(); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.scrollVerticallyTo(0); + } + }); + getInstrumentation().waitForIdleSync(); + } + + public void testHeaderViewFeatures() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertEquals(1, scrollable.getHeaderViewCount()); + ListAdapter adapter = scrollable.getAdapter(); + assertTrue(adapter instanceof ObservableGridView.HeaderViewGridAdapter); + ObservableGridView.HeaderViewGridAdapter hvgAdapter = (ObservableGridView.HeaderViewGridAdapter) adapter; + assertEquals(1, hvgAdapter.getHeadersCount()); + assertNotNull(hvgAdapter.getWrappedAdapter()); + assertTrue(hvgAdapter.areAllItemsEnabled()); + assertFalse(hvgAdapter.isEmpty()); + Object data = hvgAdapter.getItem(0); + assertNull(data); + assertNotNull(hvgAdapter.getView(0, null, scrollable)); + assertNotNull(hvgAdapter.getFilter()); + assertTrue(scrollable.removeHeaderView(activity.headerView)); + assertEquals(0, scrollable.getHeaderViewCount()); + assertEquals(0, hvgAdapter.getHeadersCount()); + assertFalse(scrollable.removeHeaderView(activity.headerView)); + + activity.headerView = new View(activity); + final int flexibleSpaceImageHeight = activity.getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, + flexibleSpaceImageHeight); + activity.headerView.setLayoutParams(lp); + + // This is required to disable header's list selector effect + activity.headerView.setClickable(true); + + scrollable.addHeaderView(activity.headerView); + } + }); + } + + public void testHeaderViewGridExceptions() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + try { + new ObservableGridView.HeaderViewGridAdapter(null, null); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("headerViewInfos cannot be null", e.getMessage()); + } + ListAdapter adapter = scrollable.getAdapter(); + ObservableGridView.HeaderViewGridAdapter hvgAdapter = (ObservableGridView.HeaderViewGridAdapter) adapter; + try { + hvgAdapter.setNumColumns(0); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Number of columns must be 1 or more", e.getMessage()); + } + ArrayList headerViewInfos = new ArrayList<>(); + ObservableGridView.HeaderViewGridAdapter adapter1 = new ObservableGridView.HeaderViewGridAdapter(headerViewInfos, null); + assertTrue(adapter1.isEmpty()); + try { + adapter1.isEnabled(-1); + fail(); + } catch (ArrayIndexOutOfBoundsException ignore) { + } + try { + adapter1.getItem(-1); + fail(); + } catch (ArrayIndexOutOfBoundsException ignore) { + } + try { + adapter1.getView(0, null, null); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Parent cannot be null", e.getMessage()); + } + try { + adapter1.getView(-1, null, scrollable); + fail(); + } catch (ArrayIndexOutOfBoundsException ignore) { + } + } + }); + } +} diff --git a/library/src/androidTest/res/values/dimens.xml b/library/src/androidTest/res/values/dimens.xml index 286c5be1..592125de 100644 --- a/library/src/androidTest/res/values/dimens.xml +++ b/library/src/androidTest/res/values/dimens.xml @@ -18,4 +18,5 @@ 72dp 56dp 16dp + 240dp diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index afee747f..bf956d47 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -492,14 +492,14 @@ public SavedState[] newArray(int size) { }; } - static class FixedViewInfo { + public static class FixedViewInfo { public View view; public ViewGroup viewContainer; public Object data; public boolean isSelectable; } - static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable { + public static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable { private final DataSetObservable mDataSetObservable = new DataSetObservable(); private final ListAdapter mAdapter; private int mNumColumns = 1; @@ -510,7 +510,7 @@ static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable { public HeaderViewGridAdapter(ArrayList headerViewInfos, ListAdapter adapter) { mAdapter = adapter; - mIsFilterable = adapter instanceof Filterable; + mIsFilterable = adapter != null && adapter instanceof Filterable; if (headerViewInfos == null) { throw new IllegalArgumentException("headerViewInfos cannot be null"); } @@ -637,6 +637,9 @@ public boolean hasStableIds() { @Override public View getView(int position, View convertView, ViewGroup parent) { + if (parent == null) { + throw new IllegalArgumentException("Parent cannot be null"); + } // Header (negative positions will throw an ArrayIndexOutOfBoundsException) int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; if (position < numHeadersAndPlaceholders) { From 554e33d278df6ec406e081bd31fff1a7fc6eb123 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 12 Jun 2015 00:25:57 +0900 Subject: [PATCH 151/208] Added tests. --- .../test/GridViewActivityTest.java | 10 ++++++++++ .../test/ListViewActivityTest.java | 10 ++++++++++ .../test/RecyclerViewActivityTest.java | 10 ++++++++++ .../test/ScrollViewActivityTest.java | 10 ++++++++++ .../TouchInterceptionScrollViewActivityTest.java | 12 ++++++++++++ .../test/WebViewActivityTest.java | 10 ++++++++++ 6 files changed, 62 insertions(+) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java index 7a08f350..f38784bd 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java @@ -24,6 +24,16 @@ protected void setUp() throws Exception { scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable); } + public void testInitialize() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + new ObservableGridView(activity); + new ObservableGridView(activity, null, 0); + } + }); + } + public void testScroll() throws Throwable { UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); getInstrumentation().waitForIdleSync(); diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java index 188fbc4f..c53fef9e 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java @@ -24,6 +24,16 @@ protected void setUp() throws Exception { scrollable = (ObservableListView) activity.findViewById(R.id.scrollable); } + public void testInitialize() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + new ObservableListView(activity); + new ObservableListView(activity, null, 0); + } + }); + } + public void testScroll() throws Throwable { UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); getInstrumentation().waitForIdleSync(); diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java index 33335ed0..e448dc33 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java @@ -25,6 +25,16 @@ protected void setUp() throws Exception { getInstrumentation().waitForIdleSync(); } + public void testInitialize() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + new ObservableRecyclerView(activity); + new ObservableRecyclerView(activity, null, 0); + } + }); + } + public void testScroll() throws Throwable { UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); getInstrumentation().waitForIdleSync(); diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java index c7bcf06a..fea0f1c2 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java @@ -22,6 +22,16 @@ protected void setUp() throws Exception { scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable); } + public void testInitialize() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + new ObservableScrollView(activity); + new ObservableScrollView(activity, null, 0); + } + }); + } + public void testScroll() throws Throwable { UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); getInstrumentation().waitForIdleSync(); diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java index 1c56fd40..f19501b4 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java @@ -5,6 +5,7 @@ import android.test.TouchUtils; import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; +import com.github.ksoichiro.android.observablescrollview.TouchInterceptionFrameLayout; public class TouchInterceptionScrollViewActivityTest extends ActivityInstrumentationTestCase2 { @@ -23,6 +24,17 @@ protected void setUp() throws Exception { scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable); } + public void testInitialize() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + new TouchInterceptionFrameLayout(activity); + new TouchInterceptionFrameLayout(activity, null, 0); + new TouchInterceptionFrameLayout(activity, null, 0, 0); + } + }); + } + public void testScroll() throws Throwable { TouchUtils.touchAndCancelView(this, scrollable); getInstrumentation().waitForIdleSync(); diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java index d6cc295e..faf9c5e3 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java @@ -24,6 +24,16 @@ protected void setUp() throws Exception { scrollable = (ObservableWebView) activity.findViewById(R.id.scrollable); } + public void testInitialize() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + new ObservableWebView(activity); + new ObservableWebView(activity, null, 0); + } + }); + } + public void testScroll() throws Throwable { UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); getInstrumentation().waitForIdleSync(); From 7998480e88c3d14281fe9e7ebb808850b437b493 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 12 Jun 2015 00:27:32 +0900 Subject: [PATCH 152/208] Released a new snapshot. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 6ba21bc0..009f8292 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.5.2 +VERSION_NAME=1.6.0-SNAPSHOT SYNCED_VERSION_NAME=1.5.2 GROUP=com.github.ksoichiro From 62cc8a4a18cece7ee9c214d3ee2b0541d4f35388 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 12 Jun 2015 01:38:06 +0900 Subject: [PATCH 153/208] Fixed a test not to execute on pre-Lollipop device. --- .../test/TouchInterceptionScrollViewActivityTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java index f19501b4..3773b2b0 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/TouchInterceptionScrollViewActivityTest.java @@ -1,6 +1,7 @@ package com.github.ksoichiro.android.observablescrollview.test; import android.app.Activity; +import android.os.Build; import android.test.ActivityInstrumentationTestCase2; import android.test.TouchUtils; @@ -30,7 +31,9 @@ public void testInitialize() throws Throwable { public void run() { new TouchInterceptionFrameLayout(activity); new TouchInterceptionFrameLayout(activity, null, 0); - new TouchInterceptionFrameLayout(activity, null, 0, 0); + if (Build.VERSION_CODES.LOLLIPOP <= Build.VERSION.SDK_INT) { + new TouchInterceptionFrameLayout(activity, null, 0, 0); + } } }); } From e308b602aa753d0ef91db524761bebafa6345ba1 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 13 Jun 2015 13:53:42 +0900 Subject: [PATCH 154/208] Updated recyclerview-v7 library version to 22.2.0. Also replaced getChildPosition() method to new getChildAdapterPosition() method. But it still works with recyclerview-v7 library version 21. --- library/build.gradle | 2 +- .../ObservableRecyclerView.java | 23 +++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 1337b067..0a49c2dc 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -15,7 +15,7 @@ repositories { } dependencies { - compile 'com.android.support:recyclerview-v7:21.0.0' + compile 'com.android.support:recyclerview-v7:22.2.0' androidTestCompile ('com.android.support:appcompat-v7:21.0.2') { exclude module: 'support-v4' } diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java index ea3ddf15..40011836 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java @@ -34,6 +34,8 @@ */ public class ObservableRecyclerView extends RecyclerView implements Scrollable { + private static int recyclerViewLibraryVersion = 22; + // Fields that should be saved onSaveInstanceState private int mPrevFirstVisiblePosition; private int mPrevFirstVisibleChildHeight = -1; @@ -96,8 +98,8 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (mCallbacks != null) { if (getChildCount() > 0) { - int firstVisiblePosition = getChildPosition(getChildAt(0)); - int lastVisiblePosition = getChildPosition(getChildAt(getChildCount() - 1)); + int firstVisiblePosition = getChildAdapterPosition(getChildAt(0)); + int lastVisiblePosition = getChildAdapterPosition(getChildAt(getChildCount() - 1)); for (int i = firstVisiblePosition, j = 0; i <= lastVisiblePosition; i++, j++) { if (mChildrenHeights.indexOfKey(i) < 0 || getChildAt(j).getHeight() != mChildrenHeights.get(i)) { mChildrenHeights.put(i, getChildAt(j).getHeight()); @@ -309,8 +311,25 @@ public int getCurrentScrollY() { return mScrollY; } + @SuppressWarnings("deprecation") + public int getChildAdapterPosition(View child) { + if (22 <= recyclerViewLibraryVersion) { + return super.getChildAdapterPosition(child); + } + return getChildPosition(child); + } + private void init() { mChildrenHeights = new SparseIntArray(); + checkLibraryVersion(); + } + + private void checkLibraryVersion() { + try { + super.getChildAdapterPosition(null); + } catch (NoSuchMethodError e) { + recyclerViewLibraryVersion = 21; + } } /** From b56552270ead6ccf815abbff55e94784cd172515 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 13 Jun 2015 14:46:40 +0900 Subject: [PATCH 155/208] #149 Avoid NPE in ObservableRecyclerView. --- .../observablescrollview/ObservableRecyclerView.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java index 40011836..d81fa804 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java @@ -101,9 +101,12 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { int firstVisiblePosition = getChildAdapterPosition(getChildAt(0)); int lastVisiblePosition = getChildAdapterPosition(getChildAt(getChildCount() - 1)); for (int i = firstVisiblePosition, j = 0; i <= lastVisiblePosition; i++, j++) { - if (mChildrenHeights.indexOfKey(i) < 0 || getChildAt(j).getHeight() != mChildrenHeights.get(i)) { - mChildrenHeights.put(i, getChildAt(j).getHeight()); + int childHeight = 0; + View child = getChildAt(j); + if (mChildrenHeights.indexOfKey(i) < 0 || (child != null && child.getHeight() != mChildrenHeights.get(i))) { + childHeight = child.getHeight(); } + mChildrenHeights.put(i, childHeight); } View firstVisibleChild = getChildAt(0); From b5a438713a8545e8a381acd10fb911ee52d677bc Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 14 Jun 2015 10:38:46 +0900 Subject: [PATCH 156/208] Fixed ViewPagerTabRecyclerViewFragment to apply a workaround for #117. --- .../samples/ViewPagerTabRecyclerViewFragment.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabRecyclerViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabRecyclerViewFragment.java index ea9a8ce2..9cbf8c81 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabRecyclerViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabRecyclerViewFragment.java @@ -54,6 +54,12 @@ public void run() { } }); } + + // TouchInterceptionViewGroup should be a parent view other than ViewPager. + // This is a workaround for the issue #117: + // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 + recyclerView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.root)); + recyclerView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); } return view; From 61f8640a062bf500b1e9a6ec095b4eb5474c041b Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 14 Jun 2015 10:46:10 +0900 Subject: [PATCH 157/208] Added GridView to ViewPagerTabActivity. Now we can add header to ObservableGridView, so it works on ViewPagerTabActivity. --- .../samples/BaseFragment.java | 7 ++ .../samples/ViewPagerTabActivity.java | 15 ++++- .../samples/ViewPagerTabGridViewFragment.java | 64 +++++++++++++++++++ 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabGridViewFragment.java diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java index bafafa35..17574263 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/BaseFragment.java @@ -26,6 +26,8 @@ import android.widget.GridView; import android.widget.ListView; +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; + import java.util.ArrayList; public abstract class BaseFragment extends Fragment { @@ -68,6 +70,11 @@ protected void setDummyData(GridView gridView) { gridView.setAdapter(new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, getDummyData())); } + protected void setDummyDataWithHeader(ObservableGridView gridView, View headerView) { + gridView.addHeaderView(headerView); + setDummyData(gridView); + } + protected void setDummyData(RecyclerView recyclerView) { recyclerView.setAdapter(new SimpleRecyclerAdapter(getActivity(), getDummyData())); } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabActivity.java index 76394542..b62cc006 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabActivity.java @@ -254,7 +254,7 @@ protected Fragment createItem(int position) { // Initialize fragments. // Please be sure to pass scroll position to each fragments using setArguments. Fragment f; - final int pattern = position % 3; + final int pattern = position % 4; switch (pattern) { case 0: { f = new ViewPagerTabScrollViewFragment(); @@ -274,8 +274,7 @@ protected Fragment createItem(int position) { } break; } - case 2: - default: { + case 2: { f = new ViewPagerTabRecyclerViewFragment(); if (0 < mScrollY) { Bundle args = new Bundle(); @@ -284,6 +283,16 @@ protected Fragment createItem(int position) { } break; } + case 3: + default: { + f = new ViewPagerTabGridViewFragment(); + if (0 < mScrollY) { + Bundle args = new Bundle(); + args.putInt(ViewPagerTabGridViewFragment.ARG_INITIAL_POSITION, 1); + f.setArguments(args); + } + break; + } } return f; } diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabGridViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabGridViewFragment.java new file mode 100644 index 00000000..5f53f231 --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabGridViewFragment.java @@ -0,0 +1,64 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; + +public class ViewPagerTabGridViewFragment extends BaseFragment { + + public static final String ARG_INITIAL_POSITION = "ARG_INITIAL_POSITION"; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_gridview, container, false); + + Activity parentActivity = getActivity(); + final ObservableGridView gridView = (ObservableGridView) view.findViewById(R.id.scroll); + setDummyDataWithHeader(gridView, inflater.inflate(R.layout.padding, gridView, false)); + + if (parentActivity instanceof ObservableScrollViewCallbacks) { + // Scroll to the specified position after layout + Bundle args = getArguments(); + if (args != null && args.containsKey(ARG_INITIAL_POSITION)) { + final int initialPosition = args.getInt(ARG_INITIAL_POSITION, 0); + ScrollUtils.addOnGlobalLayoutListener(gridView, new Runnable() { + @Override + public void run() { + // scrollTo() doesn't work, should use setSelection() + gridView.setSelection(initialPosition); + } + }); + } + + // TouchInterceptionViewGroup should be a parent view other than ViewPager. + // This is a workaround for the issue #117: + // https://github.com/ksoichiro/Android-ObservableScrollView/issues/117 + gridView.setTouchInterceptionViewGroup((ViewGroup) parentActivity.findViewById(R.id.root)); + + gridView.setScrollViewCallbacks((ObservableScrollViewCallbacks) parentActivity); + } + return view; + } +} From 07f9e9a73ff0ff4fe68e07aac2c6f690c29d90fd Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 14 Jun 2015 11:19:21 +0900 Subject: [PATCH 158/208] (website) Changed email link style to generate constant html with harp. Currently, I use harp.js to generate website, but it generates several patterns for email link. URL is encoded but every time it changes (unicode and ascii are mixed?), and it causes redundant commits. As a workaround, I changed <> style to []() style. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c3833876..ae81183e 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Please check the [FAQ](docs/faq.md) and [contributing guideline](https://github. ## Developed By -* Soichiro Kashima - +* Soichiro Kashima - [soichiro.kashima@gmail.com](mailto:soichiro.kashima@gmail.com) ## Thanks From b228c925e30dd6c6306d52fce52642768ed8a39b Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 15 Jun 2015 22:53:07 +0900 Subject: [PATCH 159/208] Updated release note for v1.5.2. --- docs/reference/release-notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/reference/release-notes.md b/docs/reference/release-notes.md index 4fa80dc3..ef771571 100644 --- a/docs/reference/release-notes.md +++ b/docs/reference/release-notes.md @@ -1,5 +1,7 @@ # Release notes +* v1.5.2 + * Fix `ObservableGridView` to use first child of line in height calculation. * v1.5.1 * Fix `scrollY` of `onScrollChanged` in `ObservableGridView` jumps when the first visible item changes. From dc77c14f931e8e62bdf03378077666bb05cb87ee Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 16 Jun 2015 21:16:16 +0900 Subject: [PATCH 160/208] Fixed FlexibleSpaceWithImageGridViewFragment not to crash on pre-Lollipop. --- ...lexibleSpaceWithImageGridViewFragment.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewFragment.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewFragment.java index 84864680..91b9716d 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewFragment.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewFragment.java @@ -17,6 +17,8 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.os.Build; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -60,7 +62,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa @Override public void run() { int offset = scrollY % flexibleSpaceImageHeight; - gridView.setSelectionFromTop(0, -offset); + setSelectionFromTop(gridView, 0, -offset); } }); updateFlexibleSpace(scrollY, view); @@ -95,7 +97,7 @@ public void setScrollY(int scrollY, int threshold) { position = scrollY / baseHeight; offset = scrollY % baseHeight; } - gridView.setSelectionFromTop(position, -offset); + setSelectionFromTop(gridView, position, -offset); } } @@ -115,4 +117,19 @@ protected void updateFlexibleSpace(int scrollY, View view) { parentActivity.onScrollChanged(scrollY, (ObservableGridView) view.findViewById(R.id.scroll)); } } + + /* + * setSelectionFromTop method has been moved from ListView to AbsListView since API level 21, + * so for API level 21-, we need to use other method to scroll with offset. + * smoothScrollToPositionFromTop seems to work, but it's from API level 11. + * We can't use GridView for Gingerbread. + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void setSelectionFromTop(ObservableGridView gridView, int position, int offset) { + if (Build.VERSION_CODES.LOLLIPOP <= Build.VERSION.SDK_INT) { + gridView.setSelectionFromTop(position, offset); + } else if (Build.VERSION_CODES.HONEYCOMB <= Build.VERSION.SDK_INT) { + gridView.smoothScrollToPositionFromTop(position, offset, 0); + } + } } From dccf1cd9a497729c33d667be9c5ecdad6e3fec9c Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 17 Jun 2015 23:11:16 +0900 Subject: [PATCH 161/208] Added ParallaxToolbarGridView example. --- samples/AndroidManifest.xml | 10 ++ .../activity_parallaxtoolbargridview.xml | 50 ++++++++++ samples/res/values/strings.xml | 1 + .../ParallaxToolbarGridViewActivity.java | 96 +++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 samples/res/layout/activity_parallaxtoolbargridview.xml create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarGridViewActivity.java diff --git a/samples/AndroidManifest.xml b/samples/AndroidManifest.xml index 8b7c3978..a6329303 100644 --- a/samples/AndroidManifest.xml +++ b/samples/AndroidManifest.xml @@ -317,6 +317,16 @@ + + + + + + + + + + + + + + + + + + + diff --git a/samples/res/values/strings.xml b/samples/res/values/strings.xml index 54a7646b..5ae9b43e 100644 --- a/samples/res/values/strings.xml +++ b/samples/res/values/strings.xml @@ -44,6 +44,7 @@ Handling touch with RecyclerView Handling touch with ScrollView Handling touch with WebView + Parallax gridView & toolbar Parallax listView & toolbar Parallax scrollView & toolbar ListView, scrolling from bottom diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarGridViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarGridViewActivity.java new file mode 100644 index 00000000..3bf08ed0 --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ParallaxToolbarGridViewActivity.java @@ -0,0 +1,96 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.AbsListView; + +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.nineoldandroids.view.ViewHelper; + +/** + * This example depends on {@code ObservableGridView#addHeaderView()} method introduced in v1.6.0. + */ +public class ParallaxToolbarGridViewActivity extends BaseActivity implements ObservableScrollViewCallbacks { + + private View mImageView; + private View mToolbarView; + private View mListBackgroundView; + private ObservableGridView mGridView; + private int mParallaxImageHeight; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_parallaxtoolbargridview) ; + + setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); + + mImageView = findViewById(R.id.image); + mToolbarView = findViewById(R.id.toolbar); + mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(0, getResources().getColor(R.color.primary))); + + mParallaxImageHeight = getResources().getDimensionPixelSize(R.dimen.parallax_image_height); + + mGridView = (ObservableGridView) findViewById(R.id.list); + mGridView.setScrollViewCallbacks(this); + // Set padding view for ListView. This is the flexible space. + View paddingView = new View(this); + AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, + mParallaxImageHeight); + paddingView.setLayoutParams(lp); + + // This is required to disable header's list selector effect + paddingView.setClickable(true); + + mGridView.addHeaderView(paddingView); + setDummyData(mGridView); + + // mListBackgroundView makes ListView's background except header view. + mListBackgroundView = findViewById(R.id.list_background); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + onScrollChanged(mGridView.getCurrentScrollY(), false, false); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + int baseColor = getResources().getColor(R.color.primary); + float alpha = Math.min(1, (float) scrollY / mParallaxImageHeight); + mToolbarView.setBackgroundColor(ScrollUtils.getColorWithAlpha(alpha, baseColor)); + ViewHelper.setTranslationY(mImageView, -scrollY / 2); + + // Translate list background + ViewHelper.setTranslationY(mListBackgroundView, Math.max(0, -scrollY + mParallaxImageHeight)); + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } +} From c76542956e514414b265c1dec5cfbbefc46e8870 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Thu, 18 Jun 2015 21:31:09 +0900 Subject: [PATCH 162/208] Added FlexibleSpaceWithImageGridView example. --- samples/AndroidManifest.xml | 10 + ...ctivity_flexiblespacewithimagegridview.xml | 79 ++++++++ samples/res/values/strings.xml | 1 + ...lexibleSpaceWithImageGridViewActivity.java | 182 ++++++++++++++++++ 4 files changed, 272 insertions(+) create mode 100644 samples/res/layout/activity_flexiblespacewithimagegridview.xml create mode 100644 samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewActivity.java diff --git a/samples/AndroidManifest.xml b/samples/AndroidManifest.xml index a6329303..fe642f43 100644 --- a/samples/AndroidManifest.xml +++ b/samples/AndroidManifest.xml @@ -187,6 +187,16 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/res/values/strings.xml b/samples/res/values/strings.xml index 5ae9b43e..bb7879a5 100644 --- a/samples/res/values/strings.xml +++ b/samples/res/values/strings.xml @@ -27,6 +27,7 @@ Flexible Space Flexible Space Flexible Space + Flexible Space Flexible Space Another implementation of Fill Gap & ListView Another implementation of Fill Gap & RecyclerView diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewActivity.java new file mode 100644 index 00000000..4b1a2987 --- /dev/null +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FlexibleSpaceWithImageGridViewActivity.java @@ -0,0 +1,182 @@ +/* + * Copyright 2014 Soichiro Kashima + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.ksoichiro.android.observablescrollview.samples; + +import android.annotation.TargetApi; +import android.content.res.Configuration; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.widget.AbsListView; +import android.widget.FrameLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.github.ksoichiro.android.observablescrollview.ObservableGridView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; +import com.github.ksoichiro.android.observablescrollview.ScrollUtils; +import com.nineoldandroids.view.ViewHelper; +import com.nineoldandroids.view.ViewPropertyAnimator; + +/** + * This example depends on {@code ObservableGridView#addHeaderView()} method introduced in v1.6.0. + */ +public class FlexibleSpaceWithImageGridViewActivity extends BaseActivity implements ObservableScrollViewCallbacks { + + private static final float MAX_TEXT_SCALE_DELTA = 0.3f; + + private View mImageView; + private View mOverlayView; + private View mListBackgroundView; + private TextView mTitleView; + private View mFab; + private int mActionBarSize; + private int mFlexibleSpaceShowFabOffset; + private int mFlexibleSpaceImageHeight; + private int mFabMargin; + private boolean mFabIsShown; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_flexiblespacewithimagegridview); + + mFlexibleSpaceImageHeight = getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + mFlexibleSpaceShowFabOffset = getResources().getDimensionPixelSize(R.dimen.flexible_space_show_fab_offset); + mActionBarSize = getActionBarSize(); + mImageView = findViewById(R.id.image); + mOverlayView = findViewById(R.id.overlay); + ObservableGridView gridView = (ObservableGridView) findViewById(R.id.list); + gridView.setScrollViewCallbacks(this); + + // Set padding view for ListView. This is the flexible space. + View paddingView = new View(this); + AbsListView.LayoutParams lp = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, + mFlexibleSpaceImageHeight); + paddingView.setLayoutParams(lp); + + // This is required to disable header's list selector effect + paddingView.setClickable(true); + + gridView.addHeaderView(paddingView); + setDummyData(gridView); + mTitleView = (TextView) findViewById(R.id.title); + mTitleView.setText(getTitle()); + setTitle(null); + mFab = findViewById(R.id.fab); + mFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(FlexibleSpaceWithImageGridViewActivity.this, "FAB is clicked", Toast.LENGTH_SHORT).show(); + } + }); + mFabMargin = getResources().getDimensionPixelSize(R.dimen.margin_standard); + ViewHelper.setScaleX(mFab, 0); + ViewHelper.setScaleY(mFab, 0); + + // mListBackgroundView makes ListView's background except header view. + mListBackgroundView = findViewById(R.id.list_background); + } + + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + // Translate overlay and image + float flexibleRange = mFlexibleSpaceImageHeight - mActionBarSize; + int minOverlayTransitionY = mActionBarSize - mOverlayView.getHeight(); + ViewHelper.setTranslationY(mOverlayView, ScrollUtils.getFloat(-scrollY, minOverlayTransitionY, 0)); + ViewHelper.setTranslationY(mImageView, ScrollUtils.getFloat(-scrollY / 2, minOverlayTransitionY, 0)); + + // Translate list background + ViewHelper.setTranslationY(mListBackgroundView, Math.max(0, -scrollY + mFlexibleSpaceImageHeight)); + + // Change alpha of overlay + ViewHelper.setAlpha(mOverlayView, ScrollUtils.getFloat((float) scrollY / flexibleRange, 0, 1)); + + // Scale title text + float scale = 1 + ScrollUtils.getFloat((flexibleRange - scrollY) / flexibleRange, 0, MAX_TEXT_SCALE_DELTA); + setPivotXToTitle(); + ViewHelper.setPivotY(mTitleView, 0); + ViewHelper.setScaleX(mTitleView, scale); + ViewHelper.setScaleY(mTitleView, scale); + + // Translate title text + int maxTitleTranslationY = (int) (mFlexibleSpaceImageHeight - mTitleView.getHeight() * scale); + int titleTranslationY = maxTitleTranslationY - scrollY; + ViewHelper.setTranslationY(mTitleView, titleTranslationY); + + // Translate FAB + int maxFabTranslationY = mFlexibleSpaceImageHeight - mFab.getHeight() / 2; + float fabTranslationY = ScrollUtils.getFloat( + -scrollY + mFlexibleSpaceImageHeight - mFab.getHeight() / 2, + mActionBarSize - mFab.getHeight() / 2, + maxFabTranslationY); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + // On pre-honeycomb, ViewHelper.setTranslationX/Y does not set margin, + // which causes FAB's OnClickListener not working. + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFab.getLayoutParams(); + lp.leftMargin = mOverlayView.getWidth() - mFabMargin - mFab.getWidth(); + lp.topMargin = (int) fabTranslationY; + mFab.requestLayout(); + } else { + ViewHelper.setTranslationX(mFab, mOverlayView.getWidth() - mFabMargin - mFab.getWidth()); + ViewHelper.setTranslationY(mFab, fabTranslationY); + } + + // Show/hide FAB + if (fabTranslationY < mFlexibleSpaceShowFabOffset) { + hideFab(); + } else { + showFab(); + } + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private void setPivotXToTitle() { + Configuration config = getResources().getConfiguration(); + if (Build.VERSION_CODES.JELLY_BEAN_MR1 <= Build.VERSION.SDK_INT + && config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + ViewHelper.setPivotX(mTitleView, findViewById(android.R.id.content).getWidth()); + } else { + ViewHelper.setPivotX(mTitleView, 0); + } + } + + private void showFab() { + if (!mFabIsShown) { + ViewPropertyAnimator.animate(mFab).cancel(); + ViewPropertyAnimator.animate(mFab).scaleX(1).scaleY(1).setDuration(200).start(); + mFabIsShown = true; + } + } + + private void hideFab() { + if (mFabIsShown) { + ViewPropertyAnimator.animate(mFab).cancel(); + ViewPropertyAnimator.animate(mFab).scaleX(0).scaleY(0).setDuration(200).start(); + mFabIsShown = false; + } + } +} From 1d9f68e375a25abcc4f80eb7bb601b1dc325d7c7 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 19 Jun 2015 22:31:16 +0900 Subject: [PATCH 163/208] Added tests. --- .../test/HeaderGridViewActivityTest.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java index 1c593c8d..21b1b525 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java @@ -6,10 +6,14 @@ import android.view.View; import android.widget.FrameLayout; import android.widget.ListAdapter; +import android.widget.SimpleAdapter; import com.github.ksoichiro.android.observablescrollview.ObservableGridView; import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; public class HeaderGridViewActivityTest extends ActivityInstrumentationTestCase2 { @@ -140,4 +144,50 @@ public void run() { } }); } + + public void testHeaderViewGridAdapter() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + try { + ObservableGridView.HeaderViewGridAdapter adapter = + new ObservableGridView.HeaderViewGridAdapter(null, null); + fail(); + } catch (IllegalArgumentException ignore) { + } + } + }); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + ArrayList list = new ArrayList<>(); + Map map = new LinkedHashMap<>(); + map.put("text", "A"); + List> data = new ArrayList<>(); + data.add(map); + ObservableGridView.HeaderViewGridAdapter adapter = + new ObservableGridView.HeaderViewGridAdapter( + list, + new SimpleAdapter( + activity, + data, + android.R.layout.simple_list_item_1, + new String[] {"text"}, + new int[]{android.R.id.text1})); + assertFalse(adapter.removeHeader(null)); + assertEquals(1, adapter.getCount()); + } + }); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + ArrayList list = new ArrayList<>(); + ObservableGridView.HeaderViewGridAdapter adapter = + new ObservableGridView.HeaderViewGridAdapter( + list, + null); + assertEquals(0, adapter.getCount()); + } + }); + } } From ad159b5c988f4829116024d05a1eae9afbc65717 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 20 Jun 2015 23:43:04 +0900 Subject: [PATCH 164/208] Added tests. --- .../test/HeaderGridViewActivityTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java index 21b1b525..988e9814 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java @@ -187,6 +187,11 @@ public void run() { list, null); assertEquals(0, adapter.getCount()); + try { + adapter.isEnabled(1); + fail(); + } catch (ArrayIndexOutOfBoundsException ignore) { + } } }); } From 3effb9c7962a411d7fb2c8323a37e5c92c68ffeb Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 21 Jun 2015 21:40:21 +0900 Subject: [PATCH 165/208] Added tests. --- .../test/HeaderGridViewActivityTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java index 988e9814..d8327b21 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java @@ -192,6 +192,11 @@ public void run() { fail(); } catch (ArrayIndexOutOfBoundsException ignore) { } + try { + adapter.getItem(1); + fail(); + } catch (ArrayIndexOutOfBoundsException ignore) { + } } }); } From 9f27f0d35d5a90ab315304d8a77eb2d63efca419 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 24 Jun 2015 23:30:00 +0900 Subject: [PATCH 166/208] Fixed warning. --- .../observablescrollview/samples/AboutActivity.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java index b4e1df1b..26af1c1f 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/AboutActivity.java @@ -27,6 +27,7 @@ import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; @@ -64,7 +65,7 @@ private void initLicenses() { String[] licenseList = getResources().getStringArray(R.array.license_list); content.addView(createItemsText(softwareList)); for (int i = 0; i < softwareList.length; i++) { - content.addView(createDivider(inflater)); + content.addView(createDivider(inflater, content)); content.addView(createHeader(softwareList[i])); content.addView(createHtmlText(licenseList[i])); } @@ -105,8 +106,8 @@ private TextView createHtmlText(final String s, final int margin) { return text; } - private View createDivider(final LayoutInflater inflater) { - return inflater.inflate(R.layout.divider, null); + private View createDivider(final LayoutInflater inflater, final ViewGroup parent) { + return inflater.inflate(R.layout.divider, parent, false); } private String getVersionName() { From 87f0487a1008fd49d2ebd660b31141b6e7d0e85e Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Thu, 25 Jun 2015 23:38:21 +0900 Subject: [PATCH 167/208] Fixed warning. --- .../observablescrollview/samples/FillGap3BaseActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3BaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3BaseActivity.java index 9f17c96e..bb1b69d2 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3BaseActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGap3BaseActivity.java @@ -17,6 +17,7 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v7.widget.Toolbar; import android.view.MotionEvent; import android.view.View; @@ -114,7 +115,7 @@ public void run() { } @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { + protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); mInitialTranslationY = savedInstanceState.getFloat(STATE_TRANSLATION_Y); } From d7133d62a59c9a5cf1ac8d4dd79c05ccf377db49 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 26 Jun 2015 00:15:26 +0900 Subject: [PATCH 168/208] Removed unused import. --- .../observablescrollview/samples/FillGapBaseActivity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java index 90d3bb29..1efcd807 100644 --- a/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java +++ b/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/FillGapBaseActivity.java @@ -17,7 +17,6 @@ package com.github.ksoichiro.android.observablescrollview.samples; import android.os.Bundle; -import android.support.v7.widget.Toolbar; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.FrameLayout; From db593e383c2ba90d75c009ca54cf6417c7fecedf Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Thu, 23 Jul 2015 00:57:43 +0900 Subject: [PATCH 169/208] Fixed NPE when mChildrenHeights.indexOfKey(i) < 0 in ObservableRecyclerView. --- .../observablescrollview/ObservableRecyclerView.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java index d81fa804..1f9517d3 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java @@ -103,8 +103,10 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { for (int i = firstVisiblePosition, j = 0; i <= lastVisiblePosition; i++, j++) { int childHeight = 0; View child = getChildAt(j); - if (mChildrenHeights.indexOfKey(i) < 0 || (child != null && child.getHeight() != mChildrenHeights.get(i))) { - childHeight = child.getHeight(); + if (child != null) { + if (mChildrenHeights.indexOfKey(i) < 0 || (child.getHeight() != mChildrenHeights.get(i))) { + childHeight = child.getHeight(); + } } mChildrenHeights.put(i, childHeight); } From 676de558a9a263d2246676735feed5ae1a10a6fd Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 28 Jul 2015 08:29:42 +0900 Subject: [PATCH 170/208] Fixed typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae81183e..5c4ec9ab 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ and [the documentation](docs/overview.md) for further more. * [Release notes](docs/reference/release-notes.md) * [FAQ](docs/faq.md) -## Apps that uses this library +## Apps that use this library * [Jair Player](https://play.google.com/store/apps/details?id=aj.jair.music) by Akshay Chordiya * [My Gradle](https://play.google.com/store/apps/details?id=se.project.generic.mygradle) by Erick Chavez Alcarraz From 7b7b34e0be34b7089159468af5849843907c054e Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 28 Jul 2015 08:30:17 +0900 Subject: [PATCH 171/208] Added an app to the "Apps that use this library" list. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5c4ec9ab..16942c72 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ and [the documentation](docs/overview.md) for further more. * [Jair Player](https://play.google.com/store/apps/details?id=aj.jair.music) by Akshay Chordiya * [My Gradle](https://play.google.com/store/apps/details?id=se.project.generic.mygradle) by Erick Chavez Alcarraz +* [ThemeDIY](https://play.google.com/store/apps/details?id=net.darkion.theme.maker) by Darkion Avey If you're using this library in your app and you'd like to list it here, please let me know via [email](mailto:soichiro.kashima@gmail.com) or [pull requests](https://github.com/ksoichiro/Android-ObservableScrollView/pulls) or [issues](https://github.com/ksoichiro/Android-ObservableScrollView/issues). From cfb3ae7dd438d20b13d3cb65300503e1f0a913aa Mon Sep 17 00:00:00 2001 From: Selim YILDIZ Date: Wed, 29 Jul 2015 15:36:45 +0300 Subject: [PATCH 172/208] Implement feature addFooterView ObservableGridView.java --- .../ObservableGridView.java | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index bf956d47..c5fc70f4 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -61,6 +61,7 @@ public class ObservableGridView extends GridView implements Scrollable { private MotionEvent mPrevMoveEvent; private ViewGroup mTouchInterceptionViewGroup; private ArrayList mHeaderViewInfos; + private ArrayList mFooterViewInfos; private OnScrollListener mOriginalScrollListener; private OnScrollListener mScrollListener = new OnScrollListener() { @@ -98,6 +99,9 @@ public ObservableGridView(Context context, AttributeSet attrs, int defStyle) { init(); } + + + @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; @@ -214,6 +218,40 @@ public void run() { return super.onTouchEvent(ev); } + + + public void addFooterView(View v) { + addFooterView(v, null, true); + } + + public void addFooterView(View v, Object data, boolean isSelectable) { + ListAdapter mAdapter = getAdapter(); + if (mAdapter != null && !(mAdapter instanceof HeaderViewGridAdapter)) { + throw new IllegalStateException( + "Cannot add header view to grid -- setAdapter has already been called."); + } + + ViewGroup.LayoutParams lyp = v.getLayoutParams(); + + FixedViewInfo info = new FixedViewInfo(); + FrameLayout fl = new FullWidthFixedViewLayout(getContext()); + + if (lyp != null) { + v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height)); + fl.setLayoutParams(new AbsListView.LayoutParams(lyp.width, lyp.height)); + } + fl.addView(v); + info.view = v; + info.viewContainer = fl; + info.data = data; + info.isSelectable = isSelectable; + mFooterViewInfos.add(info); + + if (mAdapter != null) { + ((HeaderViewGridAdapter) mAdapter).notifyDataSetChanged(); + } + } + @Override public void setOnScrollListener(OnScrollListener l) { // Don't set l to super.setOnScrollListener(). @@ -313,6 +351,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { private void init() { mChildrenHeights = new SparseIntArray(); mHeaderViewInfos = new ArrayList<>(); + mFooterViewInfos = new ArrayList<>(); super.setClipChildren(false); super.setOnScrollListener(mScrollListener); } @@ -713,4 +752,4 @@ private boolean areAllListInfosSelectable(ArrayList infos) { return true; } } -} +} \ No newline at end of file From bcfee364d38d6daea37d4be15ecb365bc259a30f Mon Sep 17 00:00:00 2001 From: Selim YILDIZ Date: Wed, 29 Jul 2015 17:53:43 +0300 Subject: [PATCH 173/208] Implement feature addFooterView in ObservableGridView 2 #183 --- .../ObservableGridView.java | 322 +++++++++++++----- 1 file changed, 241 insertions(+), 81 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index c5fc70f4..644a0807 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -252,6 +252,19 @@ public void addFooterView(View v, Object data, boolean isSelectable) { } } + public boolean removeFooterView(View v) { + if (mFooterViewInfos.size() > 0) { + boolean result = false; + ListAdapter adapter = getAdapter(); + if (adapter != null && ((HeaderViewGridAdapter) adapter).removeFooter(v)) { + result = true; + } + removeFixedViewInfo(v, mFooterViewInfos); + return result; + } + return false; + } + @Override public void setOnScrollListener(OnScrollListener l) { // Don't set l to super.setOnScrollListener(). @@ -287,7 +300,7 @@ public void setClipChildren(boolean clipChildren) { @Override public void setAdapter(ListAdapter adapter) { if (0 < mHeaderViewInfos.size()) { - HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos, adapter); + HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos,mFooterViewInfos,adapter); int numColumns = getNumColumnsCompat(); if (1 < numColumns) { headerViewGridAdapter.setNumColumns(numColumns); @@ -541,29 +554,41 @@ public static class FixedViewInfo { public static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable { private final DataSetObservable mDataSetObservable = new DataSetObservable(); private final ListAdapter mAdapter; - private int mNumColumns = 1; + static final ArrayList EMPTY_INFO_LIST = + new ArrayList(); + // This ArrayList is assumed to NOT be null. ArrayList mHeaderViewInfos; + ArrayList mFooterViewInfos; + private int mNumColumns = 1; + private int mRowHeight = -1; boolean mAreAllFixedViewsSelectable; private final boolean mIsFilterable; + private boolean mCachePlaceHoldView = true; + // From Recycle Bin or calling getView, this a question... + private boolean mCacheFirstHeaderView = false; - public HeaderViewGridAdapter(ArrayList headerViewInfos, ListAdapter adapter) { + public HeaderViewGridAdapter(ArrayList headerViewInfos, ArrayList footViewInfos, ListAdapter adapter) { mAdapter = adapter; - mIsFilterable = adapter != null && adapter instanceof Filterable; + mIsFilterable = adapter instanceof Filterable; if (headerViewInfos == null) { - throw new IllegalArgumentException("headerViewInfos cannot be null"); + mHeaderViewInfos = EMPTY_INFO_LIST; + } else { + mHeaderViewInfos = headerViewInfos; } - mHeaderViewInfos = headerViewInfos; - mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos); - } - public int getHeadersCount() { - return mHeaderViewInfos.size(); + if (footViewInfos == null) { + mFooterViewInfos = EMPTY_INFO_LIST; + } else { + mFooterViewInfos = footViewInfos; + } + mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos) + && areAllListInfosSelectable(mFooterViewInfos); } public void setNumColumns(int numColumns) { if (numColumns < 1) { - throw new IllegalArgumentException("Number of columns must be 1 or more"); + return; } if (mNumColumns != numColumns) { mNumColumns = numColumns; @@ -571,12 +596,61 @@ public void setNumColumns(int numColumns) { } } + public void setRowHeight(int height) { + mRowHeight = height; + } + + public int getHeadersCount() { + return mHeaderViewInfos.size(); + } + + public int getFootersCount() { + return mFooterViewInfos.size(); + } + + /** + * @return true if this adapter doesn't contain any data. This is used to determine + * whether the empty view should be displayed. A typical implementation will return + * getCount() == 0 but since getCount() includes the headers and footers, specialized + * adapters might want a different behavior. + */ + @Override + public boolean isEmpty() { + return (mAdapter == null || mAdapter.isEmpty()); + } + + private boolean areAllListInfosSelectable(ArrayList infos) { + if (infos != null) { + for (FixedViewInfo info : infos) { + if (!info.isSelectable) { + return false; + } + } + } + return true; + } + public boolean removeHeader(View v) { for (int i = 0; i < mHeaderViewInfos.size(); i++) { FixedViewInfo info = mHeaderViewInfos.get(i); if (info.view == v) { mHeaderViewInfos.remove(i); - mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos); + mAreAllFixedViewsSelectable = + areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos); + mDataSetObservable.notifyChanged(); + return true; + } + } + return false; + } + + public boolean removeFooter(View v) { + for (int i = 0; i < mFooterViewInfos.size(); i++) { + FixedViewInfo info = mFooterViewInfos.get(i); + if (info.view == v) { + mFooterViewInfos.remove(i); + mAreAllFixedViewsSelectable = + areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos); mDataSetObservable.notifyChanged(); return true; } @@ -585,8 +659,12 @@ public boolean removeHeader(View v) { } @Override - public ListAdapter getWrappedAdapter() { - return mAdapter; + public int getCount() { + if (mAdapter != null) { + return (getFootersCount() + getHeadersCount()) * mNumColumns + getAdapterAndPlaceHolderCount(); + } else { + return (getFootersCount() + getHeadersCount()) * mNumColumns; + } } @Override @@ -594,47 +672,33 @@ public boolean areAllItemsEnabled() { return mAdapter == null || mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled(); } + private int getAdapterAndPlaceHolderCount() { + return (int) (Math.ceil(1f * mAdapter.getCount() / mNumColumns) * mNumColumns); + } + @Override public boolean isEnabled(int position) { - // Header (negative positions will throw an ArrayIndexOutOfBoundsException) + // Header (negative positions will throw an IndexOutOfBoundsException) int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; if (position < numHeadersAndPlaceholders) { - return (position % mNumColumns == 0) + return position % mNumColumns == 0 && mHeaderViewInfos.get(position / mNumColumns).isSelectable; } + // Adapter + final int adjPosition = position - numHeadersAndPlaceholders; + int adapterCount = 0; if (mAdapter != null) { - final int adjPosition = position - numHeadersAndPlaceholders; - if (adjPosition < mAdapter.getCount()) { - return mAdapter.isEnabled(adjPosition); + adapterCount = getAdapterAndPlaceHolderCount(); + if (adjPosition < adapterCount) { + return adjPosition < mAdapter.getCount() && mAdapter.isEnabled(adjPosition); } } - throw new ArrayIndexOutOfBoundsException(position); - } - - @Override - public void registerDataSetObserver(DataSetObserver observer) { - mDataSetObservable.registerObserver(observer); - if (mAdapter != null) { - mAdapter.registerDataSetObserver(observer); - } - } - - @Override - public void unregisterDataSetObserver(DataSetObserver observer) { - mDataSetObservable.unregisterObserver(observer); - if (mAdapter != null) { - mAdapter.unregisterDataSetObserver(observer); - } - } - @Override - public int getCount() { - if (mAdapter != null) { - return getHeadersCount() * mNumColumns + mAdapter.getCount(); - } else { - return getHeadersCount() * mNumColumns; - } + // Footer (off-limits positions will throw an IndexOutOfBoundsException) + final int footerPosition = adjPosition - adapterCount; + return footerPosition % mNumColumns == 0 + && mFooterViewInfos.get(footerPosition / mNumColumns).isSelectable; } @Override @@ -647,22 +711,37 @@ public Object getItem(int position) { } return null; } + // Adapter + final int adjPosition = position - numHeadersAndPlaceholders; + int adapterCount = 0; if (mAdapter != null) { - final int adjPosition = position - numHeadersAndPlaceholders; - if (adjPosition < mAdapter.getCount()) { - return mAdapter.getItem(adjPosition); + adapterCount = getAdapterAndPlaceHolderCount(); + if (adjPosition < adapterCount) { + if (adjPosition < mAdapter.getCount()) { + return mAdapter.getItem(adjPosition); + } else { + return null; + } } } - throw new ArrayIndexOutOfBoundsException(position); + + // Footer (off-limits positions will throw an IndexOutOfBoundsException) + final int footerPosition = adjPosition - adapterCount; + if (footerPosition % mNumColumns == 0) { + return mFooterViewInfos.get(footerPosition).data; + } else { + return null; + } } @Override public long getItemId(int position) { int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - if (mAdapter != null && numHeadersAndPlaceholders <= position) { + if (mAdapter != null && position >= numHeadersAndPlaceholders) { int adjPosition = position - numHeadersAndPlaceholders; - if (adjPosition < mAdapter.getCount()) { + int adapterCount = mAdapter.getCount(); + if (adjPosition < adapterCount) { return mAdapter.getItemId(adjPosition); } } @@ -676,13 +755,12 @@ public boolean hasStableIds() { @Override public View getView(int position, View convertView, ViewGroup parent) { - if (parent == null) { - throw new IllegalArgumentException("Parent cannot be null"); - } + // Header (negative positions will throw an ArrayIndexOutOfBoundsException) int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; if (position < numHeadersAndPlaceholders) { - View headerViewContainer = mHeaderViewInfos.get(position / mNumColumns).viewContainer; + View headerViewContainer = mHeaderViewInfos + .get(position / mNumColumns).viewContainer; if (position % mNumColumns == 0) { return headerViewContainer; } else { @@ -697,10 +775,39 @@ public View getView(int position, View convertView, ViewGroup parent) { } } // Adapter + final int adjPosition = position - numHeadersAndPlaceholders; + int adapterCount = 0; if (mAdapter != null) { - final int adjPosition = position - numHeadersAndPlaceholders; - if (adjPosition < mAdapter.getCount()) { - return mAdapter.getView(adjPosition, convertView, parent); + adapterCount = getAdapterAndPlaceHolderCount(); + if (adjPosition < adapterCount) { + if (adjPosition < mAdapter.getCount()) { + return mAdapter.getView(adjPosition, convertView, parent); + } else { + if (convertView == null) { + convertView = new View(parent.getContext()); + } + convertView.setVisibility(View.INVISIBLE); + convertView.setMinimumHeight(mRowHeight); + return convertView; + } + } + } + // Footer + final int footerPosition = adjPosition - adapterCount; + if (footerPosition < getCount()) { + View footViewContainer = mFooterViewInfos + .get(footerPosition / mNumColumns).viewContainer; + if (position % mNumColumns == 0) { + return footViewContainer; + } else { + if (convertView == null) { + convertView = new View(parent.getContext()); + } + // We need to do this because GridView uses the height of the last item + // in a row to determine the height for the entire row. + convertView.setVisibility(View.INVISIBLE); + convertView.setMinimumHeight(footViewContainer.getHeight()); + return convertView; } } throw new ArrayIndexOutOfBoundsException(position); @@ -708,48 +815,101 @@ public View getView(int position, View convertView, ViewGroup parent) { @Override public int getItemViewType(int position) { - int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; - if (position < numHeadersAndPlaceholders && (position % mNumColumns != 0)) { - // Placeholders get the last view type number - return mAdapter != null ? mAdapter.getViewTypeCount() : 1; + + final int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; + final int adapterViewTypeStart = mAdapter == null ? 0 : mAdapter.getViewTypeCount() - 1; + int type = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER; + if (mCachePlaceHoldView) { + // Header + if (position < numHeadersAndPlaceholders) { + if (position == 0) { + if (mCacheFirstHeaderView) { + type = adapterViewTypeStart + mHeaderViewInfos.size() + mFooterViewInfos.size() + 1 + 1; + } + } + if (position % mNumColumns != 0) { + type = adapterViewTypeStart + (position / mNumColumns + 1); + } + } } - if (mAdapter != null && position >= numHeadersAndPlaceholders) { - int adjPosition = position - numHeadersAndPlaceholders; - if (adjPosition < mAdapter.getCount()) { - return mAdapter.getItemViewType(adjPosition); + + // Adapter + final int adjPosition = position - numHeadersAndPlaceholders; + int adapterCount = 0; + if (mAdapter != null) { + adapterCount = getAdapterAndPlaceHolderCount(); + if (adjPosition >= 0 && adjPosition < adapterCount) { + if (adjPosition < mAdapter.getCount()) { + type = mAdapter.getItemViewType(adjPosition); + } else { + if (mCachePlaceHoldView) { + type = adapterViewTypeStart + mHeaderViewInfos.size() + 1; + } + } } } - return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER; + + if (mCachePlaceHoldView) { + // Footer + final int footerPosition = adjPosition - adapterCount; + if (footerPosition >= 0 && footerPosition < getCount() && (footerPosition % mNumColumns) != 0) { + type = adapterViewTypeStart + mHeaderViewInfos.size() + 1 + (footerPosition / mNumColumns + 1); + } + } + + return type; } + /** + * content view, content view holder, header[0], header and footer placeholder(s) + * + * @return + */ @Override public int getViewTypeCount() { - return mAdapter == null ? 2 : (mAdapter.getViewTypeCount() + 1); + int count = mAdapter == null ? 1 : mAdapter.getViewTypeCount(); + if (mCachePlaceHoldView) { + int offset = mHeaderViewInfos.size() + 1 + mFooterViewInfos.size(); + if (mCacheFirstHeaderView) { + offset += 1; + } + count += offset; + } + + return count; } @Override - public boolean isEmpty() { - return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0; + public void registerDataSetObserver(DataSetObserver observer) { + mDataSetObservable.registerObserver(observer); + if (mAdapter != null) { + mAdapter.registerDataSetObserver(observer); + } + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + mDataSetObservable.unregisterObserver(observer); + if (mAdapter != null) { + mAdapter.unregisterDataSetObserver(observer); + } } @Override public Filter getFilter() { - return mIsFilterable ? ((Filterable) mAdapter).getFilter() : null; + if (mIsFilterable) { + return ((Filterable) mAdapter).getFilter(); + } + return null; } - public void notifyDataSetChanged() { - mDataSetObservable.notifyChanged(); + @Override + public ListAdapter getWrappedAdapter() { + return mAdapter; } - private boolean areAllListInfosSelectable(ArrayList infos) { - if (infos != null) { - for (FixedViewInfo info : infos) { - if (!info.isSelectable) { - return false; - } - } - } - return true; + public void notifyDataSetChanged() { + mDataSetObservable.notifyChanged(); } } } \ No newline at end of file From a10a67f18f8c9f8579d425562a83a796191da23d Mon Sep 17 00:00:00 2001 From: Vinicius Cubas Brand Date: Wed, 29 Jul 2015 18:45:49 -0300 Subject: [PATCH 174/208] Swiping bug correction Correction of a bug that was happening almost every time there was being made a swipe too fast to alternate between views of a ViewPager. The view that was happening this issue in my project uses ObservableListView in Fragments inside a ViewPager. --- .../android/observablescrollview/ObservableListView.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java index 1d20c578..ce054fbb 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java @@ -165,9 +165,14 @@ public boolean onTouchEvent(MotionEvent ev) { // we should aggregate offsets from all of the parents. float offsetX = 0; float offsetY = 0; - for (View v = this; v != null && v != parent; v = (View) v.getParent()) { + for (View v = this; v != null && v != parent; ) { offsetX += v.getLeft() - v.getScrollX(); offsetY += v.getTop() - v.getScrollY(); + try { + v = (View) v.getParent(); + } catch (ClassCastException ex) { + break; + } } final MotionEvent event = MotionEvent.obtainNoHistory(ev); event.offsetLocation(offsetX, offsetY); From 177d642432bba3a1a5255b6e31aae001369f7881 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 31 Jul 2015 00:32:21 +0900 Subject: [PATCH 175/208] Fixed tests which were affected by #183. --- .../test/HeaderGridViewActivityTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java index d8327b21..352bcb12 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java @@ -104,7 +104,7 @@ public void testHeaderViewGridExceptions() throws Throwable { @Override public void run() { try { - new ObservableGridView.HeaderViewGridAdapter(null, null); + new ObservableGridView.HeaderViewGridAdapter(null, null, null); fail(); } catch (IllegalArgumentException e) { assertEquals("headerViewInfos cannot be null", e.getMessage()); @@ -118,7 +118,7 @@ public void run() { assertEquals("Number of columns must be 1 or more", e.getMessage()); } ArrayList headerViewInfos = new ArrayList<>(); - ObservableGridView.HeaderViewGridAdapter adapter1 = new ObservableGridView.HeaderViewGridAdapter(headerViewInfos, null); + ObservableGridView.HeaderViewGridAdapter adapter1 = new ObservableGridView.HeaderViewGridAdapter(headerViewInfos, null, null); assertTrue(adapter1.isEmpty()); try { adapter1.isEnabled(-1); @@ -151,7 +151,7 @@ public void testHeaderViewGridAdapter() throws Throwable { public void run() { try { ObservableGridView.HeaderViewGridAdapter adapter = - new ObservableGridView.HeaderViewGridAdapter(null, null); + new ObservableGridView.HeaderViewGridAdapter(null, null, null); fail(); } catch (IllegalArgumentException ignore) { } @@ -168,11 +168,12 @@ public void run() { ObservableGridView.HeaderViewGridAdapter adapter = new ObservableGridView.HeaderViewGridAdapter( list, + null, new SimpleAdapter( activity, data, android.R.layout.simple_list_item_1, - new String[] {"text"}, + new String[]{"text"}, new int[]{android.R.id.text1})); assertFalse(adapter.removeHeader(null)); assertEquals(1, adapter.getCount()); @@ -185,6 +186,7 @@ public void run() { ObservableGridView.HeaderViewGridAdapter adapter = new ObservableGridView.HeaderViewGridAdapter( list, + null, null); assertEquals(0, adapter.getCount()); try { From 9da2dea142e3e70310d47cec7184b04f9837f1eb Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 31 Jul 2015 01:11:04 +0900 Subject: [PATCH 176/208] Fixed tests which were affected by #183. --- .../test/HeaderGridViewActivityTest.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java index 352bcb12..fae62afe 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java @@ -105,17 +105,15 @@ public void testHeaderViewGridExceptions() throws Throwable { public void run() { try { new ObservableGridView.HeaderViewGridAdapter(null, null, null); - fail(); } catch (IllegalArgumentException e) { - assertEquals("headerViewInfos cannot be null", e.getMessage()); + fail(); } ListAdapter adapter = scrollable.getAdapter(); ObservableGridView.HeaderViewGridAdapter hvgAdapter = (ObservableGridView.HeaderViewGridAdapter) adapter; try { hvgAdapter.setNumColumns(0); - fail(); } catch (IllegalArgumentException e) { - assertEquals("Number of columns must be 1 or more", e.getMessage()); + fail(); } ArrayList headerViewInfos = new ArrayList<>(); ObservableGridView.HeaderViewGridAdapter adapter1 = new ObservableGridView.HeaderViewGridAdapter(headerViewInfos, null, null); @@ -133,8 +131,7 @@ public void run() { try { adapter1.getView(0, null, null); fail(); - } catch (IllegalArgumentException e) { - assertEquals("Parent cannot be null", e.getMessage()); + } catch (ArrayIndexOutOfBoundsException ignore) { } try { adapter1.getView(-1, null, scrollable); @@ -150,10 +147,9 @@ public void testHeaderViewGridAdapter() throws Throwable { @Override public void run() { try { - ObservableGridView.HeaderViewGridAdapter adapter = - new ObservableGridView.HeaderViewGridAdapter(null, null, null); - fail(); + new ObservableGridView.HeaderViewGridAdapter(null, null, null); } catch (IllegalArgumentException ignore) { + fail(); } } }); @@ -192,12 +188,12 @@ public void run() { try { adapter.isEnabled(1); fail(); - } catch (ArrayIndexOutOfBoundsException ignore) { + } catch (IndexOutOfBoundsException ignore) { } try { adapter.getItem(1); fail(); - } catch (ArrayIndexOutOfBoundsException ignore) { + } catch (IndexOutOfBoundsException ignore) { } } }); From e3a51b0375333d5e1428e78b69f6456cb33db141 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Thu, 6 Aug 2015 00:53:07 +0900 Subject: [PATCH 177/208] Formatted codes. --- .../test/HeaderGridViewActivityTest.java | 28 +++++++++---------- .../ObservableGridView.java | 7 +---- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java index fae62afe..5e668a6c 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java @@ -88,7 +88,7 @@ public void run() { activity.headerView = new View(activity); final int flexibleSpaceImageHeight = activity.getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, - flexibleSpaceImageHeight); + flexibleSpaceImageHeight); activity.headerView.setLayoutParams(lp); // This is required to disable header's list selector effect @@ -162,15 +162,15 @@ public void run() { List> data = new ArrayList<>(); data.add(map); ObservableGridView.HeaderViewGridAdapter adapter = - new ObservableGridView.HeaderViewGridAdapter( - list, - null, - new SimpleAdapter( - activity, - data, - android.R.layout.simple_list_item_1, - new String[]{"text"}, - new int[]{android.R.id.text1})); + new ObservableGridView.HeaderViewGridAdapter( + list, + null, + new SimpleAdapter( + activity, + data, + android.R.layout.simple_list_item_1, + new String[]{"text"}, + new int[]{android.R.id.text1})); assertFalse(adapter.removeHeader(null)); assertEquals(1, adapter.getCount()); } @@ -180,10 +180,10 @@ public void run() { public void run() { ArrayList list = new ArrayList<>(); ObservableGridView.HeaderViewGridAdapter adapter = - new ObservableGridView.HeaderViewGridAdapter( - list, - null, - null); + new ObservableGridView.HeaderViewGridAdapter( + list, + null, + null); assertEquals(0, adapter.getCount()); try { adapter.isEnabled(1); diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index 644a0807..fa0648bb 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -99,9 +99,6 @@ public ObservableGridView(Context context, AttributeSet attrs, int defStyle) { init(); } - - - @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; @@ -218,8 +215,6 @@ public void run() { return super.onTouchEvent(ev); } - - public void addFooterView(View v) { addFooterView(v, null, true); } @@ -300,7 +295,7 @@ public void setClipChildren(boolean clipChildren) { @Override public void setAdapter(ListAdapter adapter) { if (0 < mHeaderViewInfos.size()) { - HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos,mFooterViewInfos,adapter); + HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos, mFooterViewInfos, adapter); int numColumns = getNumColumnsCompat(); if (1 < numColumns) { headerViewGridAdapter.setNumColumns(numColumns); From db28330cb2a5777d3da907628825690730375105 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Fri, 7 Aug 2015 00:57:15 +0900 Subject: [PATCH 178/208] Added EditorConfig config. --- .editorconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..3269cbdf --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[{.travis.yml,.travis-script.sh,*.json,*.coffee,*.less}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false From 2297d3cad6e17d26adb5ba7010eb775e3ba0b27c Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 13 Sep 2015 00:41:54 +0900 Subject: [PATCH 179/208] Bumped version to 1.6.0. Sorry for having kept you waited so long. --- docs/reference/release-notes.md | 6 ++++++ gradle.properties | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/reference/release-notes.md b/docs/reference/release-notes.md index ef771571..7012d8b9 100644 --- a/docs/reference/release-notes.md +++ b/docs/reference/release-notes.md @@ -1,5 +1,11 @@ # Release notes +* v1.6.0 + * Added header view feature to `ObservableGridView` (#148). + * Added footer view feature to `ObservableGridView` (#183). + * Updated `recyclerview-v7` library version to 22.2.0. + * Fixed ViewPager swiping bug in `ObservableListView` (#185). + * Fixed NPE in `ObservableRecyclerView` (#149). * v1.5.2 * Fix `ObservableGridView` to use first child of line in height calculation. * v1.5.1 diff --git a/gradle.properties b/gradle.properties index 009f8292..00af7e5e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.6.0-SNAPSHOT +VERSION_NAME=1.6.0 SYNCED_VERSION_NAME=1.5.2 GROUP=com.github.ksoichiro From b24a12eeeee1adaa54eb7002bb00ce4e87c1103d Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 14 Sep 2015 02:30:52 +0900 Subject: [PATCH 180/208] Updated Android Gradle plugin and Gradle. --- gradle/wrapper/gradle-wrapper.properties | 2 +- library/build.gradle | 2 +- samples/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0c71e760..e7faee01 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip diff --git a/library/build.gradle b/library/build.gradle index 0a49c2dc..8f6b7348 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -4,7 +4,7 @@ buildscript { } dependencies { classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.1.0' - classpath 'com.android.tools.build:gradle:1.2.2' + classpath 'com.android.tools.build:gradle:1.3.0' } } diff --git a/samples/build.gradle b/samples/build.gradle index 3aefb1e7..d2514542 100644 --- a/samples/build.gradle +++ b/samples/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.2.2' + classpath 'com.android.tools.build:gradle:1.3.0' } } From d6c8a3ead51e65c7b719f4485040964e41b58f9f Mon Sep 17 00:00:00 2001 From: numa08 Date: Fri, 9 Oct 2015 08:21:55 +0900 Subject: [PATCH 181/208] To be able add/remove scrollcallback listeners --- .../ObservableGridView.java | 74 +++++++++++++++++-- .../ObservableListView.java | 74 +++++++++++++++++-- .../ObservableRecyclerView.java | 74 +++++++++++++++++-- .../ObservableScrollView.java | 74 +++++++++++++++++-- .../ObservableWebView.java | 26 +++++++ .../observablescrollview/Scrollable.java | 21 ++++++ 6 files changed, 318 insertions(+), 25 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index fa0648bb..6ea41954 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -38,6 +38,7 @@ import android.widget.WrapperListAdapter; import java.util.ArrayList; +import java.util.List; /** * GridView that its scroll position can be observed. @@ -54,6 +55,7 @@ public class ObservableGridView extends GridView implements Scrollable { // Fields that don't need to be saved onSaveInstanceState private ObservableScrollViewCallbacks mCallbacks; + private List mCallbackCollection; private ScrollState mScrollState; private boolean mFirstScroll; private boolean mDragging; @@ -126,7 +128,7 @@ public Parcelable onSaveInstanceState() { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Whether or not motion events are consumed by children, @@ -136,7 +138,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { // Also, applications might implement initialization codes to onDownMotionEvent, // so call it here. mFirstScroll = mDragging = true; - mCallbacks.onDownMotionEvent(); + dispatchOnDownMotionEvent(); break; } } @@ -145,13 +147,13 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mIntercepted = false; mDragging = false; - mCallbacks.onUpOrCancelMotionEvent(mScrollState); + dispatchOnUpOrCancelMotionEvent(mScrollState); break; case MotionEvent.ACTION_MOVE: if (mPrevMoveEvent == null) { @@ -272,6 +274,28 @@ public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) { mCallbacks = listener; } + @Override + public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) { + if (mCallbackCollection == null) { + mCallbackCollection = new ArrayList<>(); + } + mCallbackCollection.add(listener); + } + + @Override + public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) { + if (mCallbackCollection != null) { + mCallbackCollection.remove(listener); + } + } + + @Override + public void clearScrollViewCallbacks() { + if (mCallbackCollection != null) { + mCallbackCollection.clear(); + } + } + @Override public void setTouchInterceptionViewGroup(ViewGroup viewGroup) { mTouchInterceptionViewGroup = viewGroup; @@ -380,7 +404,7 @@ private int getNumColumnsCompat() { } private void onScrollChanged() { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { if (getChildCount() > 0) { int firstVisiblePosition = getFirstVisiblePosition(); for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) { @@ -426,7 +450,7 @@ private void onScrollChanged() { mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop(); mPrevFirstVisiblePosition = firstVisiblePosition; - mCallbacks.onScrollChanged(mScrollY, mFirstScroll, mDragging); + dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); if (mFirstScroll) { mFirstScroll = false; } @@ -539,6 +563,42 @@ public SavedState[] newArray(int size) { }; } + private void dispatchOnDownMotionEvent() { + if (mCallbacks != null) { + mCallbacks.onDownMotionEvent(); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onDownMotionEvent(); + } + } + } + + private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (mCallbacks != null) { + mCallbacks.onScrollChanged(scrollY, firstScroll, dragging); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onScrollChanged(scrollY, firstScroll, dragging); + } + } + } + + private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { + if (mCallbacks != null) { + mCallbacks.onUpOrCancelMotionEvent(scrollState); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onUpOrCancelMotionEvent(scrollState); + } + } + } + public static class FixedViewInfo { public View view; public ViewGroup viewContainer; @@ -907,4 +967,4 @@ public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); } } -} \ No newline at end of file +} diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java index ce054fbb..37220c1e 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java @@ -27,6 +27,9 @@ import android.widget.AbsListView; import android.widget.ListView; +import java.util.ArrayList; +import java.util.List; + /** * ListView that its scroll position can be observed. */ @@ -42,6 +45,7 @@ public class ObservableListView extends ListView implements Scrollable { // Fields that don't need to be saved onSaveInstanceState private ObservableScrollViewCallbacks mCallbacks; + private List mCallbackCollection; private ScrollState mScrollState; private boolean mFirstScroll; private boolean mDragging; @@ -112,7 +116,7 @@ public Parcelable onSaveInstanceState() { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Whether or not motion events are consumed by children, @@ -122,7 +126,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { // Also, applications might implement initialization codes to onDownMotionEvent, // so call it here. mFirstScroll = mDragging = true; - mCallbacks.onDownMotionEvent(); + dispatchOnDownMotionEvent(); break; } } @@ -131,13 +135,13 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mIntercepted = false; mDragging = false; - mCallbacks.onUpOrCancelMotionEvent(mScrollState); + dispatchOnUpOrCancelMotionEvent(mScrollState); break; case MotionEvent.ACTION_MOVE: if (mPrevMoveEvent == null) { @@ -218,6 +222,28 @@ public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) { mCallbacks = listener; } + @Override + public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) { + if (mCallbackCollection == null) { + mCallbackCollection = new ArrayList<>(); + } + mCallbackCollection.add(listener); + } + + @Override + public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) { + if (mCallbackCollection != null) { + mCallbackCollection.remove(listener); + } + } + + @Override + public void clearScrollViewCallbacks() { + if (mCallbackCollection != null) { + mCallbackCollection.clear(); + } + } + @Override public void setTouchInterceptionViewGroup(ViewGroup viewGroup) { mTouchInterceptionViewGroup = viewGroup; @@ -244,7 +270,7 @@ private void init() { } private void onScrollChanged() { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { if (getChildCount() > 0) { int firstVisiblePosition = getFirstVisiblePosition(); for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) { @@ -298,7 +324,7 @@ private void onScrollChanged() { mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop(); mPrevFirstVisiblePosition = firstVisiblePosition; - mCallbacks.onScrollChanged(mScrollY, mFirstScroll, mDragging); + dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); if (mFirstScroll) { mFirstScroll = false; } @@ -316,6 +342,42 @@ private void onScrollChanged() { } } + private void dispatchOnDownMotionEvent() { + if (mCallbacks != null) { + mCallbacks.onDownMotionEvent(); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onDownMotionEvent(); + } + } + } + + private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (mCallbacks != null) { + mCallbacks.onScrollChanged(scrollY, firstScroll, dragging); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onScrollChanged(scrollY, firstScroll, dragging); + } + } + } + + private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { + if (mCallbacks != null) { + mCallbacks.onUpOrCancelMotionEvent(scrollState); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onUpOrCancelMotionEvent(scrollState); + } + } + } + static class SavedState extends BaseSavedState { int prevFirstVisiblePosition; int prevFirstVisibleChildHeight = -1; diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java index 1f9517d3..0e5a5411 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java @@ -27,6 +27,9 @@ import android.view.View; import android.view.ViewGroup; +import java.util.ArrayList; +import java.util.List; + /** * RecyclerView that its scroll position can be observed. * Before using this, please consider to use the RecyclerView.OnScrollListener @@ -46,6 +49,7 @@ public class ObservableRecyclerView extends RecyclerView implements Scrollable { // Fields that don't need to be saved onSaveInstanceState private ObservableScrollViewCallbacks mCallbacks; + private List mCallbackCollection; private ScrollState mScrollState; private boolean mFirstScroll; private boolean mDragging; @@ -96,7 +100,7 @@ public Parcelable onSaveInstanceState() { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { if (getChildCount() > 0) { int firstVisiblePosition = getChildAdapterPosition(getChildAt(0)); int lastVisiblePosition = getChildAdapterPosition(getChildAt(getChildCount() - 1)); @@ -157,7 +161,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop(); mPrevFirstVisiblePosition = firstVisiblePosition; - mCallbacks.onScrollChanged(mScrollY, mFirstScroll, mDragging); + dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); if (mFirstScroll) { mFirstScroll = false; } @@ -179,7 +183,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Whether or not motion events are consumed by children, @@ -189,7 +193,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { // Also, applications might implement initialization codes to onDownMotionEvent, // so call it here. mFirstScroll = mDragging = true; - mCallbacks.onDownMotionEvent(); + dispatchOnDownMotionEvent(); break; } } @@ -198,13 +202,13 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mIntercepted = false; mDragging = false; - mCallbacks.onUpOrCancelMotionEvent(mScrollState); + dispatchOnUpOrCancelMotionEvent(mScrollState); break; case MotionEvent.ACTION_MOVE: if (mPrevMoveEvent == null) { @@ -273,6 +277,28 @@ public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) { mCallbacks = listener; } + @Override + public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) { + if (mCallbackCollection == null) { + mCallbackCollection = new ArrayList<>(); + } + mCallbackCollection.add(listener); + } + + @Override + public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) { + if (mCallbackCollection != null) { + mCallbackCollection.remove(listener); + } + } + + @Override + public void clearScrollViewCallbacks() { + if (mCallbackCollection != null) { + mCallbackCollection.clear(); + } + } + @Override public void setTouchInterceptionViewGroup(ViewGroup viewGroup) { mTouchInterceptionViewGroup = viewGroup; @@ -337,6 +363,42 @@ private void checkLibraryVersion() { } } + private void dispatchOnDownMotionEvent() { + if (mCallbacks != null) { + mCallbacks.onDownMotionEvent(); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onDownMotionEvent(); + } + } + } + + private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (mCallbacks != null) { + mCallbacks.onScrollChanged(scrollY, firstScroll, dragging); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onScrollChanged(scrollY, firstScroll, dragging); + } + } + } + + private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { + if (mCallbacks != null) { + mCallbacks.onUpOrCancelMotionEvent(scrollState); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onUpOrCancelMotionEvent(scrollState); + } + } + } + /** * This saved state class is a Parcelable and should not extend * {@link android.view.View.BaseSavedState} nor {@link android.view.AbsSavedState} diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java index d38c0e2d..b69d548c 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java @@ -25,6 +25,9 @@ import android.view.ViewGroup; import android.widget.ScrollView; +import java.util.ArrayList; +import java.util.List; + /** * ScrollView that its scroll position can be observed. */ @@ -36,6 +39,7 @@ public class ObservableScrollView extends ScrollView implements Scrollable { // Fields that don't need to be saved onSaveInstanceState private ObservableScrollViewCallbacks mCallbacks; + private List mCallbackCollection; private ScrollState mScrollState; private boolean mFirstScroll; private boolean mDragging; @@ -75,10 +79,10 @@ public Parcelable onSaveInstanceState() { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { mScrollY = t; - mCallbacks.onScrollChanged(t, mFirstScroll, mDragging); + dispatchOnScrollChanged(t, mFirstScroll, mDragging); if (mFirstScroll) { mFirstScroll = false; } @@ -100,7 +104,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Whether or not motion events are consumed by children, @@ -110,7 +114,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { // Also, applications might implement initialization codes to onDownMotionEvent, // so call it here. mFirstScroll = mDragging = true; - mCallbacks.onDownMotionEvent(); + dispatchOnDownMotionEvent(); break; } } @@ -119,13 +123,13 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mIntercepted = false; mDragging = false; - mCallbacks.onUpOrCancelMotionEvent(mScrollState); + dispatchOnUpOrCancelMotionEvent(mScrollState); break; case MotionEvent.ACTION_MOVE: if (mPrevMoveEvent == null) { @@ -194,6 +198,28 @@ public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) { mCallbacks = listener; } + @Override + public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) { + if (mCallbackCollection == null) { + mCallbackCollection = new ArrayList<>(); + } + mCallbackCollection.add(listener); + } + + @Override + public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) { + if (mCallbackCollection != null) { + mCallbackCollection.remove(listener); + } + } + + @Override + public void clearScrollViewCallbacks() { + if (mCallbackCollection != null) { + mCallbackCollection.clear(); + } + } + @Override public void setTouchInterceptionViewGroup(ViewGroup viewGroup) { mTouchInterceptionViewGroup = viewGroup; @@ -209,6 +235,42 @@ public int getCurrentScrollY() { return mScrollY; } + private void dispatchOnDownMotionEvent() { + if (mCallbacks != null) { + mCallbacks.onDownMotionEvent(); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onDownMotionEvent(); + } + } + } + + private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (mCallbacks != null) { + mCallbacks.onScrollChanged(scrollY, firstScroll, dragging); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onScrollChanged(scrollY, firstScroll, dragging); + } + } + } + + private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { + if (mCallbacks != null) { + mCallbacks.onUpOrCancelMotionEvent(scrollState); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onUpOrCancelMotionEvent(scrollState); + } + } + } + static class SavedState extends BaseSavedState { int prevScrollY; int scrollY; diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java index 3e023f74..b4ccf99a 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java @@ -25,6 +25,9 @@ import android.view.ViewGroup; import android.webkit.WebView; +import java.util.ArrayList; +import java.util.List; + /** * WebView that its scroll position can be observed. */ @@ -36,6 +39,7 @@ public class ObservableWebView extends WebView implements Scrollable { // Fields that don't need to be saved onSaveInstanceState private ObservableScrollViewCallbacks mCallbacks; + private List mCallbackCollection; private ScrollState mScrollState; private boolean mFirstScroll; private boolean mDragging; @@ -192,6 +196,28 @@ public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) { mCallbacks = listener; } + @Override + public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) { + if (mCallbackCollection == null) { + mCallbackCollection = new ArrayList<>(); + } + mCallbackCollection.add(listener); + } + + @Override + public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) { + if (mCallbackCollection != null) { + mCallbackCollection.remove(listener); + } + } + + @Override + public void clearScrollViewCallbacks() { + if (mCallbackCollection != null) { + mCallbackCollection.clear(); + } + } + @Override public void setTouchInterceptionViewGroup(ViewGroup viewGroup) { mTouchInterceptionViewGroup = viewGroup; diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java index 1043b4bc..688928c0 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java @@ -27,8 +27,29 @@ public interface Scrollable { * * @param listener listener to set */ + @Deprecated void setScrollViewCallbacks(ObservableScrollViewCallbacks listener); + /** + * Add a callback listener + * + * @param listener listener to add + * */ + void addScrollViewCallbacks(ObservableScrollViewCallbacks listener); + + /** + * Remove a callback listener + * + * @param listener to remove + * */ + void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener); + + /** + * Clear callback listeners + * + * */ + void clearScrollViewCallbacks(); + /** * Scrolls vertically to the absolute Y. * Implemented classes are expected to scroll to the exact Y pixels from the top, From 1bf12c6c14b973905481b2f7d4dc419abff79d05 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 25 Oct 2015 22:05:28 +0900 Subject: [PATCH 182/208] Trivial fix for comments and format. - Reformatted codes on Android Studio. - Reformatted comments and their style. --- .../CacheFragmentStatePagerAdapter.java | 32 ++++++------- .../ObservableGridView.java | 32 ++++++------- .../ObservableListView.java | 2 +- .../ObservableRecyclerView.java | 4 +- .../ObservableScrollView.java | 2 +- .../ObservableScrollViewCallbacks.java | 12 ++--- .../ObservableWebView.java | 2 +- .../observablescrollview/ScrollUtils.java | 48 +++++++++---------- .../observablescrollview/Scrollable.java | 39 ++++++++------- .../TouchInterceptionFrameLayout.java | 26 +++++----- 10 files changed, 101 insertions(+), 98 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java index f0057410..193143d4 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java @@ -26,10 +26,10 @@ /** * FragmentStatePagerAdapter that caches each pages. - * FragmentStatePagerAdapter is also originally caches pages, + *

      FragmentStatePagerAdapter is also originally caches pages, * but its keys are not public nor documented, so depending - * on how it create cache key is dangerous. - * This adapter caches pages by itself and provide getter method to the cache. + * on how it create cache key is dangerous.

      + *

      This adapter caches pages by itself and provide getter method to the cache.

      */ public abstract class CacheFragmentStatePagerAdapter extends FragmentStatePagerAdapter { @@ -83,15 +83,15 @@ public void restoreState(Parcelable state, ClassLoader loader) { /** * Get a new Fragment instance. - * Each fragments are automatically cached in this method, + *

      Each fragments are automatically cached in this method, * so you don't have to do it by yourself. * If you want to implement instantiation of Fragments, - * you should override {@link #createItem(int)} instead. - * + * you should override {@link #createItem(int)} instead.

      + *

      * {@inheritDoc} * - * @param position position of the item in the adapter - * @return fragment instance + * @param position Position of the item in the adapter. + * @return Fragment instance. */ @Override public Fragment getItem(int position) { @@ -112,8 +112,8 @@ public void destroyItem(ViewGroup container, int position, Object object) { /** * Get the item at the specified position in the adapter. * - * @param position position of the item in the adapter - * @return fragment instance + * @param position Position of the item in the adapter. + * @return Fragment instance. */ public Fragment getItemAt(int position) { return mPages.get(position); @@ -123,16 +123,16 @@ public Fragment getItemAt(int position) { * Create a new Fragment instance. * This is called inside {@link #getItem(int)}. * - * @param position position of the item in the adapter - * @return fragment instance + * @param position Position of the item in the adapter. + * @return Fragment instance. */ protected abstract Fragment createItem(int position); /** * Create an index string for caching Fragment pages. * - * @param index index of the item in the adapter - * @return key string for caching Fragment pages + * @param index Index of the item in the adapter. + * @return Key string for caching Fragment pages. */ protected String createCacheIndex(int index) { return STATE_PAGE_INDEX_PREFIX + index; @@ -141,8 +141,8 @@ protected String createCacheIndex(int index) { /** * Create a key string for caching Fragment pages. * - * @param position position of the item in the adapter - * @return key string for caching Fragment pages + * @param position Position of the item in the adapter. + * @return Key string for caching Fragment pages. */ protected String createCacheKey(int position) { return STATE_PAGE_KEY_PREFIX + position; diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index 6ea41954..28aad893 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -225,7 +225,7 @@ public void addFooterView(View v, Object data, boolean isSelectable) { ListAdapter mAdapter = getAdapter(); if (mAdapter != null && !(mAdapter instanceof HeaderViewGridAdapter)) { throw new IllegalStateException( - "Cannot add header view to grid -- setAdapter has already been called."); + "Cannot add header view to grid -- setAdapter has already been called."); } ViewGroup.LayoutParams lyp = v.getLayoutParams(); @@ -487,10 +487,10 @@ public FullWidthFixedViewLayout(Context context) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int targetWidth = ObservableGridView.this.getMeasuredWidth() - - ObservableGridView.this.getPaddingLeft() - - ObservableGridView.this.getPaddingRight(); + - ObservableGridView.this.getPaddingLeft() + - ObservableGridView.this.getPaddingRight(); widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth, - MeasureSpec.getMode(widthMeasureSpec)); + MeasureSpec.getMode(widthMeasureSpec)); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } @@ -550,7 +550,7 @@ public void writeToParcel(Parcel out, int flags) { } public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { + = new Parcelable.Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); @@ -610,7 +610,7 @@ public static class HeaderViewGridAdapter implements WrapperListAdapter, Filtera private final DataSetObservable mDataSetObservable = new DataSetObservable(); private final ListAdapter mAdapter; static final ArrayList EMPTY_INFO_LIST = - new ArrayList(); + new ArrayList(); // This ArrayList is assumed to NOT be null. ArrayList mHeaderViewInfos; @@ -638,7 +638,7 @@ public HeaderViewGridAdapter(ArrayList headerViewInfos, ArrayList mFooterViewInfos = footViewInfos; } mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos) - && areAllListInfosSelectable(mFooterViewInfos); + && areAllListInfosSelectable(mFooterViewInfos); } public void setNumColumns(int numColumns) { @@ -664,7 +664,7 @@ public int getFootersCount() { } /** - * @return true if this adapter doesn't contain any data. This is used to determine + * @return True if this adapter doesn't contain any data. This is used to determine * whether the empty view should be displayed. A typical implementation will return * getCount() == 0 but since getCount() includes the headers and footers, specialized * adapters might want a different behavior. @@ -691,7 +691,7 @@ public boolean removeHeader(View v) { if (info.view == v) { mHeaderViewInfos.remove(i); mAreAllFixedViewsSelectable = - areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos); + areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos); mDataSetObservable.notifyChanged(); return true; } @@ -705,7 +705,7 @@ public boolean removeFooter(View v) { if (info.view == v) { mFooterViewInfos.remove(i); mAreAllFixedViewsSelectable = - areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos); + areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos); mDataSetObservable.notifyChanged(); return true; } @@ -737,7 +737,7 @@ public boolean isEnabled(int position) { int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; if (position < numHeadersAndPlaceholders) { return position % mNumColumns == 0 - && mHeaderViewInfos.get(position / mNumColumns).isSelectable; + && mHeaderViewInfos.get(position / mNumColumns).isSelectable; } // Adapter @@ -753,7 +753,7 @@ public boolean isEnabled(int position) { // Footer (off-limits positions will throw an IndexOutOfBoundsException) final int footerPosition = adjPosition - adapterCount; return footerPosition % mNumColumns == 0 - && mFooterViewInfos.get(footerPosition / mNumColumns).isSelectable; + && mFooterViewInfos.get(footerPosition / mNumColumns).isSelectable; } @Override @@ -815,7 +815,7 @@ public View getView(int position, View convertView, ViewGroup parent) { int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns; if (position < numHeadersAndPlaceholders) { View headerViewContainer = mHeaderViewInfos - .get(position / mNumColumns).viewContainer; + .get(position / mNumColumns).viewContainer; if (position % mNumColumns == 0) { return headerViewContainer; } else { @@ -851,7 +851,7 @@ public View getView(int position, View convertView, ViewGroup parent) { final int footerPosition = adjPosition - adapterCount; if (footerPosition < getCount()) { View footViewContainer = mFooterViewInfos - .get(footerPosition / mNumColumns).viewContainer; + .get(footerPosition / mNumColumns).viewContainer; if (position % mNumColumns == 0) { return footViewContainer; } else { @@ -916,9 +916,7 @@ public int getItemViewType(int position) { } /** - * content view, content view holder, header[0], header and footer placeholder(s) - * - * @return + * Content view, content view holder, header[0], header and footer placeholder(s). */ @Override public int getViewTypeCount() { diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java index 37220c1e..639fc444 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java @@ -433,7 +433,7 @@ public void writeToParcel(Parcel out, int flags) { } public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { + = new Parcelable.Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java index 0e5a5411..81042ff2 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java @@ -325,7 +325,7 @@ public void scrollVerticallyTo(int y) { * and handles which method should be called for scrolling.

      *

      Other know classes (StaggeredGridLayoutManager and GridLayoutManager) are not tested.

      * - * @param position position to scroll + * @param position Position to scroll. */ public void scrollVerticallyToPosition(int position) { LayoutManager lm = getLayoutManager(); @@ -494,7 +494,7 @@ public Parcelable getSuperState() { } public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { + = new Parcelable.Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java index b69d548c..4cc818e3 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java @@ -299,7 +299,7 @@ public void writeToParcel(Parcel out, int flags) { } public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { + = new Parcelable.Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java index b390c4cb..003c806c 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollViewCallbacks.java @@ -22,13 +22,13 @@ public interface ObservableScrollViewCallbacks { /** * Called when the scroll change events occurred. - * This won't be called just after the view is laid out, so if you'd like to + *

      This won't be called just after the view is laid out, so if you'd like to * initialize the position of your views with this method, you should call this manually - * or invoke scroll as appropriate. + * or invoke scroll as appropriate.

      * - * @param scrollY scroll position in Y axis - * @param firstScroll true when this is called for the first time in the consecutive motion events - * @param dragging true when the view is dragged and false when the view is scrolled in the inertia + * @param scrollY Scroll position in Y axis. + * @param firstScroll True when this is called for the first time in the consecutive motion events. + * @param dragging True when the view is dragged and false when the view is scrolled in the inertia. */ void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging); @@ -40,7 +40,7 @@ public interface ObservableScrollViewCallbacks { /** * Called when the dragging ended or canceled. * - * @param scrollState state to indicate the scroll direction + * @param scrollState State to indicate the scroll direction. */ void onUpOrCancelMotionEvent(ScrollState scrollState); } diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java index b4ccf99a..5de1dceb 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java @@ -261,7 +261,7 @@ public void writeToParcel(Parcel out, int flags) { } public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { + = new Parcelable.Creator() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java index daec7556..b27d4815 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java @@ -30,13 +30,13 @@ private ScrollUtils() { /** * Return a float value within the range. - * This is just a wrapper for Math.min() and Math.max(). - * This may be useful if you feel it confusing ("Which is min and which is max?"). + *

      This is just a wrapper for Math.min() and Math.max(). + * This may be useful if you feel it confusing ("Which is min and which is max?").

      * - * @param value the target value - * @param minValue minimum value. If value is less than this, minValue will be returned - * @param maxValue maximum value. If value is greater than this, maxValue will be returned - * @return float value limited to the range + * @param value The target value. + * @param minValue Minimum value. If value is less than this, minValue will be returned. + * @param maxValue Maximum value. If value is greater than this, maxValue will be returned. + * @return Float value limited to the range. */ public static float getFloat(final float value, final float minValue, final float maxValue) { return Math.min(maxValue, Math.max(minValue, value)); @@ -44,11 +44,11 @@ public static float getFloat(final float value, final float minValue, final floa /** * Create a color integer value with specified alpha. - * This may be useful to change alpha value of background color. + *

      This may be useful to change alpha value of background color.

      * - * @param alpha alpha value from 0.0f to 1.0f. - * @param baseColor base color. alpha value will be ignored. - * @return a color with alpha made from base color + * @param alpha Alpha value from 0.0f to 1.0f. + * @param baseColor Base color. alpha value will be ignored. + * @return A color with alpha made from base color. */ public static int getColorWithAlpha(float alpha, int baseColor) { int a = Math.min(255, Math.max(0, (int) (alpha * 255))) << 24; @@ -58,11 +58,11 @@ public static int getColorWithAlpha(float alpha, int baseColor) { /** * Add an OnGlobalLayoutListener for the view. - * This is just a convenience method for using {@code ViewTreeObserver.OnGlobalLayoutListener()}. - * This also handles removing listener when onGlobalLayout is called. + *

      This is just a convenience method for using {@code ViewTreeObserver.OnGlobalLayoutListener()}. + * This also handles removing listener when onGlobalLayout is called.

      * - * @param view the target view to add global layout listener - * @param runnable runnable to be executed after the view is laid out + * @param view The target view to add global layout listener. + * @param runnable Runnable to be executed after the view is laid out. */ public static void addOnGlobalLayoutListener(final View view, final Runnable runnable) { ViewTreeObserver vto = view.getViewTreeObserver(); @@ -81,13 +81,13 @@ public void onGlobalLayout() { /** * Mix two colors. - * {@code toColor} will be {@code toAlpha/1} percent, - * and {@code fromColor} will be {@code (1-toAlpha)/1} percent. + *

      {@code toColor} will be {@code toAlpha/1} percent, + * and {@code fromColor} will be {@code (1-toAlpha)/1} percent.

      * - * @param fromColor first color to be mixed - * @param toColor second color to be mixed - * @param toAlpha alpha value of toColor, 0.0f to 1.0f. - * @return mixed color value in ARGB. Alpha is fixed value (255). + * @param fromColor First color to be mixed. + * @param toColor Second color to be mixed. + * @param toAlpha Alpha value of toColor, 0.0f to 1.0f. + * @return Mixed color value in ARGB. Alpha is fixed value (255). */ public static int mixColors(int fromColor, int toColor, float toAlpha) { float[] fromCmyk = ScrollUtils.cmykFromRgb(fromColor); @@ -102,8 +102,8 @@ public static int mixColors(int fromColor, int toColor, float toAlpha) { /** * Convert RGB color to CMYK color. * - * @param rgbColor target color - * @return CMYK array + * @param rgbColor Target color. + * @return CMYK array. */ public static float[] cmykFromRgb(int rgbColor) { int red = (0xff0000 & rgbColor) >> 16; @@ -124,9 +124,9 @@ public static float[] cmykFromRgb(int rgbColor) { /** * Convert CYMK color to RGB color. - * This method doesn't check f cmyk is not null or have 4 elements in array. + * This method doesn't check if cmyk is not null or have 4 elements in array. * - * @param cmyk target CYMK color. Each value should be between 0.0f to 1.0f, + * @param cmyk Target CYMK color. Each value should be between 0.0f to 1.0f, * and should be set in this order: cyan, magenta, yellow, black. * @return ARGB color. Alpha is fixed value (255). */ diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java index 688928c0..47f10283 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/Scrollable.java @@ -19,59 +19,64 @@ import android.view.ViewGroup; /** - * Provides common API for observable and scrollable widgets. + * Interface for providing common API for observable and scrollable widgets. */ public interface Scrollable { /** - * Sets a callback listener. + * Set a callback listener.
      + * Developers should use {@link #addScrollViewCallbacks(ObservableScrollViewCallbacks)} + * and {@link #removeScrollViewCallbacks(ObservableScrollViewCallbacks)}. * - * @param listener listener to set + * @param listener Listener to set. */ @Deprecated void setScrollViewCallbacks(ObservableScrollViewCallbacks listener); /** - * Add a callback listener + * Add a callback listener. * - * @param listener listener to add - * */ + * @param listener Listener to add. + * @since 1.7.0 + */ void addScrollViewCallbacks(ObservableScrollViewCallbacks listener); /** - * Remove a callback listener + * Remove a callback listener. * - * @param listener to remove - * */ + * @param listener Listener to remove. + * @since 1.7.0 + */ void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener); /** - * Clear callback listeners + * Clear callback listeners. * - * */ + * @since 1.7.0 + */ void clearScrollViewCallbacks(); /** - * Scrolls vertically to the absolute Y. + * Scroll vertically to the absolute Y.
      * Implemented classes are expected to scroll to the exact Y pixels from the top, * but it depends on the type of the widget. * - * @param y vertical position to scroll to + * @param y Vertical position to scroll to. */ void scrollVerticallyTo(int y); /** - * Returns the current Y of the scrollable view. + * Return the current Y of the scrollable view. * - * @return current Y pixel + * @return Current Y pixel. */ int getCurrentScrollY(); /** - * Sets a touch motion event delegation ViewGroup. + * Set a touch motion event delegation ViewGroup.
      * This is used to pass motion events back to parent view. * It's up to the implementation classes whether or not it works. * - * @param viewGroup ViewGroup object to dispatch motion events + * @param viewGroup ViewGroup object to dispatch motion events. */ void setTouchInterceptionViewGroup(ViewGroup viewGroup); } diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/TouchInterceptionFrameLayout.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/TouchInterceptionFrameLayout.java index 6a5ed557..a4ecf1d7 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/TouchInterceptionFrameLayout.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/TouchInterceptionFrameLayout.java @@ -40,36 +40,36 @@ public class TouchInterceptionFrameLayout extends FrameLayout { */ public interface TouchInterceptionListener { /** - * Determines whether the layout should intercept this event. + * Determine whether the layout should intercept this event. * - * @param ev motion event - * @param moving true if this event is ACTION_MOVE type - * @param diffX difference between previous X and current X, if moving is true - * @param diffY difference between previous Y and current Y, if moving is true - * @return true if the layout should intercept + * @param ev Motion event. + * @param moving True if this event is ACTION_MOVE type. + * @param diffX Difference between previous X and current X, if moving is true. + * @param diffY Difference between previous Y and current Y, if moving is true. + * @return True if the layout should intercept. */ boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY); /** * Called if the down motion event is intercepted by this layout. * - * @param ev motion event + * @param ev Motion event. */ void onDownMotionEvent(MotionEvent ev); /** * Called if the move motion event is intercepted by this layout. * - * @param ev motion event - * @param diffX difference between previous X and current X - * @param diffY difference between previous Y and current Y + * @param ev Motion event. + * @param diffX Difference between previous X and current X. + * @param diffY Difference between previous Y and current Y. */ void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY); /** * Called if the up (or cancel) motion event is intercepted by this layout. * - * @param ev motion event + * @param ev Motion event. */ void onUpOrCancelMotionEvent(MotionEvent ev); } @@ -247,8 +247,8 @@ private MotionEvent obtainMotionEvent(MotionEvent base, int action) { * child views, but calling dispatchTouchEvent() causes StackOverflowError. * Therefore we do it manually. * - * @param ev motion event to be passed to children - * @param pendingEvents pending events like ACTION_DOWN. This will be passed to the children before ev + * @param ev Motion event to be passed to children. + * @param pendingEvents Pending events like ACTION_DOWN. This will be passed to the children before ev. */ private void duplicateTouchEventForChildren(MotionEvent ev, MotionEvent... pendingEvents) { if (ev == null) { From 4d0df9db3705e508e7688994ac29090ab0096519 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 25 Oct 2015 22:06:24 +0900 Subject: [PATCH 183/208] Update version to 1.7.0-SNAPSHOT. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 00af7e5e..5fd0a55d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.6.0 +VERSION_NAME=1.7.0-SNAPSHOT SYNCED_VERSION_NAME=1.5.2 GROUP=com.github.ksoichiro From a85c1b1b611377998668b3cfb6282a00a2ccc895 Mon Sep 17 00:00:00 2001 From: CsHeng Date: Wed, 28 Oct 2015 17:15:59 +0800 Subject: [PATCH 184/208] fix ObservableListView & ObservableGridView not restore the right scrollY state, reference: ObservableRecyclerView --- .../android/observablescrollview/ObservableGridView.java | 1 + .../android/observablescrollview/ObservableListView.java | 1 + 2 files changed, 2 insertions(+) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index 28aad893..c95c9abf 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -443,6 +443,7 @@ private void onScrollChanged() { mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); } else if (firstVisiblePosition == 0) { mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + mPrevScrolledChildrenHeight = 0; } if (mPrevFirstVisibleChildHeight < 0) { mPrevFirstVisibleChildHeight = 0; diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java index 639fc444..126e3c97 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java @@ -317,6 +317,7 @@ private void onScrollChanged() { mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); } else if (firstVisiblePosition == 0) { mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + mPrevScrolledChildrenHeight = 0; } if (mPrevFirstVisibleChildHeight < 0) { mPrevFirstVisibleChildHeight = 0; From 33f600467ac632e8f521d21e9cc2d04d6b93a136 Mon Sep 17 00:00:00 2001 From: CsHeng Date: Thu, 29 Oct 2015 22:19:35 +0800 Subject: [PATCH 185/208] fix negative scrollY when ObservableView has paddingTop --- .../android/observablescrollview/ObservableGridView.java | 2 +- .../android/observablescrollview/ObservableListView.java | 2 +- .../android/observablescrollview/ObservableRecyclerView.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index c95c9abf..e901a5a0 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -308,7 +308,7 @@ public void scrollVerticallyTo(int y) { @Override public int getCurrentScrollY() { - return mScrollY; + return mScrollY + getPaddingTop(); } @Override diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java index 126e3c97..1a3ef2f0 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java @@ -261,7 +261,7 @@ public void scrollVerticallyTo(int y) { @Override public int getCurrentScrollY() { - return mScrollY; + return mScrollY + getPaddingTop(); } private void init() { diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java index 81042ff2..97c6f4c5 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java @@ -339,7 +339,7 @@ public void scrollVerticallyToPosition(int position) { @Override public int getCurrentScrollY() { - return mScrollY; + return mScrollY + getPaddingTop(); } @SuppressWarnings("deprecation") From d19cb44726a15cd03e4d9fdba551fc9c78634b25 Mon Sep 17 00:00:00 2001 From: Gurgen Davtyan Date: Thu, 12 Nov 2015 18:49:36 +0400 Subject: [PATCH 186/208] Fixed bug with dividers in ListView https://github.com/ksoichiro/Android-ObservableScrollView/issues/219 --- .../android/observablescrollview/ObservableListView.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java index 126e3c97..99b109fd 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java @@ -322,7 +322,8 @@ private void onScrollChanged() { if (mPrevFirstVisibleChildHeight < 0) { mPrevFirstVisibleChildHeight = 0; } - mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop(); + mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + + firstVisiblePosition * getDividerHeight(); mPrevFirstVisiblePosition = firstVisiblePosition; dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); From c8ae4d5529fe2ab35069830c25dcba9e4c2002a2 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 28 Nov 2015 21:38:42 +0900 Subject: [PATCH 187/208] #215 Fix paddingTop calculation to keep scrollY meaning consistent. Just adding getPaddingTop to mScrollY breaks the meaning of scrollY, so moved the code to the mScrollY calculation. --- .../android/observablescrollview/ObservableGridView.java | 4 ++-- .../android/observablescrollview/ObservableListView.java | 6 +++--- .../observablescrollview/ObservableRecyclerView.java | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index e901a5a0..c44c339c 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -308,7 +308,7 @@ public void scrollVerticallyTo(int y) { @Override public int getCurrentScrollY() { - return mScrollY + getPaddingTop(); + return mScrollY; } @Override @@ -448,7 +448,7 @@ private void onScrollChanged() { if (mPrevFirstVisibleChildHeight < 0) { mPrevFirstVisibleChildHeight = 0; } - mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop(); + mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + getPaddingTop(); mPrevFirstVisiblePosition = firstVisiblePosition; dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java index 0912446b..79974881 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java @@ -261,7 +261,7 @@ public void scrollVerticallyTo(int y) { @Override public int getCurrentScrollY() { - return mScrollY + getPaddingTop(); + return mScrollY; } private void init() { @@ -322,8 +322,8 @@ private void onScrollChanged() { if (mPrevFirstVisibleChildHeight < 0) { mPrevFirstVisibleChildHeight = 0; } - mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + - firstVisiblePosition * getDividerHeight(); + mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + + firstVisiblePosition * getDividerHeight() + getPaddingTop(); mPrevFirstVisiblePosition = firstVisiblePosition; dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java index 97c6f4c5..9f133f89 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java @@ -158,7 +158,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { if (mPrevFirstVisibleChildHeight < 0) { mPrevFirstVisibleChildHeight = 0; } - mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop(); + mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + getPaddingTop(); mPrevFirstVisiblePosition = firstVisiblePosition; dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); @@ -339,7 +339,7 @@ public void scrollVerticallyToPosition(int position) { @Override public int getCurrentScrollY() { - return mScrollY + getPaddingTop(); + return mScrollY; } @SuppressWarnings("deprecation") From 25db1ca02710f356c1e8aa553291af87fdc1d8e4 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 28 Nov 2015 22:30:14 +0900 Subject: [PATCH 188/208] Move Gradle files to gradle dir as Gradle project does. --- {library => gradle}/gradle-mvn-push.gradle | 0 {samples => gradle}/version.gradle | 0 library/build.gradle | 2 +- samples/build.gradle | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) rename {library => gradle}/gradle-mvn-push.gradle (100%) rename {samples => gradle}/version.gradle (100%) diff --git a/library/gradle-mvn-push.gradle b/gradle/gradle-mvn-push.gradle similarity index 100% rename from library/gradle-mvn-push.gradle rename to gradle/gradle-mvn-push.gradle diff --git a/samples/version.gradle b/gradle/version.gradle similarity index 100% rename from samples/version.gradle rename to gradle/version.gradle diff --git a/library/build.gradle b/library/build.gradle index 8f6b7348..649cc23d 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -59,4 +59,4 @@ apply plugin: 'com.github.kt3k.coveralls' coveralls.jacocoReportPath = 'build/reports/coverage/debug/report.xml' // This is from 'https://github.com/chrisbanes/gradle-mvn-push' -apply from: 'gradle-mvn-push.gradle' +apply from: "${rootDir}/gradle/gradle-mvn-push.gradle" diff --git a/samples/build.gradle b/samples/build.gradle index d2514542..51eb4ffb 100644 --- a/samples/build.gradle +++ b/samples/build.gradle @@ -30,7 +30,7 @@ dependencies { //compile "com.github.ksoichiro:android-observablescrollview:$VERSION_NAME" } -apply from: 'version.gradle' +apply from: "${rootDir}/gradle/version.gradle" project.ext.versionInfo.releaseVersionName = SYNCED_VERSION_NAME android { From 1fc3c5cbd8a30931944dab0869d08d2d7c792638 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 28 Nov 2015 23:34:13 +0900 Subject: [PATCH 189/208] Update Android Gradle Plugin to 1.5.0. It's no longer supported to specify JaCoCo version with android.jacoco. Instead, adding org.jacoco.core dependency to buildscript is required. --- build.gradle | 2 ++ library/build.gradle | 6 +----- samples/build.gradle | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 6e616911..7d9fd6a0 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,8 @@ buildscript { } dependencies { classpath 'com.github.ksoichiro:gradle-eclipse-aar-plugin:0.1.1' + // Related issue: https://code.google.com/p/android/issues/detail?id=192875 + classpath 'org.jacoco:org.jacoco.core:0.7.5.201505241946' } } diff --git a/library/build.gradle b/library/build.gradle index 649cc23d..09d5d440 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -4,7 +4,7 @@ buildscript { } dependencies { classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.1.0' - classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.android.tools.build:gradle:1.5.0' } } @@ -32,10 +32,6 @@ android { minSdkVersion 9 } - jacoco { - version = '0.7.2.201409121644' - } - buildTypes { debug { testCoverageEnabled = true diff --git a/samples/build.gradle b/samples/build.gradle index 51eb4ffb..0ab1f509 100644 --- a/samples/build.gradle +++ b/samples/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.android.tools.build:gradle:1.5.0' } } From 118ed053dc148784f51c2e0cec9037ccec022527 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 29 Nov 2015 00:43:13 +0900 Subject: [PATCH 190/208] Add tests for ObservableGridView. Tests for footer view. --- .../test/HeaderGridViewActivity.java | 9 +++++++ .../test/HeaderGridViewActivityTest.java | 27 +++++++++++++++++++ .../ObservableGridView.java | 4 +++ 3 files changed, 40 insertions(+) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivity.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivity.java index 587b328d..70d536e5 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivity.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivity.java @@ -13,6 +13,7 @@ public class HeaderGridViewActivity extends Activity implements ObservableScrollViewCallbacks { public View headerView; + public View footerView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -30,6 +31,14 @@ protected void onCreate(Bundle savedInstanceState) { headerView.setClickable(true); scrollable.addHeaderView(headerView); + + // Footer is also available. + footerView = new View(this); + FrameLayout.LayoutParams lpf = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, + flexibleSpaceImageHeight); + footerView.setLayoutParams(lpf); + scrollable.addFooterView(footerView); + scrollable.setScrollViewCallbacks(this); UiTestUtils.setDummyData(this, scrollable); scrollable.setOnScrollListener(new AbsListView.OnScrollListener() { diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java index 5e668a6c..7b22009e 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java @@ -69,16 +69,19 @@ public void testHeaderViewFeatures() throws Throwable { @Override public void run() { assertEquals(1, scrollable.getHeaderViewCount()); + assertEquals(1, scrollable.getFooterViewCount()); ListAdapter adapter = scrollable.getAdapter(); assertTrue(adapter instanceof ObservableGridView.HeaderViewGridAdapter); ObservableGridView.HeaderViewGridAdapter hvgAdapter = (ObservableGridView.HeaderViewGridAdapter) adapter; assertEquals(1, hvgAdapter.getHeadersCount()); + assertEquals(1, hvgAdapter.getFootersCount()); assertNotNull(hvgAdapter.getWrappedAdapter()); assertTrue(hvgAdapter.areAllItemsEnabled()); assertFalse(hvgAdapter.isEmpty()); Object data = hvgAdapter.getItem(0); assertNull(data); assertNotNull(hvgAdapter.getView(0, null, scrollable)); + assertNotNull(hvgAdapter.getView(1, null, scrollable)); assertNotNull(hvgAdapter.getFilter()); assertTrue(scrollable.removeHeaderView(activity.headerView)); assertEquals(0, scrollable.getHeaderViewCount()); @@ -97,6 +100,30 @@ public void run() { scrollable.addHeaderView(activity.headerView); } }); + // Scroll to bottom and try removing re-adding the footer view. + for (int i = 0; i < 10; i++) { + UiTestUtils.swipeVertically(this, scrollable, UiTestUtils.Direction.UP); + } + getInstrumentation().waitForIdleSync(); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + ListAdapter adapter = scrollable.getAdapter(); + ObservableGridView.HeaderViewGridAdapter hvgAdapter = (ObservableGridView.HeaderViewGridAdapter) adapter; + + assertTrue(scrollable.removeFooterView(activity.footerView)); + assertEquals(0, scrollable.getFooterViewCount()); + assertEquals(0, hvgAdapter.getFootersCount()); + assertFalse(scrollable.removeFooterView(activity.footerView)); + + activity.footerView = new View(activity); + final int flexibleSpaceImageHeight = activity.getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + FrameLayout.LayoutParams lpf = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, + flexibleSpaceImageHeight); + activity.footerView.setLayoutParams(lpf); + scrollable.addFooterView(activity.footerView); + } + }); } public void testHeaderViewGridExceptions() throws Throwable { diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index c44c339c..d17f4a3f 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -249,6 +249,10 @@ public void addFooterView(View v, Object data, boolean isSelectable) { } } + public int getFooterViewCount() { + return mFooterViewInfos.size(); + } + public boolean removeFooterView(View v) { if (mFooterViewInfos.size() > 0) { boolean result = false; From f03fd2db27a273a40fa63b8c672e67a9f8df5b39 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 29 Nov 2015 00:56:22 +0900 Subject: [PATCH 191/208] Fix some warnings by Android Studio. - Removed redundant type parameter. - Added SuppressWarnings annotation for intentional usage of deprecated method. --- .../observablescrollview/CacheFragmentStatePagerAdapter.java | 2 +- .../android/observablescrollview/ObservableGridView.java | 3 +-- .../ksoichiro/android/observablescrollview/ScrollUtils.java | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java index 193143d4..f4699437 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/CacheFragmentStatePagerAdapter.java @@ -43,7 +43,7 @@ public abstract class CacheFragmentStatePagerAdapter extends FragmentStatePagerA public CacheFragmentStatePagerAdapter(FragmentManager fm) { super(fm); - mPages = new SparseArray(); + mPages = new SparseArray<>(); mFm = fm; } diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index d17f4a3f..11f9f6e4 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -614,8 +614,7 @@ public static class FixedViewInfo { public static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable { private final DataSetObservable mDataSetObservable = new DataSetObservable(); private final ListAdapter mAdapter; - static final ArrayList EMPTY_INFO_LIST = - new ArrayList(); + static final ArrayList EMPTY_INFO_LIST = new ArrayList<>(); // This ArrayList is assumed to NOT be null. ArrayList mHeaderViewInfos; diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java index b27d4815..cbe5e9c5 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ScrollUtils.java @@ -67,6 +67,7 @@ public static int getColorWithAlpha(float alpha, int baseColor) { public static void addOnGlobalLayoutListener(final View view, final Runnable runnable) { ViewTreeObserver vto = view.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @SuppressWarnings("deprecation") @Override public void onGlobalLayout() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { From 830781c9ae45c4ae2c54646c1e146a42b21429a4 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 29 Nov 2015 01:43:28 +0900 Subject: [PATCH 192/208] Fix lack of handling for callback collection in ObservableWebView. --- .../ObservableWebView.java | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java index 5de1dceb..f9661ea1 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java @@ -79,10 +79,10 @@ public Parcelable onSaveInstanceState() { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { mScrollY = t; - mCallbacks.onScrollChanged(t, mFirstScroll, mDragging); + dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); if (mFirstScroll) { mFirstScroll = false; } @@ -100,7 +100,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Whether or not motion events are consumed by children, @@ -110,7 +110,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { // Also, applications might implement initialization codes to onDownMotionEvent, // so call it here. mFirstScroll = mDragging = true; - mCallbacks.onDownMotionEvent(); + dispatchOnDownMotionEvent(); break; } } @@ -119,7 +119,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mCallbacks != null) { + if (mCallbacks != null || mCallbackCollection != null) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: break; @@ -127,7 +127,7 @@ public boolean onTouchEvent(MotionEvent ev) { case MotionEvent.ACTION_CANCEL: mIntercepted = false; mDragging = false; - mCallbacks.onUpOrCancelMotionEvent(mScrollState); + dispatchOnUpOrCancelMotionEvent(mScrollState); break; case MotionEvent.ACTION_MOVE: if (mPrevMoveEvent == null) { @@ -233,6 +233,42 @@ public int getCurrentScrollY() { return mScrollY; } + private void dispatchOnDownMotionEvent() { + if (mCallbacks != null) { + mCallbacks.onDownMotionEvent(); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onDownMotionEvent(); + } + } + } + + private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + if (mCallbacks != null) { + mCallbacks.onScrollChanged(scrollY, firstScroll, dragging); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onScrollChanged(scrollY, firstScroll, dragging); + } + } + } + + private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { + if (mCallbacks != null) { + mCallbacks.onUpOrCancelMotionEvent(scrollState); + } + if (mCallbackCollection != null) { + for (int i = 0; i < mCallbackCollection.size(); i++) { + ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); + callbacks.onUpOrCancelMotionEvent(scrollState); + } + } + } + static class SavedState extends BaseSavedState { int prevScrollY; int scrollY; From 19ffb0decd4b9068d59f3ce71716d373b71236ce Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 29 Nov 2015 01:51:17 +0900 Subject: [PATCH 193/208] Add tests for add/remove/clearScrollViewCallbacks. --- .../test/GridViewActivityTest.java | 76 +++++++++++++++++++ .../test/ListViewActivityTest.java | 76 +++++++++++++++++++ .../test/RecyclerViewActivityTest.java | 76 +++++++++++++++++++ .../test/ScrollViewActivityTest.java | 76 +++++++++++++++++++ .../test/WebViewActivityTest.java | 76 +++++++++++++++++++ 5 files changed, 380 insertions(+) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java index f38784bd..a5f19701 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java @@ -6,11 +6,14 @@ import android.util.TypedValue; import com.github.ksoichiro.android.observablescrollview.ObservableGridView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; public class GridViewActivityTest extends ActivityInstrumentationTestCase2 { private Activity activity; private ObservableGridView scrollable; + private int[] callbackCounter; public GridViewActivityTest() { super(GridViewActivity.class); @@ -22,6 +25,7 @@ protected void setUp() throws Exception { setActivityInitialTouchMode(true); activity = getActivity(); scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable); + callbackCounter = new int[2]; } public void testInitialize() throws Throwable { @@ -65,4 +69,76 @@ public void run() { }); getInstrumentation().waitForIdleSync(); } + + public void testCallbacks() throws Throwable { + final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2]; + callbackCounter[0] = 0; + callbackCounter[1] = 0; + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable); + callbacks[0] = new ObservableScrollViewCallbacks() { + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + callbackCounter[0]++; + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + }; + scrollable.addScrollViewCallbacks(callbacks[0]); + callbacks[1] = new ObservableScrollViewCallbacks() { + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + callbackCounter[1]++; + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + }; + scrollable.addScrollViewCallbacks(callbacks[1]); + } + }); + testScroll(); + // Assert that all the callbacks are enabled and get called. + assertTrue(0 < callbackCounter[0]); + assertTrue(0 < callbackCounter[1]); + + // Remove one of the callbacks and scroll again to assert it's really removed. + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.removeScrollViewCallbacks(callbacks[0]); + } + }); + callbackCounter[0] = 0; + callbackCounter[1] = 0; + testScroll(); + assertTrue(0 == callbackCounter[0]); + assertTrue(0 < callbackCounter[1]); + + // Clear all callbacks and assert they're really removed. + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.clearScrollViewCallbacks(); + } + }); + callbackCounter[0] = 0; + callbackCounter[1] = 0; + testScroll(); + assertTrue(0 == callbackCounter[0]); + assertTrue(0 == callbackCounter[1]); + } } diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java index c53fef9e..2714373c 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java @@ -6,11 +6,14 @@ import android.util.TypedValue; import com.github.ksoichiro.android.observablescrollview.ObservableListView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; public class ListViewActivityTest extends ActivityInstrumentationTestCase2 { private Activity activity; private ObservableListView scrollable; + private int[] callbackCounter; public ListViewActivityTest() { super(ListViewActivity.class); @@ -22,6 +25,7 @@ protected void setUp() throws Exception { setActivityInitialTouchMode(true); activity = getActivity(); scrollable = (ObservableListView) activity.findViewById(R.id.scrollable); + callbackCounter = new int[2]; } public void testInitialize() throws Throwable { @@ -65,4 +69,76 @@ public void run() { }); getInstrumentation().waitForIdleSync(); } + + public void testCallbacks() throws Throwable { + final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2]; + callbackCounter[0] = 0; + callbackCounter[1] = 0; + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable = (ObservableListView) activity.findViewById(R.id.scrollable); + callbacks[0] = new ObservableScrollViewCallbacks() { + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + callbackCounter[0]++; + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + }; + scrollable.addScrollViewCallbacks(callbacks[0]); + callbacks[1] = new ObservableScrollViewCallbacks() { + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + callbackCounter[1]++; + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + }; + scrollable.addScrollViewCallbacks(callbacks[1]); + } + }); + testScroll(); + // Assert that all the callbacks are enabled and get called. + assertTrue(0 < callbackCounter[0]); + assertTrue(0 < callbackCounter[1]); + + // Remove one of the callbacks and scroll again to assert it's really removed. + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.removeScrollViewCallbacks(callbacks[0]); + } + }); + callbackCounter[0] = 0; + callbackCounter[1] = 0; + testScroll(); + assertTrue(0 == callbackCounter[0]); + assertTrue(0 < callbackCounter[1]); + + // Clear all callbacks and assert they're really removed. + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.clearScrollViewCallbacks(); + } + }); + callbackCounter[0] = 0; + callbackCounter[1] = 0; + testScroll(); + assertTrue(0 == callbackCounter[0]); + assertTrue(0 == callbackCounter[1]); + } } diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java index e448dc33..335ec8fa 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java @@ -6,11 +6,14 @@ import android.util.TypedValue; import com.github.ksoichiro.android.observablescrollview.ObservableRecyclerView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; public class RecyclerViewActivityTest extends ActivityInstrumentationTestCase2 { private Activity activity; private ObservableRecyclerView scrollable; + private int[] callbackCounter; public RecyclerViewActivityTest() { super(RecyclerViewActivity.class); @@ -22,6 +25,7 @@ protected void setUp() throws Exception { setActivityInitialTouchMode(true); activity = getActivity(); scrollable = (ObservableRecyclerView) activity.findViewById(R.id.scrollable); + callbackCounter = new int[2]; getInstrumentation().waitForIdleSync(); } @@ -66,4 +70,76 @@ public void run() { }); getInstrumentation().waitForIdleSync(); } + + public void testCallbacks() throws Throwable { + final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2]; + callbackCounter[0] = 0; + callbackCounter[1] = 0; + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable = (ObservableRecyclerView) activity.findViewById(R.id.scrollable); + callbacks[0] = new ObservableScrollViewCallbacks() { + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + callbackCounter[0]++; + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + }; + scrollable.addScrollViewCallbacks(callbacks[0]); + callbacks[1] = new ObservableScrollViewCallbacks() { + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + callbackCounter[1]++; + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + }; + scrollable.addScrollViewCallbacks(callbacks[1]); + } + }); + testScroll(); + // Assert that all the callbacks are enabled and get called. + assertTrue(0 < callbackCounter[0]); + assertTrue(0 < callbackCounter[1]); + + // Remove one of the callbacks and scroll again to assert it's really removed. + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.removeScrollViewCallbacks(callbacks[0]); + } + }); + callbackCounter[0] = 0; + callbackCounter[1] = 0; + testScroll(); + assertTrue(0 == callbackCounter[0]); + assertTrue(0 < callbackCounter[1]); + + // Clear all callbacks and assert they're really removed. + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.clearScrollViewCallbacks(); + } + }); + callbackCounter[0] = 0; + callbackCounter[1] = 0; + testScroll(); + assertTrue(0 == callbackCounter[0]); + assertTrue(0 == callbackCounter[1]); + } } diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java index fea0f1c2..199fb901 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java @@ -4,11 +4,14 @@ import android.test.ActivityInstrumentationTestCase2; import com.github.ksoichiro.android.observablescrollview.ObservableScrollView; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; +import com.github.ksoichiro.android.observablescrollview.ScrollState; public class ScrollViewActivityTest extends ActivityInstrumentationTestCase2 { private Activity activity; private ObservableScrollView scrollable; + private int[] callbackCounter; public ScrollViewActivityTest() { super(ScrollViewActivity.class); @@ -20,6 +23,7 @@ protected void setUp() throws Exception { setActivityInitialTouchMode(true); activity = getActivity(); scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable); + callbackCounter = new int[2]; } public void testInitialize() throws Throwable { @@ -44,4 +48,76 @@ public void testSaveAndRestoreInstanceState() throws Throwable { UiTestUtils.saveAndRestoreInstanceState(this, activity); testScroll(); } + + public void testCallbacks() throws Throwable { + final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2]; + callbackCounter[0] = 0; + callbackCounter[1] = 0; + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable); + callbacks[0] = new ObservableScrollViewCallbacks() { + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + callbackCounter[0]++; + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + }; + scrollable.addScrollViewCallbacks(callbacks[0]); + callbacks[1] = new ObservableScrollViewCallbacks() { + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + callbackCounter[1]++; + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + }; + scrollable.addScrollViewCallbacks(callbacks[1]); + } + }); + testScroll(); + // Assert that all the callbacks are enabled and get called. + assertTrue(0 < callbackCounter[0]); + assertTrue(0 < callbackCounter[1]); + + // Remove one of the callbacks and scroll again to assert it's really removed. + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.removeScrollViewCallbacks(callbacks[0]); + } + }); + callbackCounter[0] = 0; + callbackCounter[1] = 0; + testScroll(); + assertTrue(0 == callbackCounter[0]); + assertTrue(0 < callbackCounter[1]); + + // Clear all callbacks and assert they're really removed. + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.clearScrollViewCallbacks(); + } + }); + callbackCounter[0] = 0; + callbackCounter[1] = 0; + testScroll(); + assertTrue(0 == callbackCounter[0]); + assertTrue(0 == callbackCounter[1]); + } } diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java index faf9c5e3..359929ad 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java @@ -5,12 +5,15 @@ import android.util.DisplayMetrics; import android.util.TypedValue; +import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; import com.github.ksoichiro.android.observablescrollview.ObservableWebView; +import com.github.ksoichiro.android.observablescrollview.ScrollState; public class WebViewActivityTest extends ActivityInstrumentationTestCase2 { private Activity activity; private ObservableWebView scrollable; + private int[] callbackCounter; public WebViewActivityTest() { super(WebViewActivity.class); @@ -22,6 +25,7 @@ protected void setUp() throws Exception { setActivityInitialTouchMode(true); activity = getActivity(); scrollable = (ObservableWebView) activity.findViewById(R.id.scrollable); + callbackCounter = new int[2]; } public void testInitialize() throws Throwable { @@ -65,4 +69,76 @@ public void run() { }); getInstrumentation().waitForIdleSync(); } + + public void testCallbacks() throws Throwable { + final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2]; + callbackCounter[0] = 0; + callbackCounter[1] = 0; + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable = (ObservableWebView) activity.findViewById(R.id.scrollable); + callbacks[0] = new ObservableScrollViewCallbacks() { + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + callbackCounter[0]++; + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + }; + scrollable.addScrollViewCallbacks(callbacks[0]); + callbacks[1] = new ObservableScrollViewCallbacks() { + @Override + public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { + callbackCounter[1]++; + } + + @Override + public void onDownMotionEvent() { + } + + @Override + public void onUpOrCancelMotionEvent(ScrollState scrollState) { + } + }; + scrollable.addScrollViewCallbacks(callbacks[1]); + } + }); + testScroll(); + // Assert that all the callbacks are enabled and get called. + assertTrue(0 < callbackCounter[0]); + assertTrue(0 < callbackCounter[1]); + + // Remove one of the callbacks and scroll again to assert it's really removed. + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.removeScrollViewCallbacks(callbacks[0]); + } + }); + callbackCounter[0] = 0; + callbackCounter[1] = 0; + testScroll(); + assertTrue(0 == callbackCounter[0]); + assertTrue(0 < callbackCounter[1]); + + // Clear all callbacks and assert they're really removed. + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable.clearScrollViewCallbacks(); + } + }); + callbackCounter[0] = 0; + callbackCounter[1] = 0; + testScroll(); + assertTrue(0 == callbackCounter[0]); + assertTrue(0 == callbackCounter[1]); + } } From 4ed0f72e8f134b6d030c2b6845290045b99f7c53 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 29 Nov 2015 22:06:17 +0900 Subject: [PATCH 194/208] Add tests for adding header/footer when the adapter is already set. --- .../test/GridViewActivityTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java index a5f19701..698f2af6 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java @@ -4,6 +4,8 @@ import android.test.ActivityInstrumentationTestCase2; import android.util.DisplayMetrics; import android.util.TypedValue; +import android.view.View; +import android.widget.FrameLayout; import com.github.ksoichiro.android.observablescrollview.ObservableGridView; import com.github.ksoichiro.android.observablescrollview.ObservableScrollViewCallbacks; @@ -141,4 +143,32 @@ public void run() { assertTrue(0 == callbackCounter[0]); assertTrue(0 == callbackCounter[1]); } + + public void testCannotAddHeaderOrFooterWhenAdapterIsAlreadySet() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + try { + View view = new View(activity); + final int flexibleSpaceImageHeight = activity.getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, flexibleSpaceImageHeight); + view.setLayoutParams(lp); + view.setClickable(true); + scrollable.addHeaderView(view); + fail(); + } catch (IllegalStateException ignore) { + } + + try { + View view = new View(activity); + final int flexibleSpaceImageHeight = activity.getResources().getDimensionPixelSize(R.dimen.flexible_space_image_height); + FrameLayout.LayoutParams lpf = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, flexibleSpaceImageHeight); + view.setLayoutParams(lpf); + scrollable.addFooterView(view); + fail(); + } catch (IllegalStateException ignore) { + } + } + }); + } } From dd3097f2046c3b1b1328a675fbd35ffdcf05f3e7 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 29 Nov 2015 22:15:08 +0900 Subject: [PATCH 195/208] Refactor: extract method to check it has any callbacks. --- .../observablescrollview/ObservableGridView.java | 10 +++++++--- .../observablescrollview/ObservableListView.java | 10 +++++++--- .../observablescrollview/ObservableRecyclerView.java | 10 +++++++--- .../observablescrollview/ObservableScrollView.java | 10 +++++++--- .../observablescrollview/ObservableWebView.java | 10 +++++++--- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index 11f9f6e4..4f124189 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -128,7 +128,7 @@ public Parcelable onSaveInstanceState() { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Whether or not motion events are consumed by children, @@ -147,7 +147,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: @@ -408,7 +408,7 @@ private int getNumColumnsCompat() { } private void onScrollChanged() { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { if (getChildCount() > 0) { int firstVisiblePosition = getFirstVisiblePosition(); for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) { @@ -484,6 +484,10 @@ private void removeFixedViewInfo(View v, ArrayList where) { } } + private boolean hasCallbacks() { + return mCallbacks != null || mCallbackCollection != null; + } + private class FullWidthFixedViewLayout extends FrameLayout { public FullWidthFixedViewLayout(Context context) { super(context); diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java index 79974881..6d6c984c 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java @@ -116,7 +116,7 @@ public Parcelable onSaveInstanceState() { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Whether or not motion events are consumed by children, @@ -135,7 +135,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: @@ -270,7 +270,7 @@ private void init() { } private void onScrollChanged() { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { if (getChildCount() > 0) { int firstVisiblePosition = getFirstVisiblePosition(); for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) { @@ -380,6 +380,10 @@ private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { } } + private boolean hasCallbacks() { + return mCallbacks != null || mCallbackCollection != null; + } + static class SavedState extends BaseSavedState { int prevFirstVisiblePosition; int prevFirstVisibleChildHeight = -1; diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java index 9f133f89..27839fbe 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java @@ -100,7 +100,7 @@ public Parcelable onSaveInstanceState() { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { if (getChildCount() > 0) { int firstVisiblePosition = getChildAdapterPosition(getChildAt(0)); int lastVisiblePosition = getChildAdapterPosition(getChildAt(getChildCount() - 1)); @@ -183,7 +183,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Whether or not motion events are consumed by children, @@ -202,7 +202,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: @@ -399,6 +399,10 @@ private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { } } + private boolean hasCallbacks() { + return mCallbacks != null || mCallbackCollection != null; + } + /** * This saved state class is a Parcelable and should not extend * {@link android.view.View.BaseSavedState} nor {@link android.view.AbsSavedState} diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java index 4cc818e3..29a967aa 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java @@ -79,7 +79,7 @@ public Parcelable onSaveInstanceState() { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { mScrollY = t; dispatchOnScrollChanged(t, mFirstScroll, mDragging); @@ -104,7 +104,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Whether or not motion events are consumed by children, @@ -123,7 +123,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: @@ -271,6 +271,10 @@ private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { } } + private boolean hasCallbacks() { + return mCallbacks != null || mCallbackCollection != null; + } + static class SavedState extends BaseSavedState { int prevScrollY; int scrollY; diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java index f9661ea1..78eddd86 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java @@ -79,7 +79,7 @@ public Parcelable onSaveInstanceState() { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { mScrollY = t; dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); @@ -100,7 +100,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Whether or not motion events are consumed by children, @@ -119,7 +119,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mCallbacks != null || mCallbackCollection != null) { + if (hasCallbacks()) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: break; @@ -269,6 +269,10 @@ private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { } } + private boolean hasCallbacks() { + return mCallbacks != null || mCallbackCollection != null; + } + static class SavedState extends BaseSavedState { int prevScrollY; int scrollY; From 6ce5204f3584f23d59502e5407177e55685e6e14 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 30 Nov 2015 00:57:45 +0900 Subject: [PATCH 196/208] Invert hasCallbacks() condition to improve nested if/switch. --- .../ObservableGridView.java | 257 ++++++++-------- .../ObservableListView.java | 285 ++++++++--------- .../ObservableRecyclerView.java | 289 +++++++++--------- .../ObservableScrollView.java | 197 ++++++------ .../ObservableWebView.java | 193 ++++++------ 5 files changed, 618 insertions(+), 603 deletions(-) diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index 4f124189..6ea47e25 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -128,91 +128,93 @@ public Parcelable onSaveInstanceState() { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (hasCallbacks()) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - // Whether or not motion events are consumed by children, - // flag initializations which are related to ACTION_DOWN events should be executed. - // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are - // passed to parent (this view), the flags will be invalid. - // Also, applications might implement initialization codes to onDownMotionEvent, - // so call it here. - mFirstScroll = mDragging = true; - dispatchOnDownMotionEvent(); - break; - } + if (hasNoCallbacks()) { + return super.onInterceptTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + // Whether or not motion events are consumed by children, + // flag initializations which are related to ACTION_DOWN events should be executed. + // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are + // passed to parent (this view), the flags will be invalid. + // Also, applications might implement initialization codes to onDownMotionEvent, + // so call it here. + mFirstScroll = mDragging = true; + dispatchOnDownMotionEvent(); + break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { - if (hasCallbacks()) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mIntercepted = false; - mDragging = false; - dispatchOnUpOrCancelMotionEvent(mScrollState); - break; - case MotionEvent.ACTION_MOVE: - if (mPrevMoveEvent == null) { - mPrevMoveEvent = ev; + if (hasNoCallbacks()) { + return super.onTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mIntercepted = false; + mDragging = false; + dispatchOnUpOrCancelMotionEvent(mScrollState); + break; + case MotionEvent.ACTION_MOVE: + if (mPrevMoveEvent == null) { + mPrevMoveEvent = ev; + } + float diffY = ev.getY() - mPrevMoveEvent.getY(); + mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); + if (getCurrentScrollY() - diffY <= 0) { + // Can't scroll anymore. + + if (mIntercepted) { + // Already dispatched ACTION_DOWN event to parents, so stop here. + return false; } - float diffY = ev.getY() - mPrevMoveEvent.getY(); - mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); - if (getCurrentScrollY() - diffY <= 0) { - // Can't scroll anymore. - - if (mIntercepted) { - // Already dispatched ACTION_DOWN event to parents, so stop here. - return false; - } - // Apps can set the interception target other than the direct parent. - final ViewGroup parent; - if (mTouchInterceptionViewGroup == null) { - parent = (ViewGroup) getParent(); - } else { - parent = mTouchInterceptionViewGroup; - } + // Apps can set the interception target other than the direct parent. + final ViewGroup parent; + if (mTouchInterceptionViewGroup == null) { + parent = (ViewGroup) getParent(); + } else { + parent = mTouchInterceptionViewGroup; + } - // Get offset to parents. If the parent is not the direct parent, - // we should aggregate offsets from all of the parents. - float offsetX = 0; - float offsetY = 0; - for (View v = this; v != null && v != parent; v = (View) v.getParent()) { - offsetX += v.getLeft() - v.getScrollX(); - offsetY += v.getTop() - v.getScrollY(); - } - final MotionEvent event = MotionEvent.obtainNoHistory(ev); - event.offsetLocation(offsetX, offsetY); - - if (parent.onInterceptTouchEvent(event)) { - mIntercepted = true; - - // If the parent wants to intercept ACTION_MOVE events, - // we pass ACTION_DOWN event to the parent - // as if these touch events just have began now. - event.setAction(MotionEvent.ACTION_DOWN); - - // Return this onTouchEvent() first and set ACTION_DOWN event for parent - // to the queue, to keep events sequence. - post(new Runnable() { - @Override - public void run() { - parent.dispatchTouchEvent(event); - } - }); - return false; - } - // Even when this can't be scrolled anymore, - // simply returning false here may cause subView's click, - // so delegate it to super. - return super.onTouchEvent(ev); + // Get offset to parents. If the parent is not the direct parent, + // we should aggregate offsets from all of the parents. + float offsetX = 0; + float offsetY = 0; + for (View v = this; v != null && v != parent; v = (View) v.getParent()) { + offsetX += v.getLeft() - v.getScrollX(); + offsetY += v.getTop() - v.getScrollY(); } - break; - } + final MotionEvent event = MotionEvent.obtainNoHistory(ev); + event.offsetLocation(offsetX, offsetY); + + if (parent.onInterceptTouchEvent(event)) { + mIntercepted = true; + + // If the parent wants to intercept ACTION_MOVE events, + // we pass ACTION_DOWN event to the parent + // as if these touch events just have began now. + event.setAction(MotionEvent.ACTION_DOWN); + + // Return this onTouchEvent() first and set ACTION_DOWN event for parent + // to the queue, to keep events sequence. + post(new Runnable() { + @Override + public void run() { + parent.dispatchTouchEvent(event); + } + }); + return false; + } + // Even when this can't be scrolled anymore, + // simply returning false here may cause subView's click, + // so delegate it to super. + return super.onTouchEvent(ev); + } + break; } return super.onTouchEvent(ev); } @@ -408,67 +410,68 @@ private int getNumColumnsCompat() { } private void onScrollChanged() { - if (hasCallbacks()) { - if (getChildCount() > 0) { - int firstVisiblePosition = getFirstVisiblePosition(); - for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) { - if (mChildrenHeights.indexOfKey(i) < 0 || getChildAt(j).getHeight() != mChildrenHeights.get(i)) { - if (i % getNumColumnsCompat() == 0) { - mChildrenHeights.put(i, getChildAt(j).getHeight()); - } + if (hasNoCallbacks()) { + return; + } + if (getChildCount() > 0) { + int firstVisiblePosition = getFirstVisiblePosition(); + for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) { + if (mChildrenHeights.indexOfKey(i) < 0 || getChildAt(j).getHeight() != mChildrenHeights.get(i)) { + if (i % getNumColumnsCompat() == 0) { + mChildrenHeights.put(i, getChildAt(j).getHeight()); } } + } - View firstVisibleChild = getChildAt(0); - if (firstVisibleChild != null) { - if (mPrevFirstVisiblePosition < firstVisiblePosition) { - // scroll down - int skippedChildrenHeight = 0; - if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) { - for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) { - if (0 < mChildrenHeights.indexOfKey(i)) { - skippedChildrenHeight += mChildrenHeights.get(i); - } + View firstVisibleChild = getChildAt(0); + if (firstVisibleChild != null) { + if (mPrevFirstVisiblePosition < firstVisiblePosition) { + // scroll down + int skippedChildrenHeight = 0; + if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) { + for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) { + if (0 < mChildrenHeights.indexOfKey(i)) { + skippedChildrenHeight += mChildrenHeights.get(i); } } - mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight; - mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); - } else if (firstVisiblePosition < mPrevFirstVisiblePosition) { - // scroll up - int skippedChildrenHeight = 0; - if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) { - for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) { - if (0 < mChildrenHeights.indexOfKey(i)) { - skippedChildrenHeight += mChildrenHeights.get(i); - } + } + mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight; + mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + } else if (firstVisiblePosition < mPrevFirstVisiblePosition) { + // scroll up + int skippedChildrenHeight = 0; + if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) { + for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) { + if (0 < mChildrenHeights.indexOfKey(i)) { + skippedChildrenHeight += mChildrenHeights.get(i); } } - mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight; - mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); - } else if (firstVisiblePosition == 0) { - mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); - mPrevScrolledChildrenHeight = 0; - } - if (mPrevFirstVisibleChildHeight < 0) { - mPrevFirstVisibleChildHeight = 0; } - mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + getPaddingTop(); - mPrevFirstVisiblePosition = firstVisiblePosition; + mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight; + mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + } else if (firstVisiblePosition == 0) { + mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + mPrevScrolledChildrenHeight = 0; + } + if (mPrevFirstVisibleChildHeight < 0) { + mPrevFirstVisibleChildHeight = 0; + } + mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + getPaddingTop(); + mPrevFirstVisiblePosition = firstVisiblePosition; - dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); - if (mFirstScroll) { - mFirstScroll = false; - } + dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); + if (mFirstScroll) { + mFirstScroll = false; + } - if (mPrevScrollY < mScrollY) { - mScrollState = ScrollState.UP; - } else if (mScrollY < mPrevScrollY) { - mScrollState = ScrollState.DOWN; - } else { - mScrollState = ScrollState.STOP; - } - mPrevScrollY = mScrollY; + if (mPrevScrollY < mScrollY) { + mScrollState = ScrollState.UP; + } else if (mScrollY < mPrevScrollY) { + mScrollState = ScrollState.DOWN; + } else { + mScrollState = ScrollState.STOP; } + mPrevScrollY = mScrollY; } } } @@ -484,8 +487,8 @@ private void removeFixedViewInfo(View v, ArrayList where) { } } - private boolean hasCallbacks() { - return mCallbacks != null || mCallbackCollection != null; + private boolean hasNoCallbacks() { + return mCallbacks == null && mCallbackCollection == null; } private class FullWidthFixedViewLayout extends FrameLayout { diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java index 6d6c984c..3820dadf 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableListView.java @@ -116,96 +116,98 @@ public Parcelable onSaveInstanceState() { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (hasCallbacks()) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - // Whether or not motion events are consumed by children, - // flag initializations which are related to ACTION_DOWN events should be executed. - // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are - // passed to parent (this view), the flags will be invalid. - // Also, applications might implement initialization codes to onDownMotionEvent, - // so call it here. - mFirstScroll = mDragging = true; - dispatchOnDownMotionEvent(); - break; - } + if (hasNoCallbacks()) { + return super.onInterceptTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + // Whether or not motion events are consumed by children, + // flag initializations which are related to ACTION_DOWN events should be executed. + // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are + // passed to parent (this view), the flags will be invalid. + // Also, applications might implement initialization codes to onDownMotionEvent, + // so call it here. + mFirstScroll = mDragging = true; + dispatchOnDownMotionEvent(); + break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { - if (hasCallbacks()) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mIntercepted = false; - mDragging = false; - dispatchOnUpOrCancelMotionEvent(mScrollState); - break; - case MotionEvent.ACTION_MOVE: - if (mPrevMoveEvent == null) { - mPrevMoveEvent = ev; + if (hasNoCallbacks()) { + return super.onTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mIntercepted = false; + mDragging = false; + dispatchOnUpOrCancelMotionEvent(mScrollState); + break; + case MotionEvent.ACTION_MOVE: + if (mPrevMoveEvent == null) { + mPrevMoveEvent = ev; + } + float diffY = ev.getY() - mPrevMoveEvent.getY(); + mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); + if (getCurrentScrollY() - diffY <= 0) { + // Can't scroll anymore. + + if (mIntercepted) { + // Already dispatched ACTION_DOWN event to parents, so stop here. + return false; } - float diffY = ev.getY() - mPrevMoveEvent.getY(); - mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); - if (getCurrentScrollY() - diffY <= 0) { - // Can't scroll anymore. - - if (mIntercepted) { - // Already dispatched ACTION_DOWN event to parents, so stop here. - return false; - } - // Apps can set the interception target other than the direct parent. - final ViewGroup parent; - if (mTouchInterceptionViewGroup == null) { - parent = (ViewGroup) getParent(); - } else { - parent = mTouchInterceptionViewGroup; - } + // Apps can set the interception target other than the direct parent. + final ViewGroup parent; + if (mTouchInterceptionViewGroup == null) { + parent = (ViewGroup) getParent(); + } else { + parent = mTouchInterceptionViewGroup; + } - // Get offset to parents. If the parent is not the direct parent, - // we should aggregate offsets from all of the parents. - float offsetX = 0; - float offsetY = 0; - for (View v = this; v != null && v != parent; ) { - offsetX += v.getLeft() - v.getScrollX(); - offsetY += v.getTop() - v.getScrollY(); - try { - v = (View) v.getParent(); - } catch (ClassCastException ex) { - break; - } - } - final MotionEvent event = MotionEvent.obtainNoHistory(ev); - event.offsetLocation(offsetX, offsetY); - - if (parent.onInterceptTouchEvent(event)) { - mIntercepted = true; - - // If the parent wants to intercept ACTION_MOVE events, - // we pass ACTION_DOWN event to the parent - // as if these touch events just have began now. - event.setAction(MotionEvent.ACTION_DOWN); - - // Return this onTouchEvent() first and set ACTION_DOWN event for parent - // to the queue, to keep events sequence. - post(new Runnable() { - @Override - public void run() { - parent.dispatchTouchEvent(event); - } - }); - return false; + // Get offset to parents. If the parent is not the direct parent, + // we should aggregate offsets from all of the parents. + float offsetX = 0; + float offsetY = 0; + for (View v = this; v != null && v != parent; ) { + offsetX += v.getLeft() - v.getScrollX(); + offsetY += v.getTop() - v.getScrollY(); + try { + v = (View) v.getParent(); + } catch (ClassCastException ex) { + break; } - // Even when this can't be scrolled anymore, - // simply returning false here may cause subView's click, - // so delegate it to super. - return super.onTouchEvent(ev); } - break; - } + final MotionEvent event = MotionEvent.obtainNoHistory(ev); + event.offsetLocation(offsetX, offsetY); + + if (parent.onInterceptTouchEvent(event)) { + mIntercepted = true; + + // If the parent wants to intercept ACTION_MOVE events, + // we pass ACTION_DOWN event to the parent + // as if these touch events just have began now. + event.setAction(MotionEvent.ACTION_DOWN); + + // Return this onTouchEvent() first and set ACTION_DOWN event for parent + // to the queue, to keep events sequence. + post(new Runnable() { + @Override + public void run() { + parent.dispatchTouchEvent(event); + } + }); + return false; + } + // Even when this can't be scrolled anymore, + // simply returning false here may cause subView's click, + // so delegate it to super. + return super.onTouchEvent(ev); + } + break; } return super.onTouchEvent(ev); } @@ -270,76 +272,77 @@ private void init() { } private void onScrollChanged() { - if (hasCallbacks()) { - if (getChildCount() > 0) { - int firstVisiblePosition = getFirstVisiblePosition(); - for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) { - if (mChildrenHeights.indexOfKey(i) < 0 || getChildAt(j).getHeight() != mChildrenHeights.get(i)) { - mChildrenHeights.put(i, getChildAt(j).getHeight()); - } + if (hasNoCallbacks()) { + return; + } + if (getChildCount() > 0) { + int firstVisiblePosition = getFirstVisiblePosition(); + for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) { + if (mChildrenHeights.indexOfKey(i) < 0 || getChildAt(j).getHeight() != mChildrenHeights.get(i)) { + mChildrenHeights.put(i, getChildAt(j).getHeight()); } + } - View firstVisibleChild = getChildAt(0); - if (firstVisibleChild != null) { - if (mPrevFirstVisiblePosition < firstVisiblePosition) { - // scroll down - int skippedChildrenHeight = 0; - if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) { - for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) { - if (0 < mChildrenHeights.indexOfKey(i)) { - skippedChildrenHeight += mChildrenHeights.get(i); - } else { - // Approximate each item's height to the first visible child. - // It may be incorrect, but without this, scrollY will be broken - // when scrolling from the bottom. - skippedChildrenHeight += firstVisibleChild.getHeight(); - } + View firstVisibleChild = getChildAt(0); + if (firstVisibleChild != null) { + if (mPrevFirstVisiblePosition < firstVisiblePosition) { + // scroll down + int skippedChildrenHeight = 0; + if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) { + for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) { + if (0 < mChildrenHeights.indexOfKey(i)) { + skippedChildrenHeight += mChildrenHeights.get(i); + } else { + // Approximate each item's height to the first visible child. + // It may be incorrect, but without this, scrollY will be broken + // when scrolling from the bottom. + skippedChildrenHeight += firstVisibleChild.getHeight(); } } - mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight; - mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); - } else if (firstVisiblePosition < mPrevFirstVisiblePosition) { - // scroll up - int skippedChildrenHeight = 0; - if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) { - for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) { - if (0 < mChildrenHeights.indexOfKey(i)) { - skippedChildrenHeight += mChildrenHeights.get(i); - } else { - // Approximate each item's height to the first visible child. - // It may be incorrect, but without this, scrollY will be broken - // when scrolling from the bottom. - skippedChildrenHeight += firstVisibleChild.getHeight(); - } + } + mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight; + mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + } else if (firstVisiblePosition < mPrevFirstVisiblePosition) { + // scroll up + int skippedChildrenHeight = 0; + if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) { + for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) { + if (0 < mChildrenHeights.indexOfKey(i)) { + skippedChildrenHeight += mChildrenHeights.get(i); + } else { + // Approximate each item's height to the first visible child. + // It may be incorrect, but without this, scrollY will be broken + // when scrolling from the bottom. + skippedChildrenHeight += firstVisibleChild.getHeight(); } } - mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight; - mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); - } else if (firstVisiblePosition == 0) { - mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); - mPrevScrolledChildrenHeight = 0; - } - if (mPrevFirstVisibleChildHeight < 0) { - mPrevFirstVisibleChildHeight = 0; } - mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + - firstVisiblePosition * getDividerHeight() + getPaddingTop(); - mPrevFirstVisiblePosition = firstVisiblePosition; + mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight; + mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + } else if (firstVisiblePosition == 0) { + mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + mPrevScrolledChildrenHeight = 0; + } + if (mPrevFirstVisibleChildHeight < 0) { + mPrevFirstVisibleChildHeight = 0; + } + mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + + firstVisiblePosition * getDividerHeight() + getPaddingTop(); + mPrevFirstVisiblePosition = firstVisiblePosition; - dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); - if (mFirstScroll) { - mFirstScroll = false; - } + dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); + if (mFirstScroll) { + mFirstScroll = false; + } - if (mPrevScrollY < mScrollY) { - mScrollState = ScrollState.UP; - } else if (mScrollY < mPrevScrollY) { - mScrollState = ScrollState.DOWN; - } else { - mScrollState = ScrollState.STOP; - } - mPrevScrollY = mScrollY; + if (mPrevScrollY < mScrollY) { + mScrollState = ScrollState.UP; + } else if (mScrollY < mPrevScrollY) { + mScrollState = ScrollState.DOWN; + } else { + mScrollState = ScrollState.STOP; } + mPrevScrollY = mScrollY; } } } @@ -380,8 +383,8 @@ private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { } } - private boolean hasCallbacks() { - return mCallbacks != null || mCallbackCollection != null; + private boolean hasNoCallbacks() { + return mCallbacks == null && mCallbackCollection == null; } static class SavedState extends BaseSavedState { diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java index 27839fbe..2f84828c 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableRecyclerView.java @@ -100,174 +100,177 @@ public Parcelable onSaveInstanceState() { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - if (hasCallbacks()) { - if (getChildCount() > 0) { - int firstVisiblePosition = getChildAdapterPosition(getChildAt(0)); - int lastVisiblePosition = getChildAdapterPosition(getChildAt(getChildCount() - 1)); - for (int i = firstVisiblePosition, j = 0; i <= lastVisiblePosition; i++, j++) { - int childHeight = 0; - View child = getChildAt(j); - if (child != null) { - if (mChildrenHeights.indexOfKey(i) < 0 || (child.getHeight() != mChildrenHeights.get(i))) { - childHeight = child.getHeight(); - } + if (hasNoCallbacks()) { + return; + } + if (getChildCount() > 0) { + int firstVisiblePosition = getChildAdapterPosition(getChildAt(0)); + int lastVisiblePosition = getChildAdapterPosition(getChildAt(getChildCount() - 1)); + for (int i = firstVisiblePosition, j = 0; i <= lastVisiblePosition; i++, j++) { + int childHeight = 0; + View child = getChildAt(j); + if (child != null) { + if (mChildrenHeights.indexOfKey(i) < 0 || (child.getHeight() != mChildrenHeights.get(i))) { + childHeight = child.getHeight(); } - mChildrenHeights.put(i, childHeight); } + mChildrenHeights.put(i, childHeight); + } - View firstVisibleChild = getChildAt(0); - if (firstVisibleChild != null) { - if (mPrevFirstVisiblePosition < firstVisiblePosition) { - // scroll down - int skippedChildrenHeight = 0; - if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) { - for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) { - if (0 < mChildrenHeights.indexOfKey(i)) { - skippedChildrenHeight += mChildrenHeights.get(i); - } else { - // Approximate each item's height to the first visible child. - // It may be incorrect, but without this, scrollY will be broken - // when scrolling from the bottom. - skippedChildrenHeight += firstVisibleChild.getHeight(); - } + View firstVisibleChild = getChildAt(0); + if (firstVisibleChild != null) { + if (mPrevFirstVisiblePosition < firstVisiblePosition) { + // scroll down + int skippedChildrenHeight = 0; + if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) { + for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) { + if (0 < mChildrenHeights.indexOfKey(i)) { + skippedChildrenHeight += mChildrenHeights.get(i); + } else { + // Approximate each item's height to the first visible child. + // It may be incorrect, but without this, scrollY will be broken + // when scrolling from the bottom. + skippedChildrenHeight += firstVisibleChild.getHeight(); } } - mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight; - mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); - } else if (firstVisiblePosition < mPrevFirstVisiblePosition) { - // scroll up - int skippedChildrenHeight = 0; - if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) { - for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) { - if (0 < mChildrenHeights.indexOfKey(i)) { - skippedChildrenHeight += mChildrenHeights.get(i); - } else { - // Approximate each item's height to the first visible child. - // It may be incorrect, but without this, scrollY will be broken - // when scrolling from the bottom. - skippedChildrenHeight += firstVisibleChild.getHeight(); - } + } + mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight; + mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + } else if (firstVisiblePosition < mPrevFirstVisiblePosition) { + // scroll up + int skippedChildrenHeight = 0; + if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) { + for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) { + if (0 < mChildrenHeights.indexOfKey(i)) { + skippedChildrenHeight += mChildrenHeights.get(i); + } else { + // Approximate each item's height to the first visible child. + // It may be incorrect, but without this, scrollY will be broken + // when scrolling from the bottom. + skippedChildrenHeight += firstVisibleChild.getHeight(); } } - mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight; - mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); - } else if (firstVisiblePosition == 0) { - mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); - mPrevScrolledChildrenHeight = 0; } - if (mPrevFirstVisibleChildHeight < 0) { - mPrevFirstVisibleChildHeight = 0; - } - mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + getPaddingTop(); - mPrevFirstVisiblePosition = firstVisiblePosition; + mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight; + mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + } else if (firstVisiblePosition == 0) { + mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); + mPrevScrolledChildrenHeight = 0; + } + if (mPrevFirstVisibleChildHeight < 0) { + mPrevFirstVisibleChildHeight = 0; + } + mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop() + getPaddingTop(); + mPrevFirstVisiblePosition = firstVisiblePosition; - dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); - if (mFirstScroll) { - mFirstScroll = false; - } + dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); + if (mFirstScroll) { + mFirstScroll = false; + } - if (mPrevScrollY < mScrollY) { - //down - mScrollState = ScrollState.UP; - } else if (mScrollY < mPrevScrollY) { - //up - mScrollState = ScrollState.DOWN; - } else { - mScrollState = ScrollState.STOP; - } - mPrevScrollY = mScrollY; + if (mPrevScrollY < mScrollY) { + //down + mScrollState = ScrollState.UP; + } else if (mScrollY < mPrevScrollY) { + //up + mScrollState = ScrollState.DOWN; + } else { + mScrollState = ScrollState.STOP; } + mPrevScrollY = mScrollY; } } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (hasCallbacks()) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - // Whether or not motion events are consumed by children, - // flag initializations which are related to ACTION_DOWN events should be executed. - // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are - // passed to parent (this view), the flags will be invalid. - // Also, applications might implement initialization codes to onDownMotionEvent, - // so call it here. - mFirstScroll = mDragging = true; - dispatchOnDownMotionEvent(); - break; - } + if (hasNoCallbacks()) { + return super.onInterceptTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + // Whether or not motion events are consumed by children, + // flag initializations which are related to ACTION_DOWN events should be executed. + // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are + // passed to parent (this view), the flags will be invalid. + // Also, applications might implement initialization codes to onDownMotionEvent, + // so call it here. + mFirstScroll = mDragging = true; + dispatchOnDownMotionEvent(); + break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { - if (hasCallbacks()) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mIntercepted = false; - mDragging = false; - dispatchOnUpOrCancelMotionEvent(mScrollState); - break; - case MotionEvent.ACTION_MOVE: - if (mPrevMoveEvent == null) { - mPrevMoveEvent = ev; + if (hasNoCallbacks()) { + return super.onTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mIntercepted = false; + mDragging = false; + dispatchOnUpOrCancelMotionEvent(mScrollState); + break; + case MotionEvent.ACTION_MOVE: + if (mPrevMoveEvent == null) { + mPrevMoveEvent = ev; + } + float diffY = ev.getY() - mPrevMoveEvent.getY(); + mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); + if (getCurrentScrollY() - diffY <= 0) { + // Can't scroll anymore. + + if (mIntercepted) { + // Already dispatched ACTION_DOWN event to parents, so stop here. + return false; } - float diffY = ev.getY() - mPrevMoveEvent.getY(); - mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); - if (getCurrentScrollY() - diffY <= 0) { - // Can't scroll anymore. - - if (mIntercepted) { - // Already dispatched ACTION_DOWN event to parents, so stop here. - return false; - } - // Apps can set the interception target other than the direct parent. - final ViewGroup parent; - if (mTouchInterceptionViewGroup == null) { - parent = (ViewGroup) getParent(); - } else { - parent = mTouchInterceptionViewGroup; - } + // Apps can set the interception target other than the direct parent. + final ViewGroup parent; + if (mTouchInterceptionViewGroup == null) { + parent = (ViewGroup) getParent(); + } else { + parent = mTouchInterceptionViewGroup; + } - // Get offset to parents. If the parent is not the direct parent, - // we should aggregate offsets from all of the parents. - float offsetX = 0; - float offsetY = 0; - for (View v = this; v != null && v != parent; v = (View) v.getParent()) { - offsetX += v.getLeft() - v.getScrollX(); - offsetY += v.getTop() - v.getScrollY(); - } - final MotionEvent event = MotionEvent.obtainNoHistory(ev); - event.offsetLocation(offsetX, offsetY); - - if (parent.onInterceptTouchEvent(event)) { - mIntercepted = true; - - // If the parent wants to intercept ACTION_MOVE events, - // we pass ACTION_DOWN event to the parent - // as if these touch events just have began now. - event.setAction(MotionEvent.ACTION_DOWN); - - // Return this onTouchEvent() first and set ACTION_DOWN event for parent - // to the queue, to keep events sequence. - post(new Runnable() { - @Override - public void run() { - parent.dispatchTouchEvent(event); - } - }); - return false; - } - // Even when this can't be scrolled anymore, - // simply returning false here may cause subView's click, - // so delegate it to super. - return super.onTouchEvent(ev); + // Get offset to parents. If the parent is not the direct parent, + // we should aggregate offsets from all of the parents. + float offsetX = 0; + float offsetY = 0; + for (View v = this; v != null && v != parent; v = (View) v.getParent()) { + offsetX += v.getLeft() - v.getScrollX(); + offsetY += v.getTop() - v.getScrollY(); } - break; - } + final MotionEvent event = MotionEvent.obtainNoHistory(ev); + event.offsetLocation(offsetX, offsetY); + + if (parent.onInterceptTouchEvent(event)) { + mIntercepted = true; + + // If the parent wants to intercept ACTION_MOVE events, + // we pass ACTION_DOWN event to the parent + // as if these touch events just have began now. + event.setAction(MotionEvent.ACTION_DOWN); + + // Return this onTouchEvent() first and set ACTION_DOWN event for parent + // to the queue, to keep events sequence. + post(new Runnable() { + @Override + public void run() { + parent.dispatchTouchEvent(event); + } + }); + return false; + } + // Even when this can't be scrolled anymore, + // simply returning false here may cause subView's click, + // so delegate it to super. + return super.onTouchEvent(ev); + } + break; } return super.onTouchEvent(ev); } @@ -399,8 +402,8 @@ private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { } } - private boolean hasCallbacks() { - return mCallbacks != null || mCallbackCollection != null; + private boolean hasNoCallbacks() { + return mCallbacks == null && mCallbackCollection == null; } /** diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java index 29a967aa..d841f0b0 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableScrollView.java @@ -79,116 +79,119 @@ public Parcelable onSaveInstanceState() { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - if (hasCallbacks()) { - mScrollY = t; + if (hasNoCallbacks()) { + return; + } + mScrollY = t; - dispatchOnScrollChanged(t, mFirstScroll, mDragging); - if (mFirstScroll) { - mFirstScroll = false; - } + dispatchOnScrollChanged(t, mFirstScroll, mDragging); + if (mFirstScroll) { + mFirstScroll = false; + } - if (mPrevScrollY < t) { - mScrollState = ScrollState.UP; - } else if (t < mPrevScrollY) { - mScrollState = ScrollState.DOWN; - //} else { - // Keep previous state while dragging. - // Never makes it STOP even if scrollY not changed. - // Before Android 4.4, onTouchEvent calls onScrollChanged directly for ACTION_MOVE, - // which makes mScrollState always STOP when onUpOrCancelMotionEvent is called. - // STOP state is now meaningless for ScrollView. - } - mPrevScrollY = t; + if (mPrevScrollY < t) { + mScrollState = ScrollState.UP; + } else if (t < mPrevScrollY) { + mScrollState = ScrollState.DOWN; + //} else { + // Keep previous state while dragging. + // Never makes it STOP even if scrollY not changed. + // Before Android 4.4, onTouchEvent calls onScrollChanged directly for ACTION_MOVE, + // which makes mScrollState always STOP when onUpOrCancelMotionEvent is called. + // STOP state is now meaningless for ScrollView. } + mPrevScrollY = t; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (hasCallbacks()) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - // Whether or not motion events are consumed by children, - // flag initializations which are related to ACTION_DOWN events should be executed. - // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are - // passed to parent (this view), the flags will be invalid. - // Also, applications might implement initialization codes to onDownMotionEvent, - // so call it here. - mFirstScroll = mDragging = true; - dispatchOnDownMotionEvent(); - break; - } + if (hasNoCallbacks()) { + return super.onInterceptTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + // Whether or not motion events are consumed by children, + // flag initializations which are related to ACTION_DOWN events should be executed. + // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are + // passed to parent (this view), the flags will be invalid. + // Also, applications might implement initialization codes to onDownMotionEvent, + // so call it here. + mFirstScroll = mDragging = true; + dispatchOnDownMotionEvent(); + break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { - if (hasCallbacks()) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mIntercepted = false; - mDragging = false; - dispatchOnUpOrCancelMotionEvent(mScrollState); - break; - case MotionEvent.ACTION_MOVE: - if (mPrevMoveEvent == null) { - mPrevMoveEvent = ev; + if (hasNoCallbacks()) { + return super.onTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mIntercepted = false; + mDragging = false; + dispatchOnUpOrCancelMotionEvent(mScrollState); + break; + case MotionEvent.ACTION_MOVE: + if (mPrevMoveEvent == null) { + mPrevMoveEvent = ev; + } + float diffY = ev.getY() - mPrevMoveEvent.getY(); + mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); + if (getCurrentScrollY() - diffY <= 0) { + // Can't scroll anymore. + + if (mIntercepted) { + // Already dispatched ACTION_DOWN event to parents, so stop here. + return false; } - float diffY = ev.getY() - mPrevMoveEvent.getY(); - mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); - if (getCurrentScrollY() - diffY <= 0) { - // Can't scroll anymore. - - if (mIntercepted) { - // Already dispatched ACTION_DOWN event to parents, so stop here. - return false; - } - - // Apps can set the interception target other than the direct parent. - final ViewGroup parent; - if (mTouchInterceptionViewGroup == null) { - parent = (ViewGroup) getParent(); - } else { - parent = mTouchInterceptionViewGroup; - } - - // Get offset to parents. If the parent is not the direct parent, - // we should aggregate offsets from all of the parents. - float offsetX = 0; - float offsetY = 0; - for (View v = this; v != null && v != parent; v = (View) v.getParent()) { - offsetX += v.getLeft() - v.getScrollX(); - offsetY += v.getTop() - v.getScrollY(); - } - final MotionEvent event = MotionEvent.obtainNoHistory(ev); - event.offsetLocation(offsetX, offsetY); - - if (parent.onInterceptTouchEvent(event)) { - mIntercepted = true; - - // If the parent wants to intercept ACTION_MOVE events, - // we pass ACTION_DOWN event to the parent - // as if these touch events just have began now. - event.setAction(MotionEvent.ACTION_DOWN); - - // Return this onTouchEvent() first and set ACTION_DOWN event for parent - // to the queue, to keep events sequence. - post(new Runnable() { - @Override - public void run() { - parent.dispatchTouchEvent(event); - } - }); - return false; - } - // Even when this can't be scrolled anymore, - // simply returning false here may cause subView's click, - // so delegate it to super. - return super.onTouchEvent(ev); + + // Apps can set the interception target other than the direct parent. + final ViewGroup parent; + if (mTouchInterceptionViewGroup == null) { + parent = (ViewGroup) getParent(); + } else { + parent = mTouchInterceptionViewGroup; } - break; - } + + // Get offset to parents. If the parent is not the direct parent, + // we should aggregate offsets from all of the parents. + float offsetX = 0; + float offsetY = 0; + for (View v = this; v != null && v != parent; v = (View) v.getParent()) { + offsetX += v.getLeft() - v.getScrollX(); + offsetY += v.getTop() - v.getScrollY(); + } + final MotionEvent event = MotionEvent.obtainNoHistory(ev); + event.offsetLocation(offsetX, offsetY); + + if (parent.onInterceptTouchEvent(event)) { + mIntercepted = true; + + // If the parent wants to intercept ACTION_MOVE events, + // we pass ACTION_DOWN event to the parent + // as if these touch events just have began now. + event.setAction(MotionEvent.ACTION_DOWN); + + // Return this onTouchEvent() first and set ACTION_DOWN event for parent + // to the queue, to keep events sequence. + post(new Runnable() { + @Override + public void run() { + parent.dispatchTouchEvent(event); + } + }); + return false; + } + // Even when this can't be scrolled anymore, + // simply returning false here may cause subView's click, + // so delegate it to super. + return super.onTouchEvent(ev); + } + break; } return super.onTouchEvent(ev); } @@ -271,8 +274,8 @@ private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { } } - private boolean hasCallbacks() { - return mCallbacks != null || mCallbackCollection != null; + private boolean hasNoCallbacks() { + return mCallbacks == null && mCallbackCollection == null; } static class SavedState extends BaseSavedState { diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java index 78eddd86..f84dceed 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableWebView.java @@ -79,114 +79,117 @@ public Parcelable onSaveInstanceState() { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - if (hasCallbacks()) { - mScrollY = t; + if (hasNoCallbacks()) { + return; + } + mScrollY = t; - dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); - if (mFirstScroll) { - mFirstScroll = false; - } + dispatchOnScrollChanged(mScrollY, mFirstScroll, mDragging); + if (mFirstScroll) { + mFirstScroll = false; + } - if (mPrevScrollY < t) { - mScrollState = ScrollState.UP; - } else if (t < mPrevScrollY) { - mScrollState = ScrollState.DOWN; - } else { - mScrollState = ScrollState.STOP; - } - mPrevScrollY = t; + if (mPrevScrollY < t) { + mScrollState = ScrollState.UP; + } else if (t < mPrevScrollY) { + mScrollState = ScrollState.DOWN; + } else { + mScrollState = ScrollState.STOP; } + mPrevScrollY = t; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { - if (hasCallbacks()) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - // Whether or not motion events are consumed by children, - // flag initializations which are related to ACTION_DOWN events should be executed. - // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are - // passed to parent (this view), the flags will be invalid. - // Also, applications might implement initialization codes to onDownMotionEvent, - // so call it here. - mFirstScroll = mDragging = true; - dispatchOnDownMotionEvent(); - break; - } + if (hasNoCallbacks()) { + return super.onInterceptTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + // Whether or not motion events are consumed by children, + // flag initializations which are related to ACTION_DOWN events should be executed. + // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are + // passed to parent (this view), the flags will be invalid. + // Also, applications might implement initialization codes to onDownMotionEvent, + // so call it here. + mFirstScroll = mDragging = true; + dispatchOnDownMotionEvent(); + break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { - if (hasCallbacks()) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mIntercepted = false; - mDragging = false; - dispatchOnUpOrCancelMotionEvent(mScrollState); - break; - case MotionEvent.ACTION_MOVE: - if (mPrevMoveEvent == null) { - mPrevMoveEvent = ev; + if (hasNoCallbacks()) { + return super.onTouchEvent(ev); + } + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mIntercepted = false; + mDragging = false; + dispatchOnUpOrCancelMotionEvent(mScrollState); + break; + case MotionEvent.ACTION_MOVE: + if (mPrevMoveEvent == null) { + mPrevMoveEvent = ev; + } + float diffY = ev.getY() - mPrevMoveEvent.getY(); + mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); + if (getCurrentScrollY() - diffY <= 0) { + // Can't scroll anymore. + + if (mIntercepted) { + // Already dispatched ACTION_DOWN event to parents, so stop here. + return false; } - float diffY = ev.getY() - mPrevMoveEvent.getY(); - mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); - if (getCurrentScrollY() - diffY <= 0) { - // Can't scroll anymore. - - if (mIntercepted) { - // Already dispatched ACTION_DOWN event to parents, so stop here. - return false; - } - - // Apps can set the interception target other than the direct parent. - final ViewGroup parent; - if (mTouchInterceptionViewGroup == null) { - parent = (ViewGroup) getParent(); - } else { - parent = mTouchInterceptionViewGroup; - } - - // Get offset to parents. If the parent is not the direct parent, - // we should aggregate offsets from all of the parents. - float offsetX = 0; - float offsetY = 0; - for (View v = this; v != null && v != parent; v = (View) v.getParent()) { - offsetX += v.getLeft() - v.getScrollX(); - offsetY += v.getTop() - v.getScrollY(); - } - final MotionEvent event = MotionEvent.obtainNoHistory(ev); - event.offsetLocation(offsetX, offsetY); - - if (parent.onInterceptTouchEvent(event)) { - mIntercepted = true; - - // If the parent wants to intercept ACTION_MOVE events, - // we pass ACTION_DOWN event to the parent - // as if these touch events just have began now. - event.setAction(MotionEvent.ACTION_DOWN); - - // Return this onTouchEvent() first and set ACTION_DOWN event for parent - // to the queue, to keep events sequence. - post(new Runnable() { - @Override - public void run() { - parent.dispatchTouchEvent(event); - } - }); - return false; - } - // Even when this can't be scrolled anymore, - // simply returning false here may cause subView's click, - // so delegate it to super. - return super.onTouchEvent(ev); + + // Apps can set the interception target other than the direct parent. + final ViewGroup parent; + if (mTouchInterceptionViewGroup == null) { + parent = (ViewGroup) getParent(); + } else { + parent = mTouchInterceptionViewGroup; } - break; - } + + // Get offset to parents. If the parent is not the direct parent, + // we should aggregate offsets from all of the parents. + float offsetX = 0; + float offsetY = 0; + for (View v = this; v != null && v != parent; v = (View) v.getParent()) { + offsetX += v.getLeft() - v.getScrollX(); + offsetY += v.getTop() - v.getScrollY(); + } + final MotionEvent event = MotionEvent.obtainNoHistory(ev); + event.offsetLocation(offsetX, offsetY); + + if (parent.onInterceptTouchEvent(event)) { + mIntercepted = true; + + // If the parent wants to intercept ACTION_MOVE events, + // we pass ACTION_DOWN event to the parent + // as if these touch events just have began now. + event.setAction(MotionEvent.ACTION_DOWN); + + // Return this onTouchEvent() first and set ACTION_DOWN event for parent + // to the queue, to keep events sequence. + post(new Runnable() { + @Override + public void run() { + parent.dispatchTouchEvent(event); + } + }); + return false; + } + // Even when this can't be scrolled anymore, + // simply returning false here may cause subView's click, + // so delegate it to super. + return super.onTouchEvent(ev); + } + break; } return super.onTouchEvent(ev); } @@ -269,8 +272,8 @@ private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { } } - private boolean hasCallbacks() { - return mCallbacks != null || mCallbackCollection != null; + private boolean hasNoCallbacks() { + return mCallbacks == null && mCallbackCollection == null; } static class SavedState extends BaseSavedState { From 145c63d72fd0ff31dbffd14e36437abb6b28aa09 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Mon, 30 Nov 2015 01:07:05 +0900 Subject: [PATCH 197/208] Add tests to assert no exceptions are thrown when there are no callbacks. --- .../test/GridViewActivityTest.java | 11 +++++++++++ .../test/ListViewActivityTest.java | 11 +++++++++++ .../test/RecyclerViewActivityTest.java | 11 +++++++++++ .../test/ScrollViewActivityTest.java | 11 +++++++++++ .../test/WebViewActivityTest.java | 11 +++++++++++ 5 files changed, 55 insertions(+) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java index 698f2af6..d8245dce 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/GridViewActivityTest.java @@ -72,6 +72,17 @@ public void run() { getInstrumentation().waitForIdleSync(); } + public void testNoCallbacks() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable = (ObservableGridView) activity.findViewById(R.id.scrollable); + scrollable.setScrollViewCallbacks(null); + } + }); + testScroll(); + } + public void testCallbacks() throws Throwable { final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2]; callbackCounter[0] = 0; diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java index 2714373c..cbf1e0b5 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ListViewActivityTest.java @@ -70,6 +70,17 @@ public void run() { getInstrumentation().waitForIdleSync(); } + public void testNoCallbacks() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable = (ObservableListView) activity.findViewById(R.id.scrollable); + scrollable.setScrollViewCallbacks(null); + } + }); + testScroll(); + } + public void testCallbacks() throws Throwable { final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2]; callbackCounter[0] = 0; diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java index 335ec8fa..73d8fa1f 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/RecyclerViewActivityTest.java @@ -71,6 +71,17 @@ public void run() { getInstrumentation().waitForIdleSync(); } + public void testNoCallbacks() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable = (ObservableRecyclerView) activity.findViewById(R.id.scrollable); + scrollable.setScrollViewCallbacks(null); + } + }); + testScroll(); + } + public void testCallbacks() throws Throwable { final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2]; callbackCounter[0] = 0; diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java index 199fb901..902154ed 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/ScrollViewActivityTest.java @@ -49,6 +49,17 @@ public void testSaveAndRestoreInstanceState() throws Throwable { testScroll(); } + public void testNoCallbacks() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable = (ObservableScrollView) activity.findViewById(R.id.scrollable); + scrollable.setScrollViewCallbacks(null); + } + }); + testScroll(); + } + public void testCallbacks() throws Throwable { final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2]; callbackCounter[0] = 0; diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java index 359929ad..96c9e8bb 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/WebViewActivityTest.java @@ -70,6 +70,17 @@ public void run() { getInstrumentation().waitForIdleSync(); } + public void testNoCallbacks() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + scrollable = (ObservableWebView) activity.findViewById(R.id.scrollable); + scrollable.setScrollViewCallbacks(null); + } + }); + testScroll(); + } + public void testCallbacks() throws Throwable { final ObservableScrollViewCallbacks[] callbacks = new ObservableScrollViewCallbacks[2]; callbackCounter[0] = 0; From f680cdf4efbe628d0d25cc65e73c7eb2e03ba66e Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Tue, 1 Dec 2015 22:58:14 +0900 Subject: [PATCH 198/208] Output test progress log for Travis CI. --- library/build.gradle | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/build.gradle b/library/build.gradle index 09d5d440..8efd210d 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -50,6 +50,18 @@ android { } } +// When testing on Travis CI, +// connectedCheck task doesn't output logs for more than 10 minutes often, +// which causes build failure. +// To avoid this, we change the log level for test tasks. +// Test tasks for buildTypes will be defined on evaluation phase, +// so do it on afterEvaluate. +afterEvaluate { project -> + tasks.withType(VerificationTask) { + logging.level = LogLevel.INFO + } +} + apply plugin: 'com.github.kt3k.coveralls' coveralls.jacocoReportPath = 'build/reports/coverage/debug/report.xml' From 3d4944d6653608e4ba0759eb0f5eef190e3f9b9a Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Wed, 2 Dec 2015 06:43:35 +0900 Subject: [PATCH 199/208] Add tests for ObservableGridView. --- .../test/HeaderGridViewActivityTest.java | 14 ++++++++++++++ .../observablescrollview/ObservableGridView.java | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java index 7b22009e..3ba7db6a 100644 --- a/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java +++ b/library/src/androidTest/java/com/github/ksoichiro/android/observablescrollview/test/HeaderGridViewActivityTest.java @@ -98,6 +98,20 @@ public void run() { activity.headerView.setClickable(true); scrollable.addHeaderView(activity.headerView); + + assertEquals(100/* items */ + 2/* header */ + 2/* footer */, hvgAdapter.getCount()); + assertEquals(1, hvgAdapter.getHeadersCount()); + assertEquals(2, hvgAdapter.getNumColumns()); + // If the header is added by addHeader(View), + // HeaderViewGridAdapter doesn't contain any associated data. + // headerData does NOT mean the view. + // If we want to get the view, we should use getView(). + assertNull(hvgAdapter.getItem(0)); + assertNull(hvgAdapter.getItem(1)); + + assertEquals(1, hvgAdapter.getFootersCount()); + assertNull(hvgAdapter.getItem(100/* items */ + 2/* header */ + 2/* footer */ - 1 - 1)); + assertNull(hvgAdapter.getItem(100/* items */ + 2/* header */ + 2/* footer */ - 1)); } }); // Scroll to bottom and try removing re-adding the footer view. diff --git a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java index 6ea47e25..8a90016d 100644 --- a/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java +++ b/library/src/main/java/com/github/ksoichiro/android/observablescrollview/ObservableGridView.java @@ -652,6 +652,10 @@ public HeaderViewGridAdapter(ArrayList headerViewInfos, ArrayList && areAllListInfosSelectable(mFooterViewInfos); } + public int getNumColumns() { + return mNumColumns; + } + public void setNumColumns(int numColumns) { if (numColumns < 1) { return; From 5776fde217a33f3430240af04a57891697c7a5b3 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sat, 5 Dec 2015 23:33:24 +0900 Subject: [PATCH 200/208] Refactor build.gradle. --- build.gradle | 12 ++++++++++++ library/build.gradle | 14 -------------- samples/build.gradle | 23 ++++++----------------- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/build.gradle b/build.gradle index 7d9fd6a0..cdfb0477 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,18 @@ allprojects { } } +subprojects { + buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.1.0' + } + } +} + apply plugin: 'com.github.ksoichiro.eclipse.aar' eclipseAar { diff --git a/library/build.gradle b/library/build.gradle index 8efd210d..68df4f51 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,19 +1,5 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.1.0' - classpath 'com.android.tools.build:gradle:1.5.0' - } -} - apply plugin: 'com.android.library' -repositories { - mavenCentral() -} - dependencies { compile 'com.android.support:recyclerview-v7:22.2.0' androidTestCompile ('com.android.support:appcompat-v7:21.0.2') { diff --git a/samples/build.gradle b/samples/build.gradle index 0ab1f509..61b4e7ef 100644 --- a/samples/build.gradle +++ b/samples/build.gradle @@ -1,22 +1,11 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' - } -} - apply plugin: 'com.android.application' -repositories { - mavenCentral() - - // for using SNAPSHOT - //maven { - // url uri('https://oss.sonatype.org/content/repositories/snapshots/') - //} -} +// for using SNAPSHOT +//repositories { +// maven { +// url uri('https://oss.sonatype.org/content/repositories/snapshots/') +// } +//} dependencies { compile 'com.android.support:appcompat-v7:22.1.1' From 8dc73b0ae9e4aa77eeee480af08d4c4171699869 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 6 Dec 2015 00:02:28 +0900 Subject: [PATCH 201/208] Update SDK/tool/dependency versions. - recyclerview-v7 - appcompat-v7 - compile SDK - build tools --- library/build.gradle | 8 ++++---- samples/build.gradle | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 68df4f51..a1bede3c 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,8 +1,8 @@ apply plugin: 'com.android.library' dependencies { - compile 'com.android.support:recyclerview-v7:22.2.0' - androidTestCompile ('com.android.support:appcompat-v7:21.0.2') { + compile 'com.android.support:recyclerview-v7:23.1.1' + androidTestCompile ('com.android.support:appcompat-v7:23.1.1') { exclude module: 'support-v4' } androidTestCompile ('com.nineoldandroids:library:2.4.0') { @@ -11,8 +11,8 @@ dependencies { } android { - compileSdkVersion 21 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.2" defaultConfig { minSdkVersion 9 diff --git a/samples/build.gradle b/samples/build.gradle index 61b4e7ef..571807b4 100644 --- a/samples/build.gradle +++ b/samples/build.gradle @@ -8,7 +8,7 @@ apply plugin: 'com.android.application' //} dependencies { - compile 'com.android.support:appcompat-v7:22.1.1' + compile 'com.android.support:appcompat-v7:23.1.1' compile 'com.nineoldandroids:library:2.4.0' compile 'com.melnykov:floatingactionbutton:1.0.7' debugCompile project(':library') @@ -23,8 +23,8 @@ apply from: "${rootDir}/gradle/version.gradle" project.ext.versionInfo.releaseVersionName = SYNCED_VERSION_NAME android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.2" defaultConfig { applicationId "com.github.ksoichiro.android.observablescrollview.samples" From 97d39359a9960aecb034ada832d8c33c33504f88 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 6 Dec 2015 16:17:11 +0900 Subject: [PATCH 202/208] Fix broken build. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ae5a595e..c5d54bae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,11 +16,12 @@ cache: - website/bower_components install: - true && ([ "$TEST_TARGET" != "website" ] || (cd website && npm install && cd ..)) -- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=build-tools-22.0.1) +- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=build-tools-23.0.2) - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=tools) - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-19) - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-21) - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-22) +- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-23) - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=sys-img-armeabi-v7a-android-19) - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=extra-android-support) - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=extra-android-m2repository) From 2202be5cf9e7a9f85991d1c3ca497ca8f30c909e Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 6 Dec 2015 16:37:13 +0900 Subject: [PATCH 203/208] Fix broken build. tools should be updated first. Also fix wercker configuration. --- .travis.yml | 2 +- wercker.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c5d54bae..6b4064ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,8 @@ cache: - website/bower_components install: - true && ([ "$TEST_TARGET" != "website" ] || (cd website && npm install && cd ..)) -- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=build-tools-23.0.2) - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=tools) +- true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=build-tools-23.0.2) - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-19) - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-21) - true && ([ "$TEST_TARGET" != "android" ] || android-update-sdk --accept-licenses='android-sdk-license-.+' --components=android-22) diff --git a/wercker.yml b/wercker.yml index e89770a0..4ea2af1b 100644 --- a/wercker.yml +++ b/wercker.yml @@ -13,7 +13,7 @@ build: echo $ANDROID_UPDATE_FILTER echo $ANDROID_NDK_HOME - android-sdk-update: - filter: tools,platform-tools,android-21,android-22,build-tools-22.0.1,extra-android-support,extra-android-m2repository + filter: tools,platform-tools,android-21,android-22,android-23,build-tools-23.0.2,extra-android-support,extra-android-m2repository # A step that executes `gradle build` command - script: name: run gradle From d0e0c9a6e60dd16ec06145391ec88937645fb165 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 6 Dec 2015 17:35:29 +0900 Subject: [PATCH 204/208] Fix wercker.yml. --- wercker.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wercker.yml b/wercker.yml index 4ea2af1b..d4fe44a7 100644 --- a/wercker.yml +++ b/wercker.yml @@ -13,7 +13,9 @@ build: echo $ANDROID_UPDATE_FILTER echo $ANDROID_NDK_HOME - android-sdk-update: - filter: tools,platform-tools,android-21,android-22,android-23,build-tools-23.0.2,extra-android-support,extra-android-m2repository + filter: tools,platform-tools + - android-sdk-update: + filter: android-21,android-22,android-23,build-tools-23.0.2,extra-android-support,extra-android-m2repository # A step that executes `gradle build` command - script: name: run gradle From d16939345eb03cbde5ca34380ed8f252d292f9ed Mon Sep 17 00:00:00 2001 From: YongJun Kim Date: Mon, 25 Jan 2016 15:45:46 +0900 Subject: [PATCH 205/208] Update README.md Fix GooglePlay download's link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 16942c72..eae57d74 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ It's easy to interact with the Toolbar introduced in Android 5.0 Lollipop and m ### Download from Google Play -[![Get it on Google Play](https://developer.android.com/images/brand/en_generic_rgb_wo_45.png)](https://play.google.com/store/apps/details?id=com.github.ksoichiro.android.observablescrollview.samples2) +
      Get it on Google Play Please note that the app on the Play store is not always the latest version. From bee7c0b8300b2ff8f72546a3a953f24bd9c596bb Mon Sep 17 00:00:00 2001 From: Joowon Ryoo Date: Thu, 28 Jan 2016 00:18:42 +0900 Subject: [PATCH 206/208] Typo Fix Remove typo error ':' --- docs/quick-start/animation.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/quick-start/animation.md b/docs/quick-start/animation.md index 979ab1e1..670c6f19 100644 --- a/docs/quick-start/animation.md +++ b/docs/quick-start/animation.md @@ -24,7 +24,6 @@ Add some initialization codes to `onCreate()`: ```java @Override protected void onCreate(Bundle savedInstanceState) { - : ObservableListView listView = (ObservableListView) findViewById(R.id.list); listView.setScrollViewCallbacks(this); } From 177cce1acc7b5929d07cdef6c38754737a8f4245 Mon Sep 17 00:00:00 2001 From: libtastic Date: Fri, 29 Jan 2016 20:15:41 -0500 Subject: [PATCH 207/208] Popular Android library badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index eae57d74..3f3b1bd1 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ and [the documentation](docs/overview.md) for further more. * [FAQ](docs/faq.md) ## Apps that use this library +[![Badge](http://www.libtastic.com/static/osbadges/4.png)](http://www.libtastic.com/technology/4/) * [Jair Player](https://play.google.com/store/apps/details?id=aj.jair.music) by Akshay Chordiya * [My Gradle](https://play.google.com/store/apps/details?id=se.project.generic.mygradle) by Erick Chavez Alcarraz From 47a5fb2db5e93d923a8c6772cde48bbb7d932345 Mon Sep 17 00:00:00 2001 From: Soichiro Kashima Date: Sun, 14 Feb 2016 19:02:12 +0900 Subject: [PATCH 208/208] Add "{Soft} Skills" by Fanatic Devs to the "Apps that use this library" list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3f3b1bd1..a7887ccb 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ and [the documentation](docs/overview.md) for further more. * [Jair Player](https://play.google.com/store/apps/details?id=aj.jair.music) by Akshay Chordiya * [My Gradle](https://play.google.com/store/apps/details?id=se.project.generic.mygradle) by Erick Chavez Alcarraz * [ThemeDIY](https://play.google.com/store/apps/details?id=net.darkion.theme.maker) by Darkion Avey +* [{Soft} Skills](https://play.google.com/store/apps/details?id=com.fanaticdevs.androider) by Fanatic Devs If you're using this library in your app and you'd like to list it here, please let me know via [email](mailto:soichiro.kashima@gmail.com) or [pull requests](https://github.com/ksoichiro/Android-ObservableScrollView/pulls) or [issues](https://github.com/ksoichiro/Android-ObservableScrollView/issues).