diff --git a/README.md b/README.md index a8f3e3e..77748c8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ It is similar to a ViewPager, but you can quickly and painlessly create layout, ## Gradle Add this into your dependencies block. ``` -compile 'com.yarolegovich:discrete-scrollview:1.4.9' +compile 'com.yarolegovich:discrete-scrollview:1.5.1' ``` ## Reporting an issue @@ -129,6 +129,12 @@ int getRealPosition(int position); int getClosestPosition(int position); ``` Currently `InfiniteScrollAdapter` handles data set changes inefficiently, so your contributions are welcome. +#### Disabling scroll +It's possible to forbid user scroll in any or specific direction using: +```java +scrollView.setScrollConfig(config); +``` +Where `config` is an instance of `DSVScrollConfig` enum. The default value enables scroll in any direction. #### Callbacks * Scroll state changes: ```java diff --git a/build.gradle b/build.gradle index 0519ee4..66fd81e 100644 --- a/build.gradle +++ b/build.gradle @@ -4,8 +4,8 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' - classpath 'com.novoda:bintray-release:0.8.0' + classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5' } } @@ -13,6 +13,7 @@ allprojects { repositories { jcenter() maven { url "https://maven.google.com" } + maven { url "https://jitpack.io" } google() } } @@ -22,16 +23,27 @@ task clean(type: Delete) { } ext { - userOrg = 'yarolegovich' - groupId = 'com.yarolegovich' - uploadName = 'DiscreteScrollView' - description = 'A scrollable list of items that centers the current element and provides easy-to-use APIs for cool item animations.' - publishVersion = '1.4.9' - licences = ['Apache-2.0'] + compileSdkVersion = 29 + buildToolsVersion = '29.0.2' + targetSdkVersion = 29 - compileSdkVersion = 27 - buildToolsVersion = '26.0.2' - targetSdkVersion = 27 + deps = [ + recycler : 'androidx.recyclerview:recyclerview:1.0.0', + designSupport : 'com.google.android.material:material:1.0.0', + annotations : 'androidx.annotation:annotation:1.1.0', + androidxCompat: 'androidx.appcompat:appcompat:1.1.0', + glide : 'com.github.bumptech.glide:glide:4.11.0', + materialPrefs : 'com.yarolegovich:mp:1.1.6' + ] - supportLibVersion = '27.1.1' + testDeps = [ + hamcrest : 'org.hamcrest:hamcrest-library:1.3', + mockito : 'org.mockito:mockito-core:2.13.0', + jUnit : 'junit:junit:4.13', + robolectric : 'org.robolectric:robolectric:3.0', + espresso : 'androidx.test.espresso:espresso-core:3.1.0', + androidJUnit: 'androidx.test.ext:junit:1.1.1', + testRules : 'androidx.test:rules:1.1.1', + testRunner : 'androidx.test:runner:1.1.1' + ] } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index aac7c9b..a28ad2d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,17 +1,3 @@ -# Project-wide Gradle settings. +android.useAndroidX=true -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true +org.gradle.jvmargs=-Xmx1536m \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b99b98f..b670cfc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Feb 03 15:44:30 EET 2018 +#Thu Jul 30 09:08:49 EEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/library/build.gradle b/library/build.gradle index 99d325c..8daa89d 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,5 +1,5 @@ apply plugin: 'com.android.library' -apply plugin: 'com.novoda.bintray-release' +apply from: rootProject.file('release-bintray.gradle') android { compileSdkVersion rootProject.compileSdkVersion @@ -11,30 +11,23 @@ android { versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } } dependencies { - implementation "com.android.support:appcompat-v7:$supportLibVersion" - implementation "com.android.support:recyclerview-v7:$supportLibVersion" + implementation deps.recycler + implementation deps.annotations - testImplementation 'org.robolectric:robolectric:3.0' - testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.13.0' - testImplementation 'org.hamcrest:hamcrest-library:1.3' + testImplementation testDeps.robolectric + testImplementation testDeps.jUnit + testImplementation testDeps.mockito + testImplementation testDeps.hamcrest - androidTestImplementation "com.android.support:appcompat-v7:$supportLibVersion" - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' - androidTestImplementation 'org.hamcrest:hamcrest-library:1.3' -} - -publish { - artifactId = 'discrete-scrollview' - userOrg = rootProject.userOrg - groupId = rootProject.groupId - uploadName = rootProject.uploadName - publishVersion = rootProject.publishVersion - description = rootProject.description - licences = rootProject.licences + debugImplementation deps.androidxCompat + androidTestImplementation testDeps.espresso + androidTestImplementation testDeps.androidJUnit + androidTestImplementation testDeps.testRunner + androidTestImplementation testDeps.testRules + androidTestImplementation testDeps.hamcrest } \ No newline at end of file diff --git a/library/src/androidTest/AndroidManifest.xml b/library/src/androidTest/AndroidManifest.xml index 403a802..0d29d87 100644 --- a/library/src/androidTest/AndroidManifest.xml +++ b/library/src/androidTest/AndroidManifest.xml @@ -1,13 +1,13 @@ + package="com.yarolegovich.discretescrollview"> - + - + diff --git a/library/src/androidTest/java/com/yarolegovich/discretescrollview/DataSetModificationTest.java b/library/src/androidTest/java/com/yarolegovich/discretescrollview/DataSetModificationTest.java index 8640f10..cf22b23 100644 --- a/library/src/androidTest/java/com/yarolegovich/discretescrollview/DataSetModificationTest.java +++ b/library/src/androidTest/java/com/yarolegovich/discretescrollview/DataSetModificationTest.java @@ -1,6 +1,7 @@ package com.yarolegovich.discretescrollview; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.yarolegovich.discretescrollview.context.TestData; @@ -10,10 +11,12 @@ import java.util.ArrayList; import java.util.List; -import static android.support.test.espresso.matcher.ViewMatchers.*; +import static androidx.test.espresso.matcher.ViewMatchers.assertThat; import static com.yarolegovich.discretescrollview.custom.CustomAssertions.currentPositionIs; import static com.yarolegovich.discretescrollview.custom.CustomAssertions.doesNotHaveChildren; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; /** * Created by yarolegovich on 2/3/18. @@ -214,9 +217,7 @@ public void notifyDataSetChanged_currentItemGoesOutsideItemRange_currentIsClampe @Override public void run() { List data = adapter.getData(); - for (int i = numOfItemsToRemove - 1; i >= 0; i--) { - data.remove(i); - } + data.subList(0, numOfItemsToRemove).clear(); adapter.notifyDataSetChanged(); } }); @@ -264,8 +265,8 @@ public void notifyDataSetChanged_scrollToPositionCalledAfterItemsRemoved_positio public void run() { List data = adapter.getData(); final int itemsToRemove = data.size() / 2; - for (int i = 0; i < itemsToRemove; i++) { - data.remove(0); + if (itemsToRemove > 0) { + data.subList(0, itemsToRemove).clear(); } adapter.notifyDataSetChanged(); scrollView.scrollToPosition(targetPosition); diff --git a/library/src/androidTest/java/com/yarolegovich/discretescrollview/DiscreteScrollViewTest.java b/library/src/androidTest/java/com/yarolegovich/discretescrollview/DiscreteScrollViewTest.java index 5a34111..5c12209 100644 --- a/library/src/androidTest/java/com/yarolegovich/discretescrollview/DiscreteScrollViewTest.java +++ b/library/src/androidTest/java/com/yarolegovich/discretescrollview/DiscreteScrollViewTest.java @@ -1,16 +1,14 @@ package com.yarolegovich.discretescrollview; -import android.os.Handler; -import android.os.Looper; -import android.support.annotation.CallSuper; -import android.support.test.espresso.Espresso; -import android.support.test.espresso.IdlingRegistry; -import android.support.test.espresso.IdlingResource; -import android.support.test.espresso.ViewInteraction; -import android.support.test.espresso.idling.CountingIdlingResource; -import android.support.test.rule.ActivityTestRule; import android.view.View; +import androidx.annotation.CallSuper; +import androidx.test.espresso.Espresso; +import androidx.test.espresso.IdlingRegistry; +import androidx.test.espresso.IdlingResource; +import androidx.test.espresso.ViewInteraction; +import androidx.test.rule.ActivityTestRule; + import com.yarolegovich.discretescrollview.context.TestActivity; import com.yarolegovich.discretescrollview.context.TestAdapter; @@ -22,7 +20,6 @@ import java.util.List; import static com.yarolegovich.discretescrollview.custom.CustomAssertions.currentPositionIs; -import static org.junit.Assert.assertThat; /** * Created by yarolegovich on 2/3/18. diff --git a/library/src/androidTest/java/com/yarolegovich/discretescrollview/ScrollFunctionalityTest.java b/library/src/androidTest/java/com/yarolegovich/discretescrollview/ScrollFunctionalityTest.java index 1ff4561..4cadeda 100644 --- a/library/src/androidTest/java/com/yarolegovich/discretescrollview/ScrollFunctionalityTest.java +++ b/library/src/androidTest/java/com/yarolegovich/discretescrollview/ScrollFunctionalityTest.java @@ -1,11 +1,11 @@ package com.yarolegovich.discretescrollview; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; -import static android.support.test.espresso.matcher.ViewMatchers.assertThat; +import static androidx.test.espresso.matcher.ViewMatchers.assertThat; import static com.yarolegovich.discretescrollview.custom.CustomAssertions.currentPositionIs; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; diff --git a/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestActivity.java b/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestActivity.java index cb1eacb..aa517f5 100644 --- a/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestActivity.java +++ b/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestActivity.java @@ -1,26 +1,23 @@ package com.yarolegovich.discretescrollview.context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.test.espresso.IdlingResource; -import android.support.test.espresso.idling.CountingIdlingResource; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.RecyclerView; -import android.util.Log; import android.view.Gravity; import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; +import androidx.test.espresso.IdlingResource; +import androidx.test.espresso.idling.CountingIdlingResource; + import com.yarolegovich.discretescrollview.DiscreteScrollView; import com.yarolegovich.discretescrollview.R; -import com.yarolegovich.discretescrollview.transform.ScaleTransformer; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; @@ -29,7 +26,7 @@ * Created by yarolegovich on 2/4/18. */ -public class TestActivity extends AppCompatActivity implements DiscreteScrollView.ScrollStateChangeListener { +public class TestActivity extends AppCompatActivity implements DiscreteScrollView.ScrollStateChangeListener { private DiscreteScrollView scrollView; private TestAdapter adapter; diff --git a/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestAdapter.java b/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestAdapter.java index 8db2f28..26665fa 100644 --- a/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestAdapter.java +++ b/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestAdapter.java @@ -1,17 +1,13 @@ package com.yarolegovich.discretescrollview.context; -import android.content.Context; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.support.v7.widget.RecyclerView; -import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; -import java.util.List; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import java.util.List; /** * Created by yarolegovich on 2/4/18. @@ -27,11 +23,12 @@ public TestAdapter(List data) { } @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); this.recyclerView = recyclerView; } + @NonNull @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { float dp = parent.getResources().getDisplayMetrics().density; diff --git a/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestData.java b/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestData.java index b04686e..446eaf5 100644 --- a/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestData.java +++ b/library/src/androidTest/java/com/yarolegovich/discretescrollview/context/TestData.java @@ -3,7 +3,8 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.support.annotation.ColorInt; + +import androidx.annotation.ColorInt; import java.util.Random; @@ -23,7 +24,8 @@ public TestData() { image = new ColorDrawable(generateRandomColor()); } - private static @ColorInt int generateRandomColor() { + private static @ColorInt + int generateRandomColor() { return Color.argb(255, random.nextInt(256), random.nextInt(256), diff --git a/library/src/androidTest/java/com/yarolegovich/discretescrollview/custom/CustomAssertions.java b/library/src/androidTest/java/com/yarolegovich/discretescrollview/custom/CustomAssertions.java index 8e65e31..99af895 100644 --- a/library/src/androidTest/java/com/yarolegovich/discretescrollview/custom/CustomAssertions.java +++ b/library/src/androidTest/java/com/yarolegovich/discretescrollview/custom/CustomAssertions.java @@ -1,16 +1,16 @@ package com.yarolegovich.discretescrollview.custom; -import android.support.test.espresso.NoMatchingViewException; -import android.support.test.espresso.ViewAssertion; -import android.support.v7.widget.RecyclerView; -import android.util.Log; import android.view.View; import android.view.ViewGroup; +import androidx.recyclerview.widget.RecyclerView; +import androidx.test.espresso.NoMatchingViewException; +import androidx.test.espresso.ViewAssertion; + import com.yarolegovich.discretescrollview.DiscreteScrollView; import static org.hamcrest.Matchers.*; -import static android.support.test.espresso.matcher.ViewMatchers.*; +import static androidx.test.espresso.matcher.ViewMatchers.*; /** diff --git a/library/src/main/java/com/yarolegovich/discretescrollview/DSVOrientation.java b/library/src/main/java/com/yarolegovich/discretescrollview/DSVOrientation.java index d00779e..8f7d7dc 100644 --- a/library/src/main/java/com/yarolegovich/discretescrollview/DSVOrientation.java +++ b/library/src/main/java/com/yarolegovich/discretescrollview/DSVOrientation.java @@ -42,7 +42,7 @@ interface Helper { void offsetChildren(int amount, RecyclerViewProxy lm); - float getDistanceFromCenter(Point center, int viewCenterX, int viewCenterY); + float getDistanceFromCenter(Point center, float viewCenterX, float viewCenterY); boolean isViewVisible(Point center, int halfWidth, int halfHeight, int endBound, int extraSpace); @@ -104,7 +104,7 @@ public void offsetChildren(int amount, RecyclerViewProxy helper) { } @Override - public float getDistanceFromCenter(Point center, int viewCenterX, int viewCenterY) { + public float getDistanceFromCenter(Point center, float viewCenterX, float viewCenterY) { return viewCenterX - center.x; } @@ -165,7 +165,7 @@ public void offsetChildren(int amount, RecyclerViewProxy helper) { } @Override - public float getDistanceFromCenter(Point center, int viewCenterX, int viewCenterY) { + public float getDistanceFromCenter(Point center, float viewCenterX, float viewCenterY) { return viewCenterY - center.y; } diff --git a/library/src/main/java/com/yarolegovich/discretescrollview/DSVScrollConfig.java b/library/src/main/java/com/yarolegovich/discretescrollview/DSVScrollConfig.java new file mode 100644 index 0000000..f6c743b --- /dev/null +++ b/library/src/main/java/com/yarolegovich/discretescrollview/DSVScrollConfig.java @@ -0,0 +1,30 @@ +package com.yarolegovich.discretescrollview; + +public enum DSVScrollConfig { + ENABLED { + @Override + boolean isScrollBlocked(Direction direction) { + return false; + } + }, + FORWARD_ONLY { + @Override + boolean isScrollBlocked(Direction direction) { + return direction == Direction.START; + } + }, + BACKWARD_ONLY { + @Override + boolean isScrollBlocked(Direction direction) { + return direction == Direction.END; + } + }, + DISABLED { + @Override + boolean isScrollBlocked(Direction direction) { + return true; + } + }; + + abstract boolean isScrollBlocked(Direction direction); +} diff --git a/library/src/main/java/com/yarolegovich/discretescrollview/Direction.java b/library/src/main/java/com/yarolegovich/discretescrollview/Direction.java index e4d1c38..d3fcf49 100644 --- a/library/src/main/java/com/yarolegovich/discretescrollview/Direction.java +++ b/library/src/main/java/com/yarolegovich/discretescrollview/Direction.java @@ -15,6 +15,11 @@ public int applyTo(int delta) { public boolean sameAs(int direction) { return direction < 0; } + + @Override + public Direction reverse() { + return Direction.END; + } }, END { @Override @@ -26,12 +31,19 @@ public int applyTo(int delta) { public boolean sameAs(int direction) { return direction > 0; } + + @Override + public Direction reverse() { + return Direction.START; + } }; public abstract int applyTo(int delta); public abstract boolean sameAs(int direction); + public abstract Direction reverse(); + public static Direction fromDelta(int delta) { return delta > 0 ? END : START; } diff --git a/library/src/main/java/com/yarolegovich/discretescrollview/DiscreteScrollLayoutManager.java b/library/src/main/java/com/yarolegovich/discretescrollview/DiscreteScrollLayoutManager.java index be81625..c22fba6 100644 --- a/library/src/main/java/com/yarolegovich/discretescrollview/DiscreteScrollLayoutManager.java +++ b/library/src/main/java/com/yarolegovich/discretescrollview/DiscreteScrollLayoutManager.java @@ -5,17 +5,16 @@ import android.graphics.PointF; import android.os.Bundle; import android.os.Parcelable; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.view.accessibility.AccessibilityEventCompat; -import android.support.v4.view.accessibility.AccessibilityRecordCompat; -import android.support.v7.widget.LinearSmoothScroller; -import android.support.v7.widget.RecyclerView; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearSmoothScroller; +import androidx.recyclerview.widget.RecyclerView; + import com.yarolegovich.discretescrollview.transform.DiscreteScrollItemTransformer; import java.util.Locale; @@ -23,7 +22,7 @@ /** * Created by yarolegovich on 17.02.2017. */ -class DiscreteScrollLayoutManager extends RecyclerView.LayoutManager { +public class DiscreteScrollLayoutManager extends RecyclerView.LayoutManager { static final int NO_POSITION = -1; @@ -69,6 +68,9 @@ class DiscreteScrollLayoutManager extends RecyclerView.LayoutManager { private int viewWidth, viewHeight; + @NonNull + private DSVScrollConfig scrollConfig = DSVScrollConfig.ENABLED; + @NonNull private final ScrollStateListener scrollStateListener; private DiscreteScrollItemTransformer itemTransformer; @@ -255,7 +257,7 @@ protected void recycleDetachedViewsAndClearCache(RecyclerView.Recycler recycler) } @Override - public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) { + public void onItemsAdded(@NonNull RecyclerView recyclerView, int positionStart, int itemCount) { int newPosition = currentPosition; if (currentPosition == NO_POSITION) { newPosition = 0; @@ -266,7 +268,7 @@ public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemC } @Override - public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { + public void onItemsRemoved(@NonNull RecyclerView recyclerView, int positionStart, int itemCount) { int newPosition = currentPosition; if (recyclerViewProxy.getItemCount() == 0) { newPosition = NO_POSITION; @@ -281,7 +283,7 @@ public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int ite } @Override - public void onItemsChanged(RecyclerView recyclerView) { + public void onItemsChanged(@NonNull RecyclerView recyclerView) { //notifyDataSetChanged() was called. We need to ensure that currentPosition is not out of bounds currentPosition = Math.min(Math.max(0, currentPosition), recyclerViewProxy.getItemCount() - 1); dataSetChangeShiftedPosition = true; @@ -450,6 +452,12 @@ private void onDragStart() { pendingScroll = 0; } + public boolean isFlingDisallowed(int velocityX, int velocityY) { + int velocity = orientationHelper.getFlingVelocity(velocityX, velocityY); + Direction direction = Direction.fromDelta(velocity); + return scrollConfig.isScrollBlocked(direction); + } + public void onFling(int velocityX, int velocityY) { int velocity = orientationHelper.getFlingVelocity(velocityX, velocityY); int throttleValue = shouldSlideOnFling ? Math.abs(velocity / flingThreshold) : 1; @@ -475,6 +483,11 @@ protected int calculateAllowedScrollIn(Direction direction) { if (pendingScroll != 0) { return Math.abs(pendingScroll); } + if (currentScrollState == RecyclerView.SCROLL_STATE_DRAGGING) { + if (scrollConfig.isScrollBlocked(direction)) { + return direction.reverse().applyTo(scrolled); + } + } int allowedScroll; boolean isBoundReached; boolean isScrollDirectionAsBefore = direction.applyTo(scrolled) > 0; @@ -518,32 +531,32 @@ public boolean isAutoMeasureEnabled() { } @Override - public int computeVerticalScrollRange(RecyclerView.State state) { + public int computeVerticalScrollRange(@NonNull RecyclerView.State state) { return computeScrollRange(state); } @Override - public int computeVerticalScrollOffset(RecyclerView.State state) { + public int computeVerticalScrollOffset(@NonNull RecyclerView.State state) { return computeScrollOffset(state); } @Override - public int computeVerticalScrollExtent(RecyclerView.State state) { + public int computeVerticalScrollExtent(@NonNull RecyclerView.State state) { return computeScrollExtent(state); } @Override - public int computeHorizontalScrollRange(RecyclerView.State state) { + public int computeHorizontalScrollRange(@NonNull RecyclerView.State state) { return computeScrollRange(state); } @Override - public int computeHorizontalScrollOffset(RecyclerView.State state) { + public int computeHorizontalScrollOffset(@NonNull RecyclerView.State state) { return computeScrollOffset(state); } @Override - public int computeHorizontalScrollExtent(RecyclerView.State state) { + public int computeHorizontalScrollExtent(@NonNull RecyclerView.State state) { return computeScrollExtent(state); } @@ -562,10 +575,10 @@ private int computeScrollExtent(RecyclerView.State state) { } private int computeScrollRange(RecyclerView.State state) { - if (getItemCount() == 0) { + if (state.getItemCount() == 0) { return 0; } else { - return scrollToChangeCurrent * (getItemCount() - 1); + return scrollToChangeCurrent * (state.getItemCount() - 1); } } @@ -647,24 +660,29 @@ public void setSlideOnFlingThreshold(int threshold) { flingThreshold = threshold; } + public void setScrollConfig(@NonNull DSVScrollConfig config) { + scrollConfig = config; + } + public int getCurrentPosition() { return currentPosition; } @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + public void onInitializeAccessibilityEvent(@NonNull AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); if (recyclerViewProxy.getChildCount() > 0) { - final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event); - record.setFromIndex(getPosition(getFirstChild())); - record.setToIndex(getPosition(getLastChild())); + event.setFromIndex(getPosition(getFirstChild())); + event.setToIndex(getPosition(getLastChild())); } } private float getCenterRelativePositionOf(View v, int maxDistance) { + float childCenterX = getDecoratedLeft(v) + v.getWidth() * 0.5f; + float childCenterY = getDecoratedTop(v) + v.getHeight() * 0.5f; float distanceFromCenter = orientationHelper.getDistanceFromCenter(recyclerCenter, - getDecoratedLeft(v) + childHalfWidth, - getDecoratedTop(v) + childHalfHeight); + childCenterX, + childCenterY); return Math.min(Math.max(-1f, distanceFromCenter / maxDistance), 1f); } diff --git a/library/src/main/java/com/yarolegovich/discretescrollview/DiscreteScrollView.java b/library/src/main/java/com/yarolegovich/discretescrollview/DiscreteScrollView.java index 2e09c97..560e5d0 100644 --- a/library/src/main/java/com/yarolegovich/discretescrollview/DiscreteScrollView.java +++ b/library/src/main/java/com/yarolegovich/discretescrollview/DiscreteScrollView.java @@ -2,13 +2,14 @@ import android.content.Context; import android.content.res.TypedArray; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.View; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + import com.yarolegovich.discretescrollview.transform.DiscreteScrollItemTransformer; import com.yarolegovich.discretescrollview.util.ScrollListenerAdapter; @@ -18,7 +19,7 @@ /** * Created by yarolegovich on 18.02.2017. */ -@SuppressWarnings("unchecked") +@SuppressWarnings({"unchecked", "rawtypes"}) public class DiscreteScrollView extends RecyclerView { public static final int NO_POSITION = DiscreteScrollLayoutManager.NO_POSITION; @@ -29,6 +30,12 @@ public class DiscreteScrollView extends RecyclerView { private List scrollStateChangeListeners; private List onItemChangedListeners; + private Runnable notifyItemChangedRunnable = new Runnable() { + @Override + public void run() { + notifyCurrentItemChanged(); + } + }; private boolean isOverScrollEnabled; @@ -78,6 +85,9 @@ public void setLayoutManager(LayoutManager layout) { @Override public boolean fling(int velocityX, int velocityY) { + if (layoutManager.isFlingDisallowed(velocityX, velocityY)) { + return false; + } boolean isFling = super.fling(velocityX, velocityY); if (isFling) { layoutManager.onFling(velocityX, velocityY); @@ -93,6 +103,15 @@ public ViewHolder getViewHolder(int position) { return view != null ? getChildViewHolder(view) : null; } + @Override + public void scrollToPosition(int position) { + int currentPosition = layoutManager.getCurrentPosition(); + super.scrollToPosition(position); + if (currentPosition != position) { + notifyCurrentItemChanged(); + } + } + /** * @return adapter position of the current item or -1 if nothing is selected */ @@ -124,6 +143,10 @@ public void setOffscreenItems(int items) { layoutManager.setOffscreenItems(items); } + public void setScrollConfig(@NonNull DSVScrollConfig config) { + layoutManager.setScrollConfig(config); + } + public void setClampTransformProgressAfter(@IntRange(from = 1) int itemCount) { if (itemCount <= 1) { throw new IllegalArgumentException("must be >= 1"); @@ -189,12 +212,17 @@ private void notifyCurrentItemChanged(ViewHolder holder, int current) { } private void notifyCurrentItemChanged() { + removeCallbacks(notifyItemChangedRunnable); if (onItemChangedListeners.isEmpty()) { return; } int current = layoutManager.getCurrentPosition(); ViewHolder currentHolder = getViewHolder(current); - notifyCurrentItemChanged(currentHolder, current); + if (currentHolder == null) { + post(notifyItemChangedRunnable); + } else { + notifyCurrentItemChanged(currentHolder, current); + } } private class ScrollStateListener implements DiscreteScrollLayoutManager.ScrollStateListener { @@ -208,6 +236,7 @@ public void onIsBoundReachedFlagChange(boolean isBoundReached) { @Override public void onScrollStart() { + removeCallbacks(notifyItemChangedRunnable); if (scrollStateChangeListeners.isEmpty()) { return; } @@ -248,12 +277,7 @@ public void onScroll(float currentViewPosition) { @Override public void onCurrentViewFirstLayout() { - post(new Runnable() { - @Override - public void run() { - notifyCurrentItemChanged(); - } - }); + notifyCurrentItemChanged(); } @Override diff --git a/library/src/main/java/com/yarolegovich/discretescrollview/InfiniteScrollAdapter.java b/library/src/main/java/com/yarolegovich/discretescrollview/InfiniteScrollAdapter.java index dacf70c..803efd5 100644 --- a/library/src/main/java/com/yarolegovich/discretescrollview/InfiniteScrollAdapter.java +++ b/library/src/main/java/com/yarolegovich/discretescrollview/InfiniteScrollAdapter.java @@ -1,9 +1,10 @@ package com.yarolegovich.discretescrollview; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + import java.util.Locale; /** diff --git a/library/src/main/java/com/yarolegovich/discretescrollview/RecyclerViewProxy.java b/library/src/main/java/com/yarolegovich/discretescrollview/RecyclerViewProxy.java index 32d5309..7aac486 100644 --- a/library/src/main/java/com/yarolegovich/discretescrollview/RecyclerViewProxy.java +++ b/library/src/main/java/com/yarolegovich/discretescrollview/RecyclerViewProxy.java @@ -1,11 +1,11 @@ package com.yarolegovich.discretescrollview; -import android.support.annotation.NonNull; -import android.support.v7.widget.LinearSmoothScroller; -import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + /** * Created by yarolegovich on 10/25/17. */ diff --git a/library/src/main/java/com/yarolegovich/discretescrollview/transform/Pivot.java b/library/src/main/java/com/yarolegovich/discretescrollview/transform/Pivot.java index a97f8ff..ff2ae5e 100644 --- a/library/src/main/java/com/yarolegovich/discretescrollview/transform/Pivot.java +++ b/library/src/main/java/com/yarolegovich/discretescrollview/transform/Pivot.java @@ -1,8 +1,9 @@ package com.yarolegovich.discretescrollview.transform; -import android.support.annotation.IntDef; import android.view.View; +import androidx.annotation.IntDef; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/library/src/main/java/com/yarolegovich/discretescrollview/transform/ScaleTransformer.java b/library/src/main/java/com/yarolegovich/discretescrollview/transform/ScaleTransformer.java index a42c313..2aa7069 100644 --- a/library/src/main/java/com/yarolegovich/discretescrollview/transform/ScaleTransformer.java +++ b/library/src/main/java/com/yarolegovich/discretescrollview/transform/ScaleTransformer.java @@ -1,8 +1,9 @@ package com.yarolegovich.discretescrollview.transform; -import android.support.annotation.FloatRange; import android.view.View; +import androidx.annotation.FloatRange; + /** * Created by yarolegovich on 03.03.2017. */ diff --git a/library/src/main/java/com/yarolegovich/discretescrollview/util/ScrollListenerAdapter.java b/library/src/main/java/com/yarolegovich/discretescrollview/util/ScrollListenerAdapter.java index f47d7df..11ad976 100644 --- a/library/src/main/java/com/yarolegovich/discretescrollview/util/ScrollListenerAdapter.java +++ b/library/src/main/java/com/yarolegovich/discretescrollview/util/ScrollListenerAdapter.java @@ -1,8 +1,9 @@ package com.yarolegovich.discretescrollview.util; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.widget.RecyclerView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; import com.yarolegovich.discretescrollview.DiscreteScrollView; @@ -37,7 +38,7 @@ public void onScroll(float scrollPosition, @Override public boolean equals(Object obj) { if (obj instanceof ScrollListenerAdapter) { - return adaptee.equals(((ScrollListenerAdapter) obj).adaptee); + return adaptee.equals(((ScrollListenerAdapter) obj).adaptee); } else { return super.equals(obj); } diff --git a/library/src/test/java/com/yarolegovich/discretescrollview/DiscreteScrollLayoutManagerTest.java b/library/src/test/java/com/yarolegovich/discretescrollview/DiscreteScrollLayoutManagerTest.java index f60ddcc..e11ba43 100644 --- a/library/src/test/java/com/yarolegovich/discretescrollview/DiscreteScrollLayoutManagerTest.java +++ b/library/src/test/java/com/yarolegovich/discretescrollview/DiscreteScrollLayoutManagerTest.java @@ -1,8 +1,9 @@ package com.yarolegovich.discretescrollview; -import android.support.v7.widget.RecyclerView; import android.view.View; +import androidx.recyclerview.widget.RecyclerView; + import com.yarolegovich.discretescrollview.stub.StubRecyclerViewProxy; import org.junit.Before; diff --git a/library/src/test/java/com/yarolegovich/discretescrollview/stub/StubRecyclerViewProxy.java b/library/src/test/java/com/yarolegovich/discretescrollview/stub/StubRecyclerViewProxy.java index bb5c3ca..eb72168 100644 --- a/library/src/test/java/com/yarolegovich/discretescrollview/stub/StubRecyclerViewProxy.java +++ b/library/src/test/java/com/yarolegovich/discretescrollview/stub/StubRecyclerViewProxy.java @@ -1,9 +1,10 @@ package com.yarolegovich.discretescrollview.stub; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; import android.view.View; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + import com.yarolegovich.discretescrollview.RecyclerViewProxy; import java.util.ArrayList; diff --git a/release-bintray.gradle b/release-bintray.gradle new file mode 100644 index 0000000..ec2206a --- /dev/null +++ b/release-bintray.gradle @@ -0,0 +1,60 @@ +apply plugin: 'maven-publish' +apply plugin: 'com.jfrog.bintray' + +def upload = [ + user : 'yarolegovich', + artifactId : 'discrete-scrollview', + userOrg : 'yarolegovich', + repository : 'maven', + groupId : 'com.yarolegovich', + uploadName : 'DiscreteScrollView', + description: 'A scrollable list of items that centers the current element and provides easy-to-use APIs for cool item animations.', + version : '1.5.1', + licences : ['Apache-2.0'] +] + +task androidSourcesJar(type: Jar) { + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs +} + +version upload.version + +afterEvaluate { + + publishing { + publications { + LibRelease(MavenPublication) { + from components.release + + artifact androidSourcesJar + + artifactId upload.artifactId + groupId upload.groupId + version upload.version + } + } + } + + Properties localProps = new Properties() + localProps.load(project.rootProject.file('local.properties').newDataInputStream()) + + bintray { + user = upload.user + key = localProps.getProperty('bintray.api_key') + publications = ['LibRelease'] + configurations = ['archives'] + pkg { + name = upload.uploadName + repo = upload.repository + userOrg = upload.userOrg + licenses = upload.licences + publish = true + dryRun = false + version { + name = upload.version + desc = upload.description + } + } + } +} \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index 25e1b00..f684808 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -21,13 +21,10 @@ android { } dependencies { - implementation "com.android.support:appcompat-v7:$supportLibVersion" - implementation "com.android.support:cardview-v7:$supportLibVersion" - implementation "com.android.support:design:$supportLibVersion" - - implementation 'com.github.bumptech.glide:glide:3.7.0' - - implementation 'com.yarolegovich:mp:1.0.9' + implementation deps.designSupport + implementation deps.annotations + implementation deps.glide + implementation deps.materialPrefs implementation project(':library') } diff --git a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/DiscreteScrollViewOptions.java b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/DiscreteScrollViewOptions.java index c693801..6e6f894 100644 --- a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/DiscreteScrollViewOptions.java +++ b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/DiscreteScrollViewOptions.java @@ -4,13 +4,14 @@ import android.content.DialogInterface; import android.content.SharedPreferences; import android.preference.PreferenceManager; -import android.support.design.widget.BottomSheetDialog; -import android.support.v7.widget.PopupMenu; -import android.support.v7.widget.RecyclerView; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import androidx.appcompat.widget.PopupMenu; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.bottomsheet.BottomSheetDialog; import com.yarolegovich.discretescrollview.DiscreteScrollView; import com.yarolegovich.discretescrollview.InfiniteScrollAdapter; @@ -45,22 +46,25 @@ public void onDismiss(DialogInterface dialog) { defaultPrefs().unregisterOnSharedPreferenceChangeListener(timeChangeListener); } }); - bsd.findViewById(R.id.dialog_btn_dismiss).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - bsd.dismiss(); - } - }); + View dismissBtn = bsd.findViewById(R.id.dialog_btn_dismiss); + if (dismissBtn != null) { + dismissBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + bsd.dismiss(); + } + }); + } bsd.show(); } public static void smoothScrollToUserSelectedPosition(final DiscreteScrollView scrollView, View anchor) { PopupMenu popupMenu = new PopupMenu(scrollView.getContext(), anchor); Menu menu = popupMenu.getMenu(); - final RecyclerView.Adapter adapter = scrollView.getAdapter(); + final RecyclerView.Adapter adapter = scrollView.getAdapter(); int itemCount = (adapter instanceof InfiniteScrollAdapter) ? - ((InfiniteScrollAdapter) adapter).getRealItemCount() : - adapter.getItemCount(); + ((InfiniteScrollAdapter) adapter).getRealItemCount() : + (adapter != null ? adapter.getItemCount() : 0); for (int i = 0; i < itemCount; i++) { menu.add(String.valueOf(i + 1)); } @@ -69,7 +73,7 @@ public static void smoothScrollToUserSelectedPosition(final DiscreteScrollView s public boolean onMenuItemClick(MenuItem item) { int destination = Integer.parseInt(String.valueOf(item.getTitle())) - 1; if (adapter instanceof InfiniteScrollAdapter) { - destination = ((InfiniteScrollAdapter) adapter).getClosestPosition(destination); + destination = ((InfiniteScrollAdapter) adapter).getClosestPosition(destination); } scrollView.smoothScrollToPosition(destination); return true; @@ -82,6 +86,7 @@ public static int getTransitionTime() { return defaultPrefs().getInt(instance.KEY_TRANSITION_TIME, 150); } + @SuppressWarnings("deprecation") private static SharedPreferences defaultPrefs() { return PreferenceManager.getDefaultSharedPreferences(App.getInstance()); } diff --git a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/MainActivity.java b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/MainActivity.java index 262a68e..b025f10 100644 --- a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/MainActivity.java +++ b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/MainActivity.java @@ -4,13 +4,14 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.support.design.widget.Snackbar; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + +import com.google.android.material.snackbar.Snackbar; import com.yarolegovich.discretescrollview.sample.gallery.GalleryActivity; import com.yarolegovich.discretescrollview.sample.shop.ShopActivity; import com.yarolegovich.discretescrollview.sample.weather.WeatherActivity; @@ -32,7 +33,7 @@ protected void onCreate(Bundle savedInstanceState) { root = findViewById(R.id.screen); - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); findViewById(R.id.preview_shop).setOnClickListener(this); @@ -52,10 +53,9 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.mi_github: - open(URL_APP_REPO); - return true; + if (item.getItemId() == R.id.mi_github) { + open(URL_APP_REPO); + return true; } return super.onOptionsItemSelected(item); } diff --git a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/gallery/GalleryActivity.java b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/gallery/GalleryActivity.java index 4decd15..1256838 100644 --- a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/gallery/GalleryActivity.java +++ b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/gallery/GalleryActivity.java @@ -2,13 +2,13 @@ import android.animation.ArgbEvaluator; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AppCompatActivity; import android.view.View; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; + +import com.google.android.material.snackbar.Snackbar; import com.yarolegovich.discretescrollview.DiscreteScrollView; import com.yarolegovich.discretescrollview.sample.R; @@ -34,7 +34,7 @@ protected void onCreate(Bundle savedInstanceState) { Gallery gallery = Gallery.get(); List data = gallery.getData(); - DiscreteScrollView itemPicker = (DiscreteScrollView) findViewById(R.id.item_picker); + DiscreteScrollView itemPicker = findViewById(R.id.item_picker); itemPicker.setAdapter(new GalleryAdapter(data)); itemPicker.addScrollListener(this); itemPicker.addOnItemChangedListener(this); diff --git a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/gallery/GalleryAdapter.java b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/gallery/GalleryAdapter.java index a502361..01f17fa 100644 --- a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/gallery/GalleryAdapter.java +++ b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/gallery/GalleryAdapter.java @@ -2,13 +2,15 @@ import android.app.Activity; import android.graphics.Point; -import android.support.annotation.ColorInt; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + import com.bumptech.glide.Glide; import com.yarolegovich.discretescrollview.sample.R; @@ -28,7 +30,7 @@ public GalleryAdapter(List data) { } @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); Activity context = (Activity) recyclerView.getContext(); Point windowDimensions = new Point(); @@ -36,6 +38,7 @@ public void onAttachedToRecyclerView(RecyclerView recyclerView) { itemHeight = Math.round(windowDimensions.y * 0.6f); } + @NonNull @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); @@ -66,7 +69,7 @@ static class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View itemView) { super(itemView); - image = (ImageView) itemView.findViewById(R.id.image); + image = itemView.findViewById(R.id.image); overlay = itemView.findViewById(R.id.overlay); } diff --git a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/shop/ShopActivity.java b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/shop/ShopActivity.java index af450a9..1aa6392 100644 --- a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/shop/ShopActivity.java +++ b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/shop/ShopActivity.java @@ -1,18 +1,18 @@ package com.yarolegovich.discretescrollview.sample.shop; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; + +import com.google.android.material.snackbar.Snackbar; +import com.yarolegovich.discretescrollview.DSVOrientation; import com.yarolegovich.discretescrollview.DiscreteScrollView; import com.yarolegovich.discretescrollview.InfiniteScrollAdapter; -import com.yarolegovich.discretescrollview.DSVOrientation; import com.yarolegovich.discretescrollview.sample.DiscreteScrollViewOptions; import com.yarolegovich.discretescrollview.sample.R; import com.yarolegovich.discretescrollview.transform.ScaleTransformer; @@ -23,7 +23,7 @@ * Created by yarolegovich on 07.03.2017. */ -public class ShopActivity extends AppCompatActivity implements DiscreteScrollView.OnItemChangedListener, +public class ShopActivity extends AppCompatActivity implements DiscreteScrollView.OnItemChangedListener, View.OnClickListener { private List data; @@ -33,20 +33,20 @@ public class ShopActivity extends AppCompatActivity implements DiscreteScrollVie private TextView currentItemPrice; private ImageView rateItemButton; private DiscreteScrollView itemPicker; - private InfiniteScrollAdapter infiniteAdapter; + private InfiniteScrollAdapter infiniteAdapter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_shop); - currentItemName = (TextView) findViewById(R.id.item_name); - currentItemPrice = (TextView) findViewById(R.id.item_price); - rateItemButton = (ImageView) findViewById(R.id.item_btn_rate); + currentItemName = findViewById(R.id.item_name); + currentItemPrice = findViewById(R.id.item_price); + rateItemButton = findViewById(R.id.item_btn_rate); shop = Shop.get(); data = shop.getData(); - itemPicker = (DiscreteScrollView) findViewById(R.id.item_picker); + itemPicker = findViewById(R.id.item_picker); itemPicker.setOrientation(DSVOrientation.HORIZONTAL); itemPicker.addOnItemChangedListener(this); infiniteAdapter = InfiniteScrollAdapter.wrap(new ShopAdapter(data)); @@ -108,8 +108,8 @@ private void changeRateButtonState(Item item) { } @Override - public void onCurrentItemChanged(@Nullable RecyclerView.ViewHolder viewHolder, int position) { - int positionInDataSet = infiniteAdapter.getRealPosition(position); + public void onCurrentItemChanged(@Nullable ShopAdapter.ViewHolder viewHolder, int adapterPosition) { + int positionInDataSet = infiniteAdapter.getRealPosition(adapterPosition); onItemChanged(data.get(positionInDataSet)); } diff --git a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/shop/ShopAdapter.java b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/shop/ShopAdapter.java index 21a3de1..0beee57 100644 --- a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/shop/ShopAdapter.java +++ b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/shop/ShopAdapter.java @@ -1,11 +1,13 @@ package com.yarolegovich.discretescrollview.sample.shop; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + import com.bumptech.glide.Glide; import com.yarolegovich.discretescrollview.sample.R; @@ -23,6 +25,7 @@ public ShopAdapter(List data) { this.data = data; } + @NonNull @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); @@ -42,13 +45,13 @@ public int getItemCount() { return data.size(); } - class ViewHolder extends RecyclerView.ViewHolder { + static class ViewHolder extends RecyclerView.ViewHolder { private ImageView image; public ViewHolder(View itemView) { super(itemView); - image = (ImageView) itemView.findViewById(R.id.image); + image = itemView.findViewById(R.id.image); } } } diff --git a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/ForecastAdapter.java b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/ForecastAdapter.java index 396b3e5..8f79665 100644 --- a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/ForecastAdapter.java +++ b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/ForecastAdapter.java @@ -1,16 +1,21 @@ package com.yarolegovich.discretescrollview.sample.weather; import android.graphics.Color; -import android.support.v4.content.ContextCompat; -import android.support.v7.widget.RecyclerView; +import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.RecyclerView; + import com.bumptech.glide.Glide; -import com.bumptech.glide.load.resource.drawable.GlideDrawable; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.GlideException; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; import com.yarolegovich.discretescrollview.sample.R; @@ -31,11 +36,12 @@ public ForecastAdapter(List data) { } @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); parentRecycler = recyclerView; } + @NonNull @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); @@ -66,8 +72,8 @@ class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener public ViewHolder(View itemView) { super(itemView); - imageView = (ImageView) itemView.findViewById(R.id.city_image); - textView = (TextView) itemView.findViewById(R.id.city_name); + imageView = itemView.findViewById(R.id.city_image); + textView = itemView.findViewById(R.id.city_name); itemView.findViewById(R.id.container).setOnClickListener(this); } @@ -103,7 +109,7 @@ public void onClick(View v) { } } - private static class TintOnLoad implements RequestListener { + private static class TintOnLoad implements RequestListener { private ImageView imageView; private int tintColor; @@ -114,13 +120,13 @@ public TintOnLoad(ImageView view, int tintColor) { } @Override - public boolean onException(Exception e, Integer model, Target target, boolean isFirstResource) { + public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + imageView.setColorFilter(tintColor); return false; } @Override - public boolean onResourceReady(GlideDrawable resource, Integer model, Target target, boolean isFromMemoryCache, boolean isFirstResource) { - imageView.setColorFilter(tintColor); + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { return false; } } diff --git a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/ForecastView.java b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/ForecastView.java index 821c992..15b5c46 100644 --- a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/ForecastView.java +++ b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/ForecastView.java @@ -6,9 +6,6 @@ import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Shader; -import android.os.Build; -import android.support.annotation.ArrayRes; -import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.view.Gravity; import android.view.animation.AccelerateDecelerateInterpolator; @@ -16,6 +13,8 @@ import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.ArrayRes; + import com.bumptech.glide.Glide; import com.yarolegovich.discretescrollview.sample.R; @@ -46,11 +45,6 @@ public ForecastView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public ForecastView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - { evaluator = new ArgbEvaluator(); @@ -61,9 +55,9 @@ public ForecastView(Context context, AttributeSet attrs, int defStyleAttr, int d setGravity(Gravity.CENTER_HORIZONTAL); inflate(getContext(), R.layout.view_forecast, this); - weatherDescription = (TextView) findViewById(R.id.weather_description); - weatherImage = (ImageView) findViewById(R.id.weather_image); - weatherTemperature = (TextView) findViewById(R.id.weather_temperature); + weatherDescription = findViewById(R.id.weather_description); + weatherImage = findViewById(R.id.weather_image); + weatherTemperature = findViewById(R.id.weather_temperature); } private void initGradient() { diff --git a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/WeatherActivity.java b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/WeatherActivity.java index 6f2cc4e..e69c7fa 100644 --- a/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/WeatherActivity.java +++ b/sample/src/main/java/com/yarolegovich/discretescrollview/sample/weather/WeatherActivity.java @@ -1,11 +1,13 @@ package com.yarolegovich.discretescrollview.sample.weather; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; + import com.yarolegovich.discretescrollview.DiscreteScrollView; import com.yarolegovich.discretescrollview.sample.R; import com.yarolegovich.discretescrollview.sample.DiscreteScrollViewOptions; @@ -32,10 +34,10 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_weather); - forecastView = (ForecastView) findViewById(R.id.forecast_view); + forecastView = findViewById(R.id.forecast_view); forecasts = WeatherStation.get().getForecasts(); - cityPicker = (DiscreteScrollView) findViewById(R.id.forecast_city_picker); + cityPicker = findViewById(R.id.forecast_city_picker); cityPicker.setSlideOnFling(true); cityPicker.setAdapter(new ForecastAdapter(forecasts)); cityPicker.addOnItemChangedListener(this); @@ -74,7 +76,9 @@ public void onScroll( @Nullable ForecastAdapter.ViewHolder currentHolder, @Nullable ForecastAdapter.ViewHolder newHolder) { Forecast current = forecasts.get(currentIndex); - if (newIndex >= 0 && newIndex < cityPicker.getAdapter().getItemCount()) { + RecyclerView.Adapter adapter = cityPicker.getAdapter(); + int itemCount = adapter != null ? adapter.getItemCount() : 0; + if (newIndex >= 0 && newIndex < itemCount) { Forecast next = forecasts.get(newIndex); forecastView.onScroll(1f - Math.abs(position), current, next); } diff --git a/sample/src/main/res/layout/activity_gallery.xml b/sample/src/main/res/layout/activity_gallery.xml index 2ad6a6d..cbdb34d 100644 --- a/sample/src/main/res/layout/activity_gallery.xml +++ b/sample/src/main/res/layout/activity_gallery.xml @@ -1,5 +1,5 @@ - - - \ No newline at end of file + \ No newline at end of file diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index cb68fb1..f873a65 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -7,12 +7,12 @@ android:layout_height="match_parent" tools:context="com.yarolegovich.discretescrollview.sample.MainActivity"> - - + @@ -79,7 +81,7 @@ android:layout_width="16dp" android:layout_height="wrap_content" /> - @@ -118,7 +121,8 @@ android:layout_weight="1" android:text="@string/btn_smooth_scroll" android:textAllCaps="true" - android:textColor="@color/shopAccent" /> + android:textColor="@color/shopAccent" + tools:ignore="NestedWeights" />