From edd89c80a993cd20008408079c5b872f50c869ff Mon Sep 17 00:00:00 2001 From: Shanmugasanthosh Date: Thu, 19 Mar 2020 14:23:32 +0530 Subject: [PATCH 1/2] Sample added for Viewpager2 and PageIndicatorView2 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- pageindicatorview/build.gradle | 10 +- .../main/java/com/rd/PageIndicatorView2.java | 902 ++++++++++++++++++ sample/build.gradle | 10 +- sample/src/main/AndroidManifest.xml | 7 +- .../pageindicatorview/base/BaseActivity.java | 4 +- .../pageindicatorview/home/HomeActivity.java | 10 +- .../viewpager2/SamplePageFragment.java | 20 + .../viewpager2/SamplePagerAdapter.java | 26 + .../viewpager2/ViewPagerActivity.java | 46 + sample/src/main/res/layout/ac_home.xml | 2 +- sample/src/main/res/layout/ac_viewpager2.xml | 42 + .../src/main/res/layout/fragment_sample.xml | 14 + sample/src/main/res/menu/menu_customize.xml | 5 + sample/src/main/res/values/strings.xml | 2 + 16 files changed, 1087 insertions(+), 19 deletions(-) create mode 100644 pageindicatorview/src/main/java/com/rd/PageIndicatorView2.java create mode 100644 sample/src/main/java/com/rd/pageindicatorview/viewpager2/SamplePageFragment.java create mode 100644 sample/src/main/java/com/rd/pageindicatorview/viewpager2/SamplePagerAdapter.java create mode 100644 sample/src/main/java/com/rd/pageindicatorview/viewpager2/ViewPagerActivity.java create mode 100644 sample/src/main/res/layout/ac_viewpager2.xml create mode 100644 sample/src/main/res/layout/fragment_sample.xml diff --git a/build.gradle b/build.gradle index 8cd235c..b785ee6 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.6.1' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 599d4a6..9e1c0a0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Apr 30 21:23:45 EEST 2018 +#Wed Mar 18 22:09:31 IST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/pageindicatorview/build.gradle b/pageindicatorview/build.gradle index cd97610..1f90ff8 100644 --- a/pageindicatorview/build.gradle +++ b/pageindicatorview/build.gradle @@ -25,12 +25,11 @@ ext { } android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + compileSdkVersion 29 defaultConfig { minSdkVersion 14 - targetSdkVersion 28 + targetSdkVersion 29 versionCode 1 versionName "1.0" @@ -49,9 +48,10 @@ dependencies { repositories { maven { url "https://maven.google.com" } } - implementation 'androidx.annotation:annotation:1.0.0' - implementation 'androidx.core:core:1.0.0' + implementation 'androidx.annotation:annotation:1.1.0' + implementation 'androidx.core:core:1.2.0' implementation 'androidx.viewpager:viewpager:1.0.0' + implementation "androidx.viewpager2:viewpager2:1.0.0" } allprojects { diff --git a/pageindicatorview/src/main/java/com/rd/PageIndicatorView2.java b/pageindicatorview/src/main/java/com/rd/PageIndicatorView2.java new file mode 100644 index 0000000..c4b42f8 --- /dev/null +++ b/pageindicatorview/src/main/java/com/rd/PageIndicatorView2.java @@ -0,0 +1,902 @@ +package com.rd; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Canvas; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.Pair; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.text.TextUtilsCompat; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewpager.widget.ViewPager; +import androidx.viewpager2.widget.ViewPager2; + +import com.rd.animation.type.AnimationType; +import com.rd.animation.type.BaseAnimation; +import com.rd.animation.type.ColorAnimation; +import com.rd.animation.type.FillAnimation; +import com.rd.animation.type.ScaleAnimation; +import com.rd.draw.controller.DrawController; +import com.rd.draw.data.Indicator; +import com.rd.draw.data.Orientation; +import com.rd.draw.data.PositionSavedState; +import com.rd.draw.data.RtlMode; +import com.rd.utils.CoordinatesUtils; +import com.rd.utils.DensityUtils; +import com.rd.utils.IdUtils; + +public class PageIndicatorView2 extends View implements IndicatorManager.Listener, View.OnTouchListener { + + private static final Handler HANDLER = new Handler(Looper.getMainLooper()); + + ViewPager2.OnPageChangeCallback callback = new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + super.onPageScrolled(position, positionOffset, positionOffsetPixels); + onPageScroll(position, positionOffset); + } + + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + } + + @Override + public void onPageScrollStateChanged(int state) { + super.onPageScrollStateChanged(state); + if (state == ViewPager.SCROLL_STATE_IDLE) { + manager.indicator().setInteractiveAnimation(isInteractionEnabled); + } + } + }; + + private IndicatorManager manager; + private RecyclerView.AdapterDataObserver setObserver; + private ViewPager2 viewPager; + private boolean isInteractionEnabled; + + public PageIndicatorView2(Context context) { + super(context); + init(null); + } + + public PageIndicatorView2(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public PageIndicatorView2(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public PageIndicatorView2(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(attrs); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + findViewPager(getParent()); + } + + @Override + protected void onDetachedFromWindow() { + unRegisterSetObserver(); + super.onDetachedFromWindow(); + } + + @Override + public Parcelable onSaveInstanceState() { + Indicator indicator = manager.indicator(); + PositionSavedState positionSavedState = new PositionSavedState(super.onSaveInstanceState()); + positionSavedState.setSelectedPosition(indicator.getSelectedPosition()); + positionSavedState.setSelectingPosition(indicator.getSelectingPosition()); + positionSavedState.setLastSelectedPosition(indicator.getLastSelectedPosition()); + + return positionSavedState; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + if (state instanceof PositionSavedState) { + Indicator indicator = manager.indicator(); + PositionSavedState positionSavedState = (PositionSavedState) state; + indicator.setSelectedPosition(positionSavedState.getSelectedPosition()); + indicator.setSelectingPosition(positionSavedState.getSelectingPosition()); + indicator.setLastSelectedPosition(positionSavedState.getLastSelectedPosition()); + super.onRestoreInstanceState(positionSavedState.getSuperState()); + + } else { + super.onRestoreInstanceState(state); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + Pair pair = manager.drawer().measureViewSize(widthMeasureSpec, heightMeasureSpec); + setMeasuredDimension(pair.first, pair.second); + } + + @Override + protected void onDraw(Canvas canvas) { + manager.drawer().draw(canvas); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + manager.drawer().touch(event); + return true; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (!manager.indicator().isFadeOnIdle()) return false; + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + stopIdleRunnable(); + break; + + case MotionEvent.ACTION_UP: + startIdleRunnable(); + break; + } + return false; + } + + @Override + public void onIndicatorUpdated() { + invalidate(); + } + + /*@Override + public void onAdapterChanged(@NonNull ViewPager viewPager, @Nullable PagerAdapter oldAdapter, @Nullable PagerAdapter newAdapter) { + if (manager.indicator().isDynamicCount()) { + if (oldAdapter != null && setObserver != null) { + oldAdapter.unregisterDataSetObserver(setObserver); + setObserver = null; + } + registerSetObserver(); + } + updateState(); + }*/ + + /** + * Set static number of circle indicators to be displayed. + * + * @param count total count of indicators. + */ + public void setCount(int count) { + if (count >= 0 && manager.indicator().getCount() != count) { + manager.indicator().setCount(count); + updateVisibility(); + requestLayout(); + } + } + + /** + * Return number of circle indicators + */ + public int getCount() { + return manager.indicator().getCount(); + } + + /** + * Dynamic count will automatically update number of circle indicators + * if {@link ViewPager2} page count updates on run-time. If new count will be bigger than current count, + * selected circle will stay as it is, otherwise it will be set to last one. + * Note: works if {@link ViewPager2} set and already have it's adapter. See {@link #setViewPager(ViewPager2)}. + * + * @param dynamicCount boolean value to add/remove indicators dynamically. + */ + public void setDynamicCount(boolean dynamicCount) { + manager.indicator().setDynamicCount(dynamicCount); + + if (dynamicCount) { + registerSetObserver(); + } else { + unRegisterSetObserver(); + } + } + + /** + * Fade on idle will make {@link PageIndicatorView2} {@link View#INVISIBLE} if {@link ViewPager} is not interacted + * in time equal to {@link Indicator#idleDuration}. Take care when setting {@link PageIndicatorView2} alpha + * manually if this is true. Alpha is used to manage fading and appearance of {@link PageIndicatorView2} and value you provide + * will be overridden when {@link PageIndicatorView2} enters or leaves idle state. + * + * @param fadeOnIdle boolean value to hide {@link PageIndicatorView2} when {@link ViewPager} is idle + */ + public void setFadeOnIdle(boolean fadeOnIdle) { + manager.indicator().setFadeOnIdle(fadeOnIdle); + if (fadeOnIdle) { + startIdleRunnable(); + } else { + stopIdleRunnable(); + } + } + + /** + * Set radius in dp of each circle indicator. Default value is {@link Indicator#DEFAULT_RADIUS_DP}. + * Note: make sure you set circle Radius, not a Diameter. + * + * @param radiusDp radius of circle in dp. + */ + public void setRadius(int radiusDp) { + if (radiusDp < 0) { + radiusDp = 0; + } + + int radiusPx = DensityUtils.dpToPx(radiusDp); + manager.indicator().setRadius(radiusPx); + invalidate(); + } + + /** + * Set radius in px of each circle indicator. Default value is {@link Indicator#DEFAULT_RADIUS_DP}. + * Note: make sure you set circle Radius, not a Diameter. + * + * @param radiusPx radius of circle in px. + */ + public void setRadius(float radiusPx) { + if (radiusPx < 0) { + radiusPx = 0; + } + + manager.indicator().setRadius((int) radiusPx); + invalidate(); + } + + /** + * Return radius of each circle indicators in px. If custom radius is not set, return + * default value {@link Indicator#DEFAULT_RADIUS_DP}. + */ + public int getRadius() { + return manager.indicator().getRadius(); + } + + /** + * Set padding in dp between each circle indicator. Default value is {@link Indicator#DEFAULT_PADDING_DP}. + * + * @param paddingDp padding between circles in dp. + */ + public void setPadding(int paddingDp) { + if (paddingDp < 0) { + paddingDp = 0; + } + + int paddingPx = DensityUtils.dpToPx(paddingDp); + manager.indicator().setPadding(paddingPx); + invalidate(); + } + + /** + * Set padding in px between each circle indicator. Default value is {@link Indicator#DEFAULT_PADDING_DP}. + * + * @param paddingPx padding between circles in px. + */ + public void setPadding(float paddingPx) { + if (paddingPx < 0) { + paddingPx = 0; + } + + manager.indicator().setPadding((int) paddingPx); + invalidate(); + } + + /** + * Return padding in px between each circle indicator. If custom padding is not set, + * return default value {@link Indicator#DEFAULT_PADDING_DP}. + */ + public int getPadding() { + return manager.indicator().getPadding(); + } + + /** + * Set scale factor used in {@link AnimationType#SCALE} animation. + * Defines size of unselected indicator circles in comparing to selected one. + * Minimum and maximum values are {@link ScaleAnimation#MAX_SCALE_FACTOR} and {@link ScaleAnimation#MIN_SCALE_FACTOR}. + * See also {@link ScaleAnimation#DEFAULT_SCALE_FACTOR}. + * + * @param factor float value in range between 0 and 1. + */ + public void setScaleFactor(float factor) { + if (factor > ScaleAnimation.MAX_SCALE_FACTOR) { + factor = ScaleAnimation.MAX_SCALE_FACTOR; + + } else if (factor < ScaleAnimation.MIN_SCALE_FACTOR) { + factor = ScaleAnimation.MIN_SCALE_FACTOR; + } + + manager.indicator().setScaleFactor(factor); + } + + /** + * Returns scale factor values used in {@link AnimationType#SCALE} animation. + * Defines size of unselected indicator circles in comparing to selected one. + * Minimum and maximum values are {@link ScaleAnimation#MAX_SCALE_FACTOR} and {@link ScaleAnimation#MIN_SCALE_FACTOR}. + * See also {@link ScaleAnimation#DEFAULT_SCALE_FACTOR}. + * + * @return float value that indicate scale factor. + */ + public float getScaleFactor() { + return manager.indicator().getScaleFactor(); + } + + /** + * Set stroke width in px to set while {@link AnimationType#FILL} is selected. + * Default value is {@link FillAnimation#DEFAULT_STROKE_DP} + * + * @param strokePx stroke width in px. + */ + public void setStrokeWidth(float strokePx) { + int radiusPx = manager.indicator().getRadius(); + + if (strokePx < 0) { + strokePx = 0; + + } else if (strokePx > radiusPx) { + strokePx = radiusPx; + } + + manager.indicator().setStroke((int) strokePx); + invalidate(); + } + + /** + * Set stroke width in dp to set while {@link AnimationType#FILL} is selected. + * Default value is {@link FillAnimation#DEFAULT_STROKE_DP} + * + * @param strokeDp stroke width in dp. + */ + + public void setStrokeWidth(int strokeDp) { + int strokePx = DensityUtils.dpToPx(strokeDp); + int radiusPx = manager.indicator().getRadius(); + + if (strokePx < 0) { + strokePx = 0; + + } else if (strokePx > radiusPx) { + strokePx = radiusPx; + } + + manager.indicator().setStroke(strokePx); + invalidate(); + } + + /** + * Return stroke width in px if {@link AnimationType#FILL} is selected, 0 otherwise. + */ + public int getStrokeWidth() { + return manager.indicator().getStroke(); + } + + /** + * Set color of selected state to circle indicator. Default color is {@link ColorAnimation#DEFAULT_SELECTED_COLOR}. + * + * @param color color selected circle. + */ + public void setSelectedColor(int color) { + manager.indicator().setSelectedColor(color); + invalidate(); + } + + /** + * Return color of selected circle indicator. If custom unselected color + * is not set, return default color {@link ColorAnimation#DEFAULT_SELECTED_COLOR}. + */ + public int getSelectedColor() { + return manager.indicator().getSelectedColor(); + } + + /** + * Set color of unselected state to each circle indicator. Default color {@link ColorAnimation#DEFAULT_UNSELECTED_COLOR}. + * + * @param color color of each unselected circle. + */ + public void setUnselectedColor(int color) { + manager.indicator().setUnselectedColor(color); + invalidate(); + } + + /** + * Return color of unselected state of each circle indicator. If custom unselected color + * is not set, return default color {@link ColorAnimation#DEFAULT_UNSELECTED_COLOR}. + */ + public int getUnselectedColor() { + return manager.indicator().getUnselectedColor(); + } + + /** + * Automatically hide (View.INVISIBLE) PageIndicatorView while indicator count is <= 1. + * Default is true. + * + * @param autoVisibility auto hide indicators. + */ + public void setAutoVisibility(boolean autoVisibility) { + if (!autoVisibility) { + setVisibility(VISIBLE); + } + + manager.indicator().setAutoVisibility(autoVisibility); + updateVisibility(); + } + + /** + * Set orientation for indicator, one of HORIZONTAL or VERTICAL. + * Default is HORIZONTAL. + * + * @param orientation an orientation to display page indicators. + */ + public void setOrientation(@Nullable Orientation orientation) { + if (orientation != null) { + manager.indicator().setOrientation(orientation); + requestLayout(); + } + } + + /** + * Set animation duration time in millisecond. Default animation duration time is {@link BaseAnimation#DEFAULT_ANIMATION_TIME}. + * (Won't affect on anything unless {@link #setAnimationType(AnimationType type)} is specified + * and {@link #setInteractiveAnimation(boolean isInteractive)} is false). + * + * @param duration animation duration time. + */ + public void setAnimationDuration(long duration) { + manager.indicator().setAnimationDuration(duration); + } + + /** + * Sets time in millis after which {@link ViewPager} is considered idle. + * If {@link Indicator#fadeOnIdle} is true, {@link PageIndicatorView2} will + * fade away after entering idle state and appear when it is left. + * + * @param duration time in millis after which {@link ViewPager} is considered idle + */ + public void setIdleDuration(long duration) { + manager.indicator().setIdleDuration(duration); + if (manager.indicator().isFadeOnIdle()) { + startIdleRunnable(); + } else { + stopIdleRunnable(); + } + } + + /** + * Return animation duration time in milliseconds. If custom duration is not set, + * return default duration time {@link BaseAnimation#DEFAULT_ANIMATION_TIME}. + */ + public long getAnimationDuration() { + return manager.indicator().getAnimationDuration(); + } + + /** + * Set animation type to perform while selecting new circle indicator. + * Default animation type is {@link AnimationType#NONE}. + * + * @param type type of animation, one of {@link AnimationType} + */ + public void setAnimationType(@Nullable AnimationType type) { + manager.onValueUpdated(null); + + if (type != null) { + manager.indicator().setAnimationType(type); + } else { + manager.indicator().setAnimationType(AnimationType.NONE); + } + invalidate(); + } + + /** + * Interactive animation will animate indicator smoothly + * from position to position based on user's current swipe progress. + * (Won't affect on anything unless {@link #setViewPager(ViewPager2)} is specified). + * + * @param isInteractive value of animation to be interactive or not. + */ + public void setInteractiveAnimation(boolean isInteractive) { + manager.indicator().setInteractiveAnimation(isInteractive); + this.isInteractionEnabled = isInteractive; + } + + /** + * Set {@link ViewPager2} to add {@link ViewPager2.OnPageChangeCallback} and automatically + * handle selecting new indicators (and interactive animation effect if it is enabled). + * + * @param pager instance of {@link ViewPager2} to work with + */ + @SuppressLint("ClickableViewAccessibility") + public void setViewPager(@Nullable ViewPager2 pager) { + releaseViewPager(); + if (pager == null) { + return; + } + + viewPager = pager; + viewPager.registerOnPageChangeCallback(callback); + //viewPager.addOnAdapterChangeListener(this); + viewPager.setOnTouchListener(this); + manager.indicator().setViewPagerId(viewPager.getId()); + + setDynamicCount(manager.indicator().isDynamicCount()); + updateState(); + } + + /** + * Release {@link ViewPager2} and stop handling events of {@link ViewPager2.OnPageChangeCallback}. + */ + public void releaseViewPager() { + if (viewPager != null) { + viewPager.unregisterOnPageChangeCallback(callback); + //viewPager.removeOnAdapterChangeListener(this); + viewPager = null; + } + } + + /** + * Specify to display PageIndicatorView with Right to left layout or not. + * One of {@link RtlMode}: Off (Left to right), On (Right to left) + * or Auto (handle this mode automatically based on users language preferences). + * Default is Off. + * + * @param mode instance of {@link RtlMode} + */ + public void setRtlMode(@Nullable RtlMode mode) { + Indicator indicator = manager.indicator(); + if (mode == null) { + indicator.setRtlMode(RtlMode.Off); + } else { + indicator.setRtlMode(mode); + } + + if (viewPager == null) { + return; + } + + int selectedPosition = indicator.getSelectedPosition(); + int position = selectedPosition; + + if (isRtl()) { + position = (indicator.getCount() - 1) - selectedPosition; + + } else if (viewPager != null) { + position = viewPager.getCurrentItem(); + } + + indicator.setLastSelectedPosition(position); + indicator.setSelectingPosition(position); + indicator.setSelectedPosition(position); + invalidate(); + } + + /** + * Return position of currently selected circle indicator. + */ + public int getSelection() { + return manager.indicator().getSelectedPosition(); + } + + /** + * Set specific circle indicator position to be selected. If position < or > total count, + * accordingly first or last circle indicator will be selected. + * + * @param position position of indicator to select. + */ + public void setSelection(int position) { + Indicator indicator = manager.indicator(); + position = adjustPosition(position); + + if (position == indicator.getSelectedPosition() || position == indicator.getSelectingPosition()) { + return; + } + + indicator.setInteractiveAnimation(false); + indicator.setLastSelectedPosition(indicator.getSelectedPosition()); + indicator.setSelectingPosition(position); + indicator.setSelectedPosition(position); + manager.animate().basic(); + } + + /** + * Set specific circle indicator position to be selected without any kind of animation. If position < or > total count, + * accordingly first or last circle indicator will be selected. + * + * @param position position of indicator to select. + */ + public void setSelected(int position) { + Indicator indicator = manager.indicator(); + AnimationType animationType = indicator.getAnimationType(); + indicator.setAnimationType(AnimationType.NONE); + + setSelection(position); + indicator.setAnimationType(animationType); + } + + /** + * Clears selection of all indicators + */ + public void clearSelection() { + //TODO check + Indicator indicator = manager.indicator(); + indicator.setInteractiveAnimation(false); + indicator.setLastSelectedPosition(Indicator.COUNT_NONE); + indicator.setSelectingPosition(Indicator.COUNT_NONE); + indicator.setSelectedPosition(Indicator.COUNT_NONE); + manager.animate().basic(); + } + + /** + * Set progress value in range [0 - 1] to specify state of animation while selecting new circle indicator. + * + * @param selectingPosition selecting position with specific progress value. + * @param progress float value of progress. + */ + public void setProgress(int selectingPosition, float progress) { + Indicator indicator = manager.indicator(); + if (!indicator.isInteractiveAnimation()) { + return; + } + + int count = indicator.getCount(); + if (count <= 0 || selectingPosition < 0) { + selectingPosition = 0; + + } else if (selectingPosition > count - 1) { + selectingPosition = count - 1; + } + + if (progress < 0) { + progress = 0; + + } else if (progress > 1) { + progress = 1; + } + + if (progress == 1) { + indicator.setLastSelectedPosition(indicator.getSelectedPosition()); + indicator.setSelectedPosition(selectingPosition); + } + + indicator.setSelectingPosition(selectingPosition); + manager.animate().interactive(progress); + } + + public void setClickListener(@Nullable DrawController.ClickListener listener) { + manager.drawer().setClickListener(listener); + } + + private void init(@Nullable AttributeSet attrs) { + setupId(); + initIndicatorManager(attrs); + + if (manager.indicator().isFadeOnIdle()) { + startIdleRunnable(); + } + } + + private void setupId() { + if (getId() == NO_ID) { + setId(IdUtils.generateViewId()); + } + } + + private void initIndicatorManager(@Nullable AttributeSet attrs) { + manager = new IndicatorManager(this); + manager.drawer().initAttributes(getContext(), attrs); + + Indicator indicator = manager.indicator(); + indicator.setPaddingLeft(getPaddingLeft()); + indicator.setPaddingTop(getPaddingTop()); + indicator.setPaddingRight(getPaddingRight()); + indicator.setPaddingBottom(getPaddingBottom()); + isInteractionEnabled = indicator.isInteractiveAnimation(); + } + + private void registerSetObserver() { + if (setObserver != null || viewPager == null || viewPager.getAdapter() == null) { + return; + } + + setObserver = new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + updateState(); + } + }; + + try { + viewPager.getAdapter().registerAdapterDataObserver(setObserver); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + private void unRegisterSetObserver() { + if (setObserver == null || viewPager == null || viewPager.getAdapter() == null) { + return; + } + + try { + viewPager.getAdapter().unregisterAdapterDataObserver(setObserver); + setObserver = null; + } catch (IllegalStateException e) { + e.printStackTrace(); + } + } + + private void updateState() { + if (viewPager == null || viewPager.getAdapter() == null) { + return; + } + + int count = viewPager.getAdapter().getItemCount(); + int selectedPos = isRtl() ? (count - 1) - viewPager.getCurrentItem() : viewPager.getCurrentItem(); + + manager.indicator().setSelectedPosition(selectedPos); + manager.indicator().setSelectingPosition(selectedPos); + manager.indicator().setLastSelectedPosition(selectedPos); + manager.indicator().setCount(count); + manager.animate().end(); + + updateVisibility(); + requestLayout(); + } + + private void updateVisibility() { + if (!manager.indicator().isAutoVisibility()) { + return; + } + + int count = manager.indicator().getCount(); + int visibility = getVisibility(); + + if (visibility != VISIBLE && count > Indicator.MIN_COUNT) { + setVisibility(VISIBLE); + + } else if (visibility != INVISIBLE && count <= Indicator.MIN_COUNT) { + setVisibility(View.INVISIBLE); + } + } + + private void onPageSelect(int position) { + Indicator indicator = manager.indicator(); + boolean canSelectIndicator = isViewMeasured(); + int count = indicator.getCount(); + + if (canSelectIndicator) { + if (isRtl()) { + position = (count - 1) - position; + } + + setSelection(position); + } + } + + private void onPageScroll(int position, float positionOffset) { + Indicator indicator = manager.indicator(); + AnimationType animationType = indicator.getAnimationType(); + boolean interactiveAnimation = indicator.isInteractiveAnimation(); + boolean canSelectIndicator = isViewMeasured() && interactiveAnimation && animationType != AnimationType.NONE; + + if (!canSelectIndicator) { + return; + } + + Pair progressPair = CoordinatesUtils.getProgress(indicator, position, positionOffset, isRtl()); + int selectingPosition = progressPair.first; + float selectingProgress = progressPair.second; + setProgress(selectingPosition, selectingProgress); + } + + private boolean isRtl() { + switch (manager.indicator().getRtlMode()) { + case On: + return true; + + case Off: + return false; + + case Auto: + return TextUtilsCompat.getLayoutDirectionFromLocale(getContext().getResources().getConfiguration().locale) == ViewCompat.LAYOUT_DIRECTION_RTL; + } + + return false; + } + + private boolean isViewMeasured() { + return getMeasuredHeight() != 0 || getMeasuredWidth() != 0; + } + + private void findViewPager(@Nullable ViewParent viewParent) { + boolean isValidParent = viewParent != null && + viewParent instanceof ViewGroup && + ((ViewGroup) viewParent).getChildCount() > 0; + + if (!isValidParent) { + return; + } + + int viewPagerId = manager.indicator().getViewPagerId(); + ViewPager2 viewPager = findViewPager((ViewGroup) viewParent, viewPagerId); + + if (viewPager != null) { + setViewPager(viewPager); + } else { + findViewPager(viewParent.getParent()); + } + } + + @Nullable + private ViewPager2 findViewPager(@NonNull ViewGroup viewGroup, int id) { + if (viewGroup.getChildCount() <= 0) { + return null; + } + + View view = viewGroup.findViewById(id); + if (view != null && view instanceof ViewPager2) { + return (ViewPager2) view; + } else { + return null; + } + } + + private int adjustPosition(int position) { + Indicator indicator = manager.indicator(); + int count = indicator.getCount(); + int lastPosition = count - 1; + + if (position < 0) { + position = 0; + + } else if (position > lastPosition) { + position = lastPosition; + } + + return position; + } + + private void displayWithAnimation() { + animate().cancel(); + animate().alpha(1.0f).setDuration(Indicator.IDLE_ANIMATION_DURATION); + } + + private void hideWithAnimation() { + animate().cancel(); + animate().alpha(0f).setDuration(Indicator.IDLE_ANIMATION_DURATION); + } + + private void startIdleRunnable() { + HANDLER.removeCallbacks(idleRunnable); + HANDLER.postDelayed(idleRunnable, manager.indicator().getIdleDuration()); + } + + private void stopIdleRunnable() { + HANDLER.removeCallbacks(idleRunnable); + displayWithAnimation(); + } + + private Runnable idleRunnable = new Runnable() { + @Override + public void run() { + manager.indicator().setIdle(true); + hideWithAnimation(); + } + }; +} diff --git a/sample/build.gradle b/sample/build.gradle index b4b7135..b3fef79 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,13 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + compileSdkVersion 29 defaultConfig { applicationId "com.rd.pageindicatorview" minSdkVersion 14 - targetSdkVersion 28 + targetSdkVersion 29 versionCode 1 versionName "1.0" } @@ -21,7 +20,8 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.appcompat:appcompat:1.0.1' - implementation 'androidx.recyclerview:recyclerview:1.0.0' + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation "androidx.viewpager2:viewpager2:1.0.0" implementation project(':pageindicatorview') } diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 2a72b74..cc416de 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -1,13 +1,15 @@ + android:theme="@style/Base" + tools:ignore="LockedOrientationActivity"> @@ -20,6 +22,7 @@ + \ No newline at end of file diff --git a/sample/src/main/java/com/rd/pageindicatorview/base/BaseActivity.java b/sample/src/main/java/com/rd/pageindicatorview/base/BaseActivity.java index ba384bd..e41d74b 100644 --- a/sample/src/main/java/com/rd/pageindicatorview/base/BaseActivity.java +++ b/sample/src/main/java/com/rd/pageindicatorview/base/BaseActivity.java @@ -3,10 +3,12 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; + import android.view.MenuItem; + import com.rd.pageindicatorview.sample.R; -public class BaseActivity extends AppCompatActivity { +public abstract class BaseActivity extends AppCompatActivity { private ActionBar toolbar; diff --git a/sample/src/main/java/com/rd/pageindicatorview/home/HomeActivity.java b/sample/src/main/java/com/rd/pageindicatorview/home/HomeActivity.java index e7b832d..c0fe8ed 100644 --- a/sample/src/main/java/com/rd/pageindicatorview/home/HomeActivity.java +++ b/sample/src/main/java/com/rd/pageindicatorview/home/HomeActivity.java @@ -2,16 +2,20 @@ import android.content.Intent; import android.os.Bundle; + import androidx.annotation.NonNull; import androidx.viewpager.widget.ViewPager; + import android.view.Menu; import android.view.MenuItem; import android.view.View; + import com.rd.PageIndicatorView; import com.rd.pageindicatorview.base.BaseActivity; import com.rd.pageindicatorview.customize.CustomizeActivity; import com.rd.pageindicatorview.data.Customization; import com.rd.pageindicatorview.sample.R; +import com.rd.pageindicatorview.viewpager2.ViewPagerActivity; import java.util.ArrayList; import java.util.List; @@ -34,6 +38,7 @@ protected void onCreate(Bundle savedInstanceState) { @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); boolean customization = requestCode == CustomizeActivity.EXTRAS_CUSTOMIZATION_REQUEST_CODE && resultCode == RESULT_OK; if (customization && intent != null) { this.customization = intent.getParcelableExtra(CustomizeActivity.EXTRAS_CUSTOMIZATION); @@ -53,13 +58,14 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.actionCustomize: CustomizeActivity.start(this, customization); return true; - + case R.id.actionViewPager2: + ViewPagerActivity.start(this, customization); + return true; default: return super.onOptionsItemSelected(item); } } - @SuppressWarnings("ConstantConditions") private void initViews() { HomeAdapter adapter = new HomeAdapter(); adapter.setData(createPageList()); diff --git a/sample/src/main/java/com/rd/pageindicatorview/viewpager2/SamplePageFragment.java b/sample/src/main/java/com/rd/pageindicatorview/viewpager2/SamplePageFragment.java new file mode 100644 index 0000000..30a20ee --- /dev/null +++ b/sample/src/main/java/com/rd/pageindicatorview/viewpager2/SamplePageFragment.java @@ -0,0 +1,20 @@ +package com.rd.pageindicatorview.viewpager2; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.fragment.app.Fragment; + +import com.rd.pageindicatorview.sample.R; + +public class SamplePageFragment extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate( + R.layout.fragment_sample, container, false); + } +} \ No newline at end of file diff --git a/sample/src/main/java/com/rd/pageindicatorview/viewpager2/SamplePagerAdapter.java b/sample/src/main/java/com/rd/pageindicatorview/viewpager2/SamplePagerAdapter.java new file mode 100644 index 0000000..127cc1b --- /dev/null +++ b/sample/src/main/java/com/rd/pageindicatorview/viewpager2/SamplePagerAdapter.java @@ -0,0 +1,26 @@ +package com.rd.pageindicatorview.viewpager2; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.viewpager2.adapter.FragmentStateAdapter; + +public class SamplePagerAdapter extends FragmentStateAdapter { + + private static final int NUM_PAGES = 5; + + SamplePagerAdapter(FragmentActivity fa) { + super(fa); + } + + @NonNull + @Override + public Fragment createFragment(int position) { + return new SamplePageFragment(); + } + + @Override + public int getItemCount() { + return NUM_PAGES; + } +} \ No newline at end of file diff --git a/sample/src/main/java/com/rd/pageindicatorview/viewpager2/ViewPagerActivity.java b/sample/src/main/java/com/rd/pageindicatorview/viewpager2/ViewPagerActivity.java new file mode 100644 index 0000000..5405cef --- /dev/null +++ b/sample/src/main/java/com/rd/pageindicatorview/viewpager2/ViewPagerActivity.java @@ -0,0 +1,46 @@ +package com.rd.pageindicatorview.viewpager2; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.viewpager2.widget.ViewPager2; + +import com.rd.PageIndicatorView2; +import com.rd.pageindicatorview.base.BaseActivity; +import com.rd.pageindicatorview.data.Customization; +import com.rd.pageindicatorview.sample.R; + +import static com.rd.pageindicatorview.customize.CustomizeActivity.EXTRAS_CUSTOMIZATION; +import static com.rd.pageindicatorview.customize.CustomizeActivity.EXTRAS_CUSTOMIZATION_REQUEST_CODE; + +/** + * Created by Santhosh on 3/19/2020. + */ +public class ViewPagerActivity extends BaseActivity { + + private PageIndicatorView2 pageIndicatorView2; + + public static void start(@NonNull Activity activity, @NonNull Customization customization) { + Intent intent = new Intent(activity, ViewPagerActivity.class); + intent.putExtra(EXTRAS_CUSTOMIZATION, customization); + activity.startActivityForResult(intent, EXTRAS_CUSTOMIZATION_REQUEST_CODE); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.ac_viewpager2); + + initToolbar(); + initViews(); + } + + private void initViews() { + SamplePagerAdapter pagerAdapter = new SamplePagerAdapter(this); + final ViewPager2 pager = findViewById(R.id.viewPager2); + pager.setAdapter(pagerAdapter); + pageIndicatorView2 = findViewById(R.id.pageIndicatorView2); + } +} diff --git a/sample/src/main/res/layout/ac_home.xml b/sample/src/main/res/layout/ac_home.xml index 72a4d25..2ff997a 100644 --- a/sample/src/main/res/layout/ac_home.xml +++ b/sample/src/main/res/layout/ac_home.xml @@ -25,12 +25,12 @@ diff --git a/sample/src/main/res/layout/ac_viewpager2.xml b/sample/src/main/res/layout/ac_viewpager2.xml new file mode 100644 index 0000000..de9d46d --- /dev/null +++ b/sample/src/main/res/layout/ac_viewpager2.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + diff --git a/sample/src/main/res/layout/fragment_sample.xml b/sample/src/main/res/layout/fragment_sample.xml new file mode 100644 index 0000000..4f3c7d2 --- /dev/null +++ b/sample/src/main/res/layout/fragment_sample.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/sample/src/main/res/menu/menu_customize.xml b/sample/src/main/res/menu/menu_customize.xml index 3158f3c..f8e9513 100644 --- a/sample/src/main/res/menu/menu_customize.xml +++ b/sample/src/main/res/menu/menu_customize.xml @@ -7,4 +7,9 @@ android:title="@string/customize" app:showAsAction="never" /> + + \ No newline at end of file diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml index 5e212ff..3cfc799 100644 --- a/sample/src/main/res/values/strings.xml +++ b/sample/src/main/res/values/strings.xml @@ -5,9 +5,11 @@ Animation type Orientation Customize + ViewPager2 Home Right to left mode Fade on idle + Sample Fragment None From 00dad9063a3b08cbfe9eedfaeb868bfd9451c7fd Mon Sep 17 00:00:00 2001 From: Shanmugasanthosh Date: Thu, 19 Mar 2020 14:44:43 +0530 Subject: [PATCH 2/2] Remove unused --- .../rd/pageindicatorview/viewpager2/ViewPagerActivity.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sample/src/main/java/com/rd/pageindicatorview/viewpager2/ViewPagerActivity.java b/sample/src/main/java/com/rd/pageindicatorview/viewpager2/ViewPagerActivity.java index 5405cef..bd627df 100644 --- a/sample/src/main/java/com/rd/pageindicatorview/viewpager2/ViewPagerActivity.java +++ b/sample/src/main/java/com/rd/pageindicatorview/viewpager2/ViewPagerActivity.java @@ -7,7 +7,6 @@ import androidx.annotation.NonNull; import androidx.viewpager2.widget.ViewPager2; -import com.rd.PageIndicatorView2; import com.rd.pageindicatorview.base.BaseActivity; import com.rd.pageindicatorview.data.Customization; import com.rd.pageindicatorview.sample.R; @@ -20,8 +19,6 @@ */ public class ViewPagerActivity extends BaseActivity { - private PageIndicatorView2 pageIndicatorView2; - public static void start(@NonNull Activity activity, @NonNull Customization customization) { Intent intent = new Intent(activity, ViewPagerActivity.class); intent.putExtra(EXTRAS_CUSTOMIZATION, customization); @@ -32,7 +29,6 @@ public static void start(@NonNull Activity activity, @NonNull Customization cust protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ac_viewpager2); - initToolbar(); initViews(); } @@ -41,6 +37,5 @@ private void initViews() { SamplePagerAdapter pagerAdapter = new SamplePagerAdapter(this); final ViewPager2 pager = findViewById(R.id.viewPager2); pager.setAdapter(pagerAdapter); - pageIndicatorView2 = findViewById(R.id.pageIndicatorView2); } }