From 8df744c11c466357817ef3f6979cc02aecfeeb19 Mon Sep 17 00:00:00 2001 From: "pa.gulko" Date: Wed, 5 Jul 2017 18:39:07 +0300 Subject: [PATCH 1/6] started splitting on more compact components --- .../example/slideup/SlideUpViewActivity.java | 12 +++ build.gradle | 12 ++- gradle/wrapper/gradle-wrapper.properties | 2 +- .../com/mancj/slideup/LoggerNotifier.java | 11 +++ .../main/java/com/mancj/slideup/SlideUp.java | 12 +-- .../com/mancj/slideup/VerticalConsumer.java | 79 +++++++++++++++++++ 6 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 library/src/main/java/com/mancj/slideup/LoggerNotifier.java create mode 100644 library/src/main/java/com/mancj/slideup/VerticalConsumer.java diff --git a/app/src/main/java/com/example/slideup/SlideUpViewActivity.java b/app/src/main/java/com/example/slideup/SlideUpViewActivity.java index 1ae8491..654e4f6 100644 --- a/app/src/main/java/com/example/slideup/SlideUpViewActivity.java +++ b/app/src/main/java/com/example/slideup/SlideUpViewActivity.java @@ -9,6 +9,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.widget.Toast; import com.mancj.slideup.SlideUp; @@ -17,6 +18,7 @@ public class SlideUpViewActivity extends AppCompatActivity { private View dim; private View sliderView; private FloatingActionButton fab; + Toast toast; @Override protected void onCreate(Bundle savedInstanceState) { @@ -26,6 +28,16 @@ protected void onCreate(Bundle savedInstanceState) { setSupportActionBar(toolbar); sliderView = findViewById(R.id.slideView); + sliderView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (toast != null){ + toast.cancel(); + } + toast = Toast.makeText(SlideUpViewActivity.this, "click", Toast.LENGTH_SHORT); + toast.show(); + } + }); dim = findViewById(R.id.dim); fab = (FloatingActionButton) findViewById(R.id.fab); diff --git a/build.gradle b/build.gradle index cf1b904..524090d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { versions = [ - support : '25.3.1', + support : '25.4.0', SlideUp : '2.2.1', Rx : [ Java : '1.3.0' @@ -9,16 +9,24 @@ buildscript { ] } repositories { + mavenCentral() jcenter() + maven { + url "https://maven.google.com" + } } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:3.0.0-alpha5' } } allprojects { repositories { + mavenCentral() jcenter() + maven { + url "https://maven.google.com" + } } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 673e008..64532f6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-milestone-1-all.zip diff --git a/library/src/main/java/com/mancj/slideup/LoggerNotifier.java b/library/src/main/java/com/mancj/slideup/LoggerNotifier.java new file mode 100644 index 0000000..389427f --- /dev/null +++ b/library/src/main/java/com/mancj/slideup/LoggerNotifier.java @@ -0,0 +1,11 @@ +package com.mancj.slideup; + +/** + * @author pa.gulko zTrap (05.07.2017) + */ +public interface LoggerNotifier { + + void notifyPercentChanged(float percent); + + void notifyVisibilityChanged(int visibility); +} diff --git a/library/src/main/java/com/mancj/slideup/SlideUp.java b/library/src/main/java/com/mancj/slideup/SlideUp.java index 22c2ea5..7464773 100644 --- a/library/src/main/java/com/mancj/slideup/SlideUp.java +++ b/library/src/main/java/com/mancj/slideup/SlideUp.java @@ -32,7 +32,7 @@ import static com.mancj.slideup.SlideUp.State.HIDDEN; import static com.mancj.slideup.SlideUp.State.SHOWED; -public class SlideUp implements View.OnTouchListener, ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { +public class SlideUp implements View.OnTouchListener, ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener, LoggerNotifier { private final static String TAG = SlideUp.class.getSimpleName(); private final static String KEY_START_GRAVITY = TAG + "_start_gravity"; @@ -945,8 +945,9 @@ private int getEnd(){ return mSliderView.getRight(); } } - - private void notifyPercentChanged(float percent){ + + @Override + public void notifyPercentChanged(float percent){ percent = percent > 100 ? 100 : percent; percent = percent < 0 ? 0 : percent; if (mSlideAnimationTo == 0 && mHideKeyboard) @@ -966,8 +967,9 @@ private void notifyPercentChanged(float percent){ } } } - - private void notifyVisibilityChanged(int visibility){ + + @Override + public void notifyVisibilityChanged(int visibility){ mSliderView.setVisibility(visibility); if (mListeners != null && !mListeners.isEmpty()){ for (int i = 0; i < mListeners.size(); i++) { diff --git a/library/src/main/java/com/mancj/slideup/VerticalConsumer.java b/library/src/main/java/com/mancj/slideup/VerticalConsumer.java new file mode 100644 index 0000000..a4e362d --- /dev/null +++ b/library/src/main/java/com/mancj/slideup/VerticalConsumer.java @@ -0,0 +1,79 @@ +package com.mancj.slideup; + +import android.animation.ValueAnimator; +import android.support.annotation.NonNull; +import android.view.MotionEvent; +import android.view.View; + +/** + * @author pa.gulko zTrap (05.07.2017) + */ +public class VerticalConsumer { + private float mSlideAnimationTo; + private ValueAnimator mValueAnimator; + private LoggerNotifier mNotifier; + private boolean mCanSlide = true; + private float mMaxSlidePosition; + private float mTouchableArea; + + private float mStartPositionY; + private float mViewStartPositionY; + + private float mViewHeight; + private View mSliderView; + + public VerticalConsumer(@NonNull View sliderView, float touchableArea, LoggerNotifier notifier, + ValueAnimator valueAnimator){ + mValueAnimator = valueAnimator; + mTouchableArea = touchableArea; + mSliderView = sliderView; + mNotifier = notifier; + } + + public boolean consumeUpToDown(MotionEvent event){ + float touchedArea = event.getRawY() - mSliderView.getBottom(); + switch (event.getActionMasked()){ + case MotionEvent.ACTION_DOWN: + mViewHeight = mSliderView.getHeight(); + mStartPositionY = event.getRawY(); + mViewStartPositionY = mSliderView.getTranslationY(); + mMaxSlidePosition = mViewHeight; + if (mTouchableArea < touchedArea){ + mCanSlide = false; + } + break; + case MotionEvent.ACTION_MOVE: + float difference = event.getRawY() - mStartPositionY; + float moveTo = mViewStartPositionY + difference; + float percents = moveTo * 100 / -mSliderView.getHeight(); + + if (moveTo < 0 && mCanSlide){ + mNotifier.notifyPercentChanged(percents); + mSliderView.setTranslationY(moveTo); + } + if (event.getRawY() < mMaxSlidePosition) { + mMaxSlidePosition = event.getRawY(); + } + break; + case MotionEvent.ACTION_UP: + float slideAnimationFrom = -mSliderView.getTranslationY(); + if (slideAnimationFrom == mViewStartPositionY){ + return false; + } + boolean mustShow = mMaxSlidePosition < event.getRawY(); + boolean scrollableAreaConsumed = mSliderView.getTranslationY() < -mSliderView.getHeight() / 5; + + if (scrollableAreaConsumed && !mustShow){ + mSlideAnimationTo = mSliderView.getHeight() + mSliderView.getTop(); + }else { + mSlideAnimationTo = 0; + } + mValueAnimator.setFloatValues(slideAnimationFrom, mSlideAnimationTo); + mValueAnimator.start(); + mCanSlide = true; + mMaxSlidePosition = 0; + break; + } + return true; + } +} From 0aa1ef6e3a3efc87c15155956d0ae98543f8f1b3 Mon Sep 17 00:00:00 2001 From: "pa.gulko" Date: Wed, 12 Jul 2017 20:33:45 +0300 Subject: [PATCH 2/6] Now splitting done --- MIGRATION.md | 4 + app/build.gradle | 8 +- .../slideup/SlideDownViewActivity.java | 3 +- .../example/slideup/SlideEndViewActivity.java | 3 +- .../slideup/SlideStartViewActivity.java | 3 +- .../example/slideup/SlideUpViewActivity.java | 7 +- build.gradle | 14 +- library/build.gradle | 8 +- .../com/mancj/slideup/AnimationProcessor.java | 51 + .../slideup/HorizontalTouchConsumer.java | 98 ++ .../main/java/com/mancj/slideup/Internal.java | 33 + .../com/mancj/slideup/LoggerNotifier.java | 2 +- .../slideup/OnGlobalLayoutSingleListener.java | 29 + .../main/java/com/mancj/slideup/SlideUp.java | 1061 ++++++----------- .../com/mancj/slideup/SlideUpBuilder.java | 201 ++++ .../java/com/mancj/slideup/TouchConsumer.java | 52 + .../com/mancj/slideup/VerticalConsumer.java | 79 -- .../mancj/slideup/VerticalTouchConsumer.java | 98 ++ 18 files changed, 951 insertions(+), 803 deletions(-) create mode 100644 MIGRATION.md create mode 100644 library/src/main/java/com/mancj/slideup/AnimationProcessor.java create mode 100644 library/src/main/java/com/mancj/slideup/HorizontalTouchConsumer.java create mode 100644 library/src/main/java/com/mancj/slideup/Internal.java create mode 100644 library/src/main/java/com/mancj/slideup/OnGlobalLayoutSingleListener.java create mode 100644 library/src/main/java/com/mancj/slideup/SlideUpBuilder.java create mode 100644 library/src/main/java/com/mancj/slideup/TouchConsumer.java delete mode 100644 library/src/main/java/com/mancj/slideup/VerticalConsumer.java create mode 100644 library/src/main/java/com/mancj/slideup/VerticalTouchConsumer.java diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 0000000..797c81c --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,4 @@ +# v2.2.6 -> ... + `SlideUp.Builder` was moved into separated class and renamed to `SlideUpBuilder` + method `setTouchebleArea(float touchableArea)` was splitted to two methods: `setTouchebleAreaDp(float touchableArea)` and `setTouchebleAreaPx(float touchableArea)` + method `getTouchebleArea()` was splitted to two methods: `getTouchebleAreaDp()` and `getTouchebleAreaPx()` \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 284c71f..7f3cd83 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion '25.0.3' + compileSdkVersion 26 + buildToolsVersion '26.0.0' defaultConfig { applicationId "com.example.slideup" - minSdkVersion 12 - targetSdkVersion 25 + minSdkVersion 14 + targetSdkVersion 26 versionCode 1 versionName "1.0" diff --git a/app/src/main/java/com/example/slideup/SlideDownViewActivity.java b/app/src/main/java/com/example/slideup/SlideDownViewActivity.java index c21f0f0..e4e7356 100644 --- a/app/src/main/java/com/example/slideup/SlideDownViewActivity.java +++ b/app/src/main/java/com/example/slideup/SlideDownViewActivity.java @@ -11,6 +11,7 @@ import android.view.View; import com.mancj.slideup.SlideUp; +import com.mancj.slideup.SlideUpBuilder; public class SlideDownViewActivity extends AppCompatActivity { private SlideUp slideUp; @@ -29,7 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { dim = findViewById(R.id.dim); fab = (FloatingActionButton) findViewById(R.id.fab); - slideUp = new SlideUp.Builder(sliderView) + slideUp = new SlideUpBuilder(sliderView) .withListeners(new SlideUp.Listener.Events() { @Override public void onSlide(float percent) { diff --git a/app/src/main/java/com/example/slideup/SlideEndViewActivity.java b/app/src/main/java/com/example/slideup/SlideEndViewActivity.java index 2556729..e20ccfa 100644 --- a/app/src/main/java/com/example/slideup/SlideEndViewActivity.java +++ b/app/src/main/java/com/example/slideup/SlideEndViewActivity.java @@ -11,6 +11,7 @@ import android.view.View; import com.mancj.slideup.SlideUp; +import com.mancj.slideup.SlideUpBuilder; public class SlideEndViewActivity extends AppCompatActivity { private SlideUp slideUp; @@ -29,7 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { dim = findViewById(R.id.dim); fab = (FloatingActionButton) findViewById(R.id.fab); - slideUp = new SlideUp.Builder(sliderView) + slideUp = new SlideUpBuilder(sliderView) .withListeners(new SlideUp.Listener.Events() { @Override public void onSlide(float percent) { diff --git a/app/src/main/java/com/example/slideup/SlideStartViewActivity.java b/app/src/main/java/com/example/slideup/SlideStartViewActivity.java index abed750..23dbd23 100644 --- a/app/src/main/java/com/example/slideup/SlideStartViewActivity.java +++ b/app/src/main/java/com/example/slideup/SlideStartViewActivity.java @@ -11,6 +11,7 @@ import android.view.View; import com.mancj.slideup.SlideUp; +import com.mancj.slideup.SlideUpBuilder; public class SlideStartViewActivity extends AppCompatActivity { private SlideUp slideUp; @@ -29,7 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { dim = findViewById(R.id.dim); fab = (FloatingActionButton) findViewById(R.id.fab); - slideUp = new SlideUp.Builder(sliderView) + slideUp = new SlideUpBuilder(sliderView) .withListeners(new SlideUp.Listener.Events() { @Override public void onSlide(float percent) { diff --git a/app/src/main/java/com/example/slideup/SlideUpViewActivity.java b/app/src/main/java/com/example/slideup/SlideUpViewActivity.java index 654e4f6..73f4c53 100644 --- a/app/src/main/java/com/example/slideup/SlideUpViewActivity.java +++ b/app/src/main/java/com/example/slideup/SlideUpViewActivity.java @@ -12,6 +12,7 @@ import android.widget.Toast; import com.mancj.slideup.SlideUp; +import com.mancj.slideup.SlideUpBuilder; public class SlideUpViewActivity extends AppCompatActivity { private SlideUp slideUp; @@ -24,7 +25,7 @@ public class SlideUpViewActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_slide_up_view); - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); sliderView = findViewById(R.id.slideView); @@ -39,9 +40,9 @@ public void onClick(View v) { } }); dim = findViewById(R.id.dim); - fab = (FloatingActionButton) findViewById(R.id.fab); + fab = findViewById(R.id.fab); - slideUp = new SlideUp.Builder(sliderView) + slideUp = new SlideUpBuilder(sliderView) .withListeners(new SlideUp.Listener.Events() { @Override public void onSlide(float percent) { diff --git a/build.gradle b/build.gradle index 524090d..d6901b9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { versions = [ - support : '25.4.0', + support : '26.0.0-beta2', SlideUp : '2.2.1', Rx : [ Java : '1.3.0' @@ -9,24 +9,18 @@ buildscript { ] } repositories { - mavenCentral() jcenter() - maven { - url "https://maven.google.com" - } + google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.0-alpha5' + classpath 'com.android.tools.build:gradle:3.0.0-alpha6' } } allprojects { repositories { - mavenCentral() jcenter() - maven { - url "https://maven.google.com" - } + google() } } diff --git a/library/build.gradle b/library/build.gradle index c11de7f..4b62915 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 25 - buildToolsVersion '25.0.3' + compileSdkVersion 26 + buildToolsVersion '26.0.0' defaultConfig { - minSdkVersion 12 - targetSdkVersion 25 + minSdkVersion 14 + targetSdkVersion 26 versionCode 7 versionName "2.2.5" } diff --git a/library/src/main/java/com/mancj/slideup/AnimationProcessor.java b/library/src/main/java/com/mancj/slideup/AnimationProcessor.java new file mode 100644 index 0000000..e4fb722 --- /dev/null +++ b/library/src/main/java/com/mancj/slideup/AnimationProcessor.java @@ -0,0 +1,51 @@ +package com.mancj.slideup; + +import android.animation.Animator; +import android.animation.ValueAnimator; + +/** + * @author pa.gulko zTrap (12.07.2017) + */ +class AnimationProcessor { + private SlideUpBuilder mBuilder; + private ValueAnimator mValueAnimator; + private float mSlideAnimationTo; + + AnimationProcessor(SlideUpBuilder builder, ValueAnimator.AnimatorUpdateListener updateListener, Animator.AnimatorListener listener){ + mBuilder = builder; + createAnimation(updateListener, listener); + } + + void endAnimation() { + if (mValueAnimator != null && mValueAnimator.getValues() != null && mValueAnimator.isRunning()) { + mValueAnimator.end(); + } + } + + void paramsChanged(){ + mValueAnimator.setDuration(mBuilder.mAutoSlideDuration); + mValueAnimator.setInterpolator(mBuilder.mInterpolator); + } + + float getSlideAnimationTo() { + return mSlideAnimationTo; + } + + boolean isAnimationRunning(){ + return mValueAnimator != null && mValueAnimator.isRunning(); + } + + void setValuesAndStart(float from, float to){ + mSlideAnimationTo = to; + mValueAnimator.setFloatValues(from, to); + mValueAnimator.start(); + } + + private void createAnimation(ValueAnimator.AnimatorUpdateListener updateListener, Animator.AnimatorListener listener){ + mValueAnimator = ValueAnimator.ofFloat(); + mValueAnimator.setDuration(mBuilder.mAutoSlideDuration); + mValueAnimator.setInterpolator(mBuilder.mInterpolator); + mValueAnimator.addUpdateListener(updateListener); + mValueAnimator.addListener(listener); + } +} diff --git a/library/src/main/java/com/mancj/slideup/HorizontalTouchConsumer.java b/library/src/main/java/com/mancj/slideup/HorizontalTouchConsumer.java new file mode 100644 index 0000000..fd02273 --- /dev/null +++ b/library/src/main/java/com/mancj/slideup/HorizontalTouchConsumer.java @@ -0,0 +1,98 @@ +package com.mancj.slideup; + +import android.view.MotionEvent; + +/** + * @author pa.gulko zTrap (12.07.2017) + */ +class HorizontalTouchConsumer extends TouchConsumer { + + HorizontalTouchConsumer(SlideUpBuilder builder, LoggerNotifier notifier, AnimationProcessor animationProcessor) { + super(builder, notifier, animationProcessor); + } + + boolean consumeEndToStart(MotionEvent event){ + float touchedArea = event.getX(); + switch (event.getActionMasked()){ + case MotionEvent.ACTION_DOWN: + mViewWidth = mBuilder.mSliderView.getWidth(); + mStartPositionX = event.getRawX(); + mViewStartPositionX = mBuilder.mSliderView.getTranslationX(); + mCanSlide = getStart() + mBuilder.mTouchableArea >= touchedArea; + break; + case MotionEvent.ACTION_MOVE: + float difference = event.getRawX() - mStartPositionX; + float moveTo = mViewStartPositionX + difference; + float percents = moveTo * 100 / mBuilder.mSliderView.getWidth(); + + if (moveTo > 0 && mCanSlide){ + mNotifier.notifyPercentChanged(percents); + mBuilder.mSliderView.setTranslationX(moveTo); + } + if (event.getRawX() > mMaxSlidePosition) { + mMaxSlidePosition = event.getRawX(); + } + break; + case MotionEvent.ACTION_UP: + float slideAnimationFrom = mBuilder.mSliderView.getTranslationX(); + if (slideAnimationFrom == mViewStartPositionX){ + return !Internal.isUpEventInView(mBuilder.mSliderView, event); + } + boolean mustShow = mMaxSlidePosition > event.getRawX(); + boolean scrollableAreaConsumed = mBuilder.mSliderView.getTranslationX() > mBuilder.mSliderView.getWidth() / 5; + + if (scrollableAreaConsumed && !mustShow){ + mAnimationProcessor.setValuesAndStart(slideAnimationFrom, mBuilder.mSliderView.getWidth()); + }else { + mAnimationProcessor.setValuesAndStart(slideAnimationFrom, 0); + } + mCanSlide = true; + mMaxSlidePosition = 0; + break; + } + return true; + } + + boolean consumeStartToEnd(MotionEvent event){ + float touchedArea = event.getX(); + switch (event.getActionMasked()){ + case MotionEvent.ACTION_DOWN: + mMaxSlidePosition = mViewWidth; + mViewWidth = mBuilder.mSliderView.getWidth(); + mStartPositionX = event.getRawX(); + mViewStartPositionX = mBuilder.mSliderView.getTranslationX(); + mCanSlide = getEnd() - mBuilder.mTouchableArea >= touchedArea; + break; + case MotionEvent.ACTION_MOVE: + float difference = event.getRawX() - mStartPositionX; + float moveTo = mViewStartPositionX + difference; + float percents = moveTo * 100 / -mBuilder.mSliderView.getWidth(); + + if (moveTo < 0 && mCanSlide){ + mNotifier.notifyPercentChanged(percents); + mBuilder.mSliderView.setTranslationX(moveTo); + } + if (event.getRawX() < mMaxSlidePosition) { + mMaxSlidePosition = event.getRawX(); + } + break; + case MotionEvent.ACTION_UP: + float slideAnimationFrom = -mBuilder.mSliderView.getTranslationX(); + if (slideAnimationFrom == mViewStartPositionX){ + return !Internal.isUpEventInView(mBuilder.mSliderView, event); + } + boolean mustShow = mMaxSlidePosition < event.getRawX(); + boolean scrollableAreaConsumed = mBuilder.mSliderView.getTranslationX() < -mBuilder.mSliderView.getHeight() / 5; + + if (scrollableAreaConsumed && !mustShow){ + mAnimationProcessor.setValuesAndStart(slideAnimationFrom, mBuilder.mSliderView.getWidth()); + }else { + mAnimationProcessor.setValuesAndStart(slideAnimationFrom, 0); + } + mCanSlide = true; + mMaxSlidePosition = 0; + break; + } + return true; + } +} diff --git a/library/src/main/java/com/mancj/slideup/Internal.java b/library/src/main/java/com/mancj/slideup/Internal.java new file mode 100644 index 0000000..5ff7df1 --- /dev/null +++ b/library/src/main/java/com/mancj/slideup/Internal.java @@ -0,0 +1,33 @@ +package com.mancj.slideup; + +import android.view.MotionEvent; +import android.view.View; + +/** + * @author pa.gulko zTrap (12.07.2017) + */ +class Internal { + + static void checkNonNull(Object obj, String message) { + if (obj == null) { + throw new NullPointerException(message); + } + } + + static boolean isUpEventInView(View view, MotionEvent event){ + int top = view.getTop(); + int bottom = view.getBottom(); + int right = view.getRight(); + int left = view.getLeft(); + if (event.getRawY() > top){ + if (event.getRawY() < bottom){ + if (event.getRawX() > left){ + if (event.getRawX() < right){ + return true; + } + } + } + } + return false; + } +} diff --git a/library/src/main/java/com/mancj/slideup/LoggerNotifier.java b/library/src/main/java/com/mancj/slideup/LoggerNotifier.java index 389427f..bb12990 100644 --- a/library/src/main/java/com/mancj/slideup/LoggerNotifier.java +++ b/library/src/main/java/com/mancj/slideup/LoggerNotifier.java @@ -3,7 +3,7 @@ /** * @author pa.gulko zTrap (05.07.2017) */ -public interface LoggerNotifier { +interface LoggerNotifier { void notifyPercentChanged(float percent); diff --git a/library/src/main/java/com/mancj/slideup/OnGlobalLayoutSingleListener.java b/library/src/main/java/com/mancj/slideup/OnGlobalLayoutSingleListener.java new file mode 100644 index 0000000..39b0214 --- /dev/null +++ b/library/src/main/java/com/mancj/slideup/OnGlobalLayoutSingleListener.java @@ -0,0 +1,29 @@ +package com.mancj.slideup; + +import android.os.Build; +import android.view.View; +import android.view.ViewTreeObserver; + +/** + * @author pa.gulko zTrap (12.07.2017) + */ +public class OnGlobalLayoutSingleListener implements ViewTreeObserver.OnGlobalLayoutListener { + private final View mView; + private final Runnable mRunnable; + + OnGlobalLayoutSingleListener(View view, Runnable runnable) { + mView = view; + mRunnable = runnable; + } + + @Override + public final void onGlobalLayout() { + ViewTreeObserver observer = mView.getViewTreeObserver(); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) { + observer.removeGlobalOnLayoutListener(this); + } else { + observer.removeOnGlobalLayoutListener(this); + } + mRunnable.run(); + } +} diff --git a/library/src/main/java/com/mancj/slideup/SlideUp.java b/library/src/main/java/com/mancj/slideup/SlideUp.java index 7464773..c1eb40b 100644 --- a/library/src/main/java/com/mancj/slideup/SlideUp.java +++ b/library/src/main/java/com/mancj/slideup/SlideUp.java @@ -4,24 +4,17 @@ import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.content.Context; -import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.support.annotation.IntDef; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.util.Log; import android.view.MotionEvent; import android.view.View; -import android.view.ViewTreeObserver; -import android.view.animation.DecelerateInterpolator; import android.view.inputmethod.InputMethodManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import static android.view.Gravity.BOTTOM; import static android.view.Gravity.END; @@ -34,327 +27,137 @@ public class SlideUp implements View.OnTouchListener, ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener, LoggerNotifier { private final static String TAG = SlideUp.class.getSimpleName(); - - private final static String KEY_START_GRAVITY = TAG + "_start_gravity"; - private final static String KEY_DEBUG = TAG + "_debug"; - private final static String KEY_TOUCHABLE_AREA = TAG + "_touchable_area"; - private final static String KEY_STATE = TAG + "_state"; - private final static String KEY_AUTO_SLIDE_DURATION = TAG + "_auto_slide_duration"; - private final static String KEY_HIDE_SOFT_INPUT = TAG + "_hide_soft_input"; - private final static String KEY_STATE_SAVED = TAG + "_state_saved"; + + final static String KEY_START_GRAVITY = TAG + "_start_gravity"; + final static String KEY_DEBUG = TAG + "_debug"; + final static String KEY_TOUCHABLE_AREA = TAG + "_touchable_area"; + final static String KEY_STATE = TAG + "_state"; + final static String KEY_AUTO_SLIDE_DURATION = TAG + "_auto_slide_duration"; + final static String KEY_HIDE_SOFT_INPUT = TAG + "_hide_soft_input"; + final static String KEY_STATE_SAVED = TAG + "_state_saved"; /** *

Available start states

- * */ - public enum State{ + */ + public enum State { /** * State hidden is equal {@link View#GONE} - * */ + */ HIDDEN, - + /** * State showed is equal {@link View#VISIBLE} - * */ + */ SHOWED } - + @IntDef(value = {START, END, TOP, BOTTOM}) @Retention(RetentionPolicy.SOURCE) - private @interface StartVector{} + @interface StartVector { + } - private State mStartState; private State mCurrentState; - private View mSliderView; - private float mTouchableArea; - private int mAutoSlideDuration; - private List mListeners; - - private ValueAnimator mValueAnimator; - private float mSlideAnimationTo; - - private float mStartPositionY; - private float mStartPositionX; - private float mViewStartPositionY; - private float mViewStartPositionX; - private boolean mCanSlide = true; - private float mDensity; - private float mMaxSlidePosition; + private float mViewHeight; private float mViewWidth; - private boolean mHideKeyboard; - private TimeInterpolator mInterpolator; - - private boolean mGesturesEnabled; - - private boolean mIsRTL; - private int mStartGravity; + private SlideUpBuilder mBuilder; + + private VerticalTouchConsumer mVerticalTouchConsumer; + private HorizontalTouchConsumer mHorizontalTouchConsumer; + + private AnimationProcessor mAnimationProcessor; - private boolean mDebug; - /** *

Interface to listen to all handled events taking place in the slider

- * */ + */ public interface Listener { - interface Slide extends Listener{ + interface Slide extends Listener { /** * @param percent percents of complete slide (100 = HIDDEN, 0 = SHOWED) - * */ + */ void onSlide(float percent); } - - interface Visibility extends Listener{ - + + interface Visibility extends Listener { + /** * @param visibility (GONE or VISIBLE) - * */ + */ void onVisibilityChanged(int visibility); } - interface Events extends Visibility, Slide{} + interface Events extends Visibility, Slide { + } } - /** - *

Default constructor for SlideUp

- * */ - public final static class Builder{ - private boolean mStateRestored = false; - - private View mSliderView; - private float mDensity; - private float mTouchableArea; - private boolean mIsRTL; - private State mStartState = HIDDEN; - private List mListeners = new ArrayList<>(); - private boolean mDebug = false; - private int mAutoSlideDuration = 300; - private int mStartGravity = BOTTOM; - private boolean mGesturesEnabled = true; - private boolean mHideKeyboard = false; - private TimeInterpolator mInterpolator = new DecelerateInterpolator(); - - /** - *

Construct a SlideUp by passing the view or his child to use for the generation

- * */ - public Builder(@NonNull View sliderView){ - this.mSliderView = sliderView; - mDensity = sliderView.getResources().getDisplayMetrics().density; - mIsRTL = sliderView.getResources().getBoolean(R.bool.is_right_to_left); - mTouchableArea = 300 * mDensity; - } - - /** - *

Define a start state on screen

- * - * @param startState (default - {@link State#HIDDEN}) - * */ - public Builder withStartState(@NonNull State startState){ - if (!mStateRestored) { - this.mStartState = startState; - } - return this; - } - - /** - *

Define a start gravity, this parameter affects the motion vector slider

- * - * @param gravity (default - {@link android.view.Gravity#BOTTOM}) - * */ - public Builder withStartGravity(@StartVector int gravity){ - if (!mStateRestored) { - mStartGravity = gravity; - } - return this; - } - - /** - *

Define a {@link Listener} for this SlideUp

- * - * @param listeners {@link List} of listeners - * */ - public Builder withListeners(@NonNull List listeners){ - this.mListeners = listeners; - return this; - } - - /** - *

Define a {@link Listener} for this SlideUp

- * - * @param listeners array of listeners - * */ - public Builder withListeners(@NonNull Listener... listeners){ - List listeners_list = new ArrayList<>(); - Collections.addAll(listeners_list, listeners); - return withListeners(listeners_list); - } - - /** - *

Turning on/off debug logging for all handled events

- * - * @param enabled (default - false) - * */ - public Builder withLoggingEnabled(boolean enabled){ - if (!mStateRestored) { - mDebug = enabled; - } - return this; - } - - /** - *

Define duration of animation (whenever you use {@link #hide()} or {@link #show()} methods)

- * - * @param duration (default - 300) - * */ - public Builder withAutoSlideDuration(int duration){ - if (!mStateRestored) { - mAutoSlideDuration = duration; - } - return this; - } - - /** - *

Define touchable area (in dp) for interaction

- * - * @param area (default - 300dp) - * */ - public Builder withTouchableArea(float area){ - if (!mStateRestored) { - mTouchableArea = area * mDensity; - } - return this; - } - - /** - *

Turning on/off sliding on touch event

- * - * @param enabled (default - true) - * */ - public Builder withGesturesEnabled(boolean enabled){ - mGesturesEnabled = enabled; - return this; - } - - /** - *

Define behavior of soft input

- * - * @param hide (default - false) - * */ - public Builder withHideSoftInputWhenDisplayed(boolean hide){ - if (!mStateRestored) { - mHideKeyboard = hide; - } - return this; - } - - /** - *

Define interpolator for animation (whenever you use {@link #hide()} or {@link #show()} methods)

- * - * @param interpolator (default - Decelerate interpolator) - * */ - public Builder withInterpolator(TimeInterpolator interpolator){ - this.mInterpolator = interpolator; - return this; - } - - /** - * @param savedState parameters will be restored from this bundle, if it contains them - * */ - public Builder withSavedState(@Nullable Bundle savedState){ - restoreParams(savedState); - return this; - } - - /** - *

Build the SlideUp and add behavior to view

- * */ - public SlideUp build(){ - return new SlideUp(this); - } - - /** - *

Trying restore saved state

- * */ - private void restoreParams(@Nullable Bundle savedState){ - if (savedState == null) return; - mStateRestored = savedState.getBoolean(KEY_STATE_SAVED, false); - if (savedState.getSerializable(KEY_STATE) != null) { - mStartState = (SlideUp.State) savedState.getSerializable(KEY_STATE); - } - mStartGravity = savedState.getInt(KEY_START_GRAVITY, mStartGravity); - mDebug = savedState.getBoolean(KEY_DEBUG, mDebug); - mTouchableArea = savedState.getFloat(KEY_TOUCHABLE_AREA, mTouchableArea) * mDensity; - mAutoSlideDuration = savedState.getInt(KEY_AUTO_SLIDE_DURATION, mAutoSlideDuration); - mHideKeyboard = savedState.getBoolean(KEY_HIDE_SOFT_INPUT, mHideKeyboard); - } + SlideUp(SlideUpBuilder builder) { + mBuilder = builder; + init(); } - /** - *

Trying hide soft input from window

- * - * @see InputMethodManager#hideSoftInputFromWindow(IBinder, int) - * */ - public void hideSoftInput(){ - ((InputMethodManager) mSliderView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE)) - .hideSoftInputFromWindow(mSliderView.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + private void init() { + mBuilder.mSliderView.setOnTouchListener(this); + createAnimation(); + mBuilder.mSliderView.getViewTreeObserver().addOnGlobalLayoutListener( + new OnGlobalLayoutSingleListener(mBuilder.mSliderView, new Runnable() { + @Override + public void run() { + + mViewHeight = mBuilder.mSliderView.getHeight(); + mViewWidth = mBuilder.mSliderView.getWidth(); + switch (mBuilder.mStartGravity) { + case TOP: + mBuilder.mSliderView.setPivotY(mViewHeight); + setTouchableAreaVertical(); + break; + case BOTTOM: + mBuilder.mSliderView.setPivotY(0); + setTouchableAreaVertical(); + break; + case START: + mBuilder.mSliderView.setPivotX(0); + setTouchableAreaHorizontal(); + break; + case END: + mBuilder.mSliderView.setPivotX(mViewWidth); + setTouchableAreaHorizontal(); + break; + } + createConsumers(); + updateToCurrentState(); + } + })); + updateToCurrentState(); } - /** - *

Trying show soft input to window

- * - * @see InputMethodManager#showSoftInput(View, int) - * */ - public void showSoftInput(){ - ((InputMethodManager) mSliderView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE)) - .showSoftInput(mSliderView, 0); + private void setTouchableAreaHorizontal(){ + if (mBuilder.mTouchableArea == 0) { + mBuilder.mTouchableArea = (float) Math.ceil(mViewWidth / 10); + } } - private SlideUp(Builder builder){ - mStartGravity = builder.mStartGravity; - mListeners = builder.mListeners; - mSliderView = builder.mSliderView; - mStartState = builder.mStartState; - mDensity = builder.mDensity; - mTouchableArea = builder.mTouchableArea; - mAutoSlideDuration = builder.mAutoSlideDuration; - mDebug = builder.mDebug; - mIsRTL = builder.mIsRTL; - mGesturesEnabled = builder.mGesturesEnabled; - mHideKeyboard = builder.mHideKeyboard; - mInterpolator = builder.mInterpolator; - init(); + private void setTouchableAreaVertical(){ + if (mBuilder.mTouchableArea == 0) { + mBuilder.mTouchableArea = (float) Math.ceil(mViewHeight / 10); + } } - - private void init() { - mSliderView.setOnTouchListener(this); + + private void createAnimation() { + mAnimationProcessor = new AnimationProcessor(mBuilder, this, this); + } + + private void createConsumers() { createAnimation(); - mSliderView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - mViewHeight = mSliderView.getHeight(); - mViewWidth = mSliderView.getWidth(); - switch (mStartGravity){ - case TOP: mSliderView.setPivotY(mViewHeight); break; - case BOTTOM: mSliderView.setPivotY(0); break; - case START: mSliderView.setPivotX(0); break; - case END: mSliderView.setPivotX(mViewWidth); break; - } - updateToCurrentState(); - ViewTreeObserver observer = mSliderView.getViewTreeObserver(); - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) { - observer.removeGlobalOnLayoutListener(this); - } else { - observer.removeOnGlobalLayoutListener(this); - } - } - }); - updateToCurrentState(); + mVerticalTouchConsumer = new VerticalTouchConsumer(mBuilder, this, mAnimationProcessor); + mHorizontalTouchConsumer = new HorizontalTouchConsumer(mBuilder, this, mAnimationProcessor); } - + private void updateToCurrentState() { - switch (mStartState){ + switch (mBuilder.mStartState) { case HIDDEN: hideImmediately(); break; @@ -363,665 +166,525 @@ private void updateToCurrentState() { break; } } - + + //region public interface + /** + *

Trying hide soft input from window

+ * + * @see InputMethodManager#hideSoftInputFromWindow(IBinder, int) + */ + public void hideSoftInput() { + ((InputMethodManager) mBuilder.mSliderView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE)) + .hideSoftInputFromWindow(mBuilder.mSliderView.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + + /** + *

Trying show soft input to window

+ * + * @see InputMethodManager#showSoftInput(View, int) + */ + public void showSoftInput() { + ((InputMethodManager) mBuilder.mSliderView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE)) + .showSoftInput(mBuilder.mSliderView, 0); + } + /** *

Returns the visibility status for this view.

* * @return true if view have status {@link View#VISIBLE} */ - public boolean isVisible(){ - return mSliderView.getVisibility() == VISIBLE; + public boolean isVisible() { + return mBuilder.mSliderView.getVisibility() == VISIBLE; } - + /** *

Add Listener which will be used in combination with this SlideUp

- * */ - public void addSlideListener(@NonNull Listener listener){ - mListeners.add(listener); + */ + public void addSlideListener(@NonNull Listener listener) { + mBuilder.mListeners.add(listener); } - + /** *

Remove Listener which was used in combination with this SlideUp

- * */ - public void removeSlideListener(@NonNull Listener listener){ - mListeners.remove(listener); + */ + public void removeSlideListener(@NonNull Listener listener) { + mBuilder.mListeners.remove(listener); } - + /** *

Returns typed view which was used as slider

- * */ + */ public T getSliderView() { - return (T) mSliderView; + return (T) mBuilder.mSliderView; } - + /** *

Set duration of animation (whenever you use {@link #hide()} or {@link #show()} methods)

* * @param autoSlideDuration (default - 300) - * */ + */ public void setAutoSlideDuration(int autoSlideDuration) { - this.mAutoSlideDuration = autoSlideDuration; + mBuilder.withAutoSlideDuration(autoSlideDuration); + mAnimationProcessor.paramsChanged(); } - + /** *

Returns duration of animation (whenever you use {@link #hide()} or {@link #show()} methods)

- * */ - public float getAutoSlideDuration(){ - return this.mAutoSlideDuration; + */ + public float getAutoSlideDuration() { + return mBuilder.mAutoSlideDuration; } - + /** *

Set touchable area (in dp) for interaction

* * @param touchableArea (default - 300dp) - * */ - public void setTouchableArea(float touchableArea) { - this.mTouchableArea = touchableArea * mDensity; + */ + public void setTouchableAreaDp(float touchableArea) { + mBuilder.withTouchableAreaDp(touchableArea); } - + + /** + *

Set touchable area (in px) for interaction

+ * + * @param touchableArea (default - 300dp) + */ + public void setTouchableAreaPx(float touchableArea) { + mBuilder.withTouchableAreaPx(touchableArea); + } + /** *

Returns touchable area (in dp) for interaction

- * */ - public float getTouchableArea() { - return this.mTouchableArea / mDensity; + */ + public float getTouchableAreaDp() { + return mBuilder.mTouchableArea / mBuilder.mDensity; } - + + /** + *

Returns touchable area (in px) for interaction

+ */ + public float getTouchableAreaPx() { + return mBuilder.mTouchableArea; + } + /** *

Returns running status of animation

* * @return true if animation is running - * */ - public boolean isAnimationRunning(){ - return mValueAnimator != null && mValueAnimator.isRunning(); + */ + public boolean isAnimationRunning() { + return mAnimationProcessor.isAnimationRunning(); } - + /** *

Show view with animation

- * */ - public void show(){ + */ + public void show() { show(false); } - + /** *

Hide view with animation

- * */ - public void hide(){ + */ + public void hide() { hide(false); } - + /** *

Hide view without animation

- * */ + */ public void hideImmediately() { hide(true); } - + /** *

Show view without animation

- * */ + */ public void showImmediately() { show(true); } - + /** *

Turning on/off debug logging

* * @param enabled (default - false) - * */ - public void setLoggingEnabled(boolean enabled){ - mDebug = enabled; + */ + public void setLoggingEnabled(boolean enabled) { + mBuilder.withLoggingEnabled(enabled); } - + /** *

Returns current status of debug logging

- * */ - public boolean isLoggingEnabled(){ - return mDebug; + */ + public boolean isLoggingEnabled() { + return mBuilder.mDebug; } - + /** *

Turning on/off gestures

* * @param enabled (default - true) - * */ + */ public void setGesturesEnabled(boolean enabled) { - this.mGesturesEnabled = enabled; + mBuilder.withGesturesEnabled(enabled); } - + /** *

Returns current status of gestures

- * */ + */ public boolean isGesturesEnabled() { - return mGesturesEnabled; + return mBuilder.mGesturesEnabled; } /** *

Returns current interpolator

- * */ + */ public TimeInterpolator getInterpolator() { - return mInterpolator; + return mBuilder.mInterpolator; } /** *

Returns gravity which used in combination with this SlideUp

- * */ + */ @StartVector public int getStartGravity() { - return mStartGravity; + return mBuilder.mStartGravity; } /** *

Sets interpolator for animation (whenever you use {@link #hide()} or {@link #show()} methods)

* * @param interpolator (default - Decelerate interpolator) - * */ + */ public void setInterpolator(TimeInterpolator interpolator) { - mValueAnimator.setInterpolator(this.mInterpolator = interpolator); + mBuilder.withInterpolator(interpolator); + mAnimationProcessor.paramsChanged(); } /** *

Returns current behavior of soft input

- * */ + */ public boolean isHideKeyboardWhenDisplayed() { - return mHideKeyboard; + return mBuilder.mHideKeyboard; } /** *

Sets behavior of soft input

* * @param hide (default - false) - * */ + */ public void setHideKeyboardWhenDisplayed(boolean hide) { - mHideKeyboard = hide; + mBuilder.withHideSoftInputWhenDisplayed(hide); } - + /** *

Toggle current state with animation

- * */ - public void toggle(){ + */ + public void toggle() { if (isVisible()) { hide(); } else { show(); } } - + /** *

Toggle current state without animation

- * */ - public void toggleImmediately(){ + */ + public void toggleImmediately() { if (isVisible()) { hideImmediately(); } else { showImmediately(); } } - + /** *

Saving current parameters of SlideUp

- * */ - public void onSaveInstanceState(@NonNull Bundle savedState){ + */ + public void onSaveInstanceState(@NonNull Bundle savedState) { savedState.putBoolean(KEY_STATE_SAVED, true); - savedState.putInt(KEY_START_GRAVITY, mStartGravity); - savedState.putBoolean(KEY_DEBUG, mDebug); - savedState.putFloat(KEY_TOUCHABLE_AREA, mTouchableArea / mDensity); + savedState.putInt(KEY_START_GRAVITY, mBuilder.mStartGravity); + savedState.putBoolean(KEY_DEBUG, mBuilder.mDebug); + savedState.putFloat(KEY_TOUCHABLE_AREA, mBuilder.mTouchableArea / mBuilder.mDensity); savedState.putSerializable(KEY_STATE, mCurrentState); - savedState.putInt(KEY_AUTO_SLIDE_DURATION, mAutoSlideDuration); - savedState.putBoolean(KEY_HIDE_SOFT_INPUT, mHideKeyboard); + savedState.putInt(KEY_AUTO_SLIDE_DURATION, mBuilder.mAutoSlideDuration); + savedState.putBoolean(KEY_HIDE_SOFT_INPUT, mBuilder.mHideKeyboard); } - - private void endAnimation(){ - if (mValueAnimator != null && mValueAnimator.getValues() != null && mValueAnimator.isRunning()) { - mValueAnimator.end(); - } - } - + //endregion + private void hide(boolean immediately) { - endAnimation(); - switch (mStartGravity){ + mAnimationProcessor.endAnimation(); + switch (mBuilder.mStartGravity) { case TOP: - if (immediately){ - if (mSliderView.getHeight() > 0){ - mSliderView.setTranslationY(-mViewHeight); + if (immediately) { + if (mBuilder.mSliderView.getHeight() > 0) { + mBuilder.mSliderView.setTranslationY(-mViewHeight); notifyVisibilityChanged(GONE); - }else { - mStartState = HIDDEN; + } else { + mBuilder.mStartState = HIDDEN; } - }else { - setValuesAndStart(mSliderView.getTranslationY(), mSliderView.getHeight()); + } else { + mAnimationProcessor.setValuesAndStart(mBuilder.mSliderView.getTranslationY(), mBuilder.mSliderView.getHeight()); } break; case BOTTOM: - if (immediately){ - if (mSliderView.getHeight() > 0){ - mSliderView.setTranslationY(mViewHeight); + if (immediately) { + if (mBuilder.mSliderView.getHeight() > 0) { + mBuilder.mSliderView.setTranslationY(mViewHeight); notifyVisibilityChanged(GONE); - }else { - mStartState = HIDDEN; + } else { + mBuilder.mStartState = HIDDEN; } - }else { - setValuesAndStart(mSliderView.getTranslationY(), mSliderView.getHeight()); + } else { + mAnimationProcessor.setValuesAndStart(mBuilder.mSliderView.getTranslationY(), mBuilder.mSliderView.getHeight()); } break; case START: - if (immediately){ - if (mSliderView.getWidth() > 0){ - mSliderView.setTranslationX(-mViewWidth); + if (immediately) { + if (mBuilder.mSliderView.getWidth() > 0) { + mBuilder.mSliderView.setTranslationX(-mViewWidth); notifyVisibilityChanged(GONE); - }else { - mStartState = HIDDEN; + } else { + mBuilder.mStartState = HIDDEN; } - }else { - setValuesAndStart(mSliderView.getTranslationX(), mSliderView.getHeight()); + } else { + mAnimationProcessor.setValuesAndStart(mBuilder.mSliderView.getTranslationX(), mBuilder.mSliderView.getHeight()); } break; case END: - if (immediately){ - if (mSliderView.getWidth() > 0){ - mSliderView.setTranslationX(mViewWidth); + if (immediately) { + if (mBuilder.mSliderView.getWidth() > 0) { + mBuilder.mSliderView.setTranslationX(mViewWidth); notifyVisibilityChanged(GONE); - }else { - mStartState = HIDDEN; + } else { + mBuilder.mStartState = HIDDEN; } - }else { - setValuesAndStart(mSliderView.getTranslationX(), mSliderView.getHeight()); + } else { + mAnimationProcessor.setValuesAndStart(mBuilder.mSliderView.getTranslationX(), mBuilder.mSliderView.getHeight()); } break; } } - private void show(boolean immediately){ - endAnimation(); - switch (mStartGravity) { + private void show(boolean immediately) { + mAnimationProcessor.endAnimation(); + switch (mBuilder.mStartGravity) { case TOP: - if (immediately){ - if (mSliderView.getHeight() > 0){ - mSliderView.setTranslationY(0); + if (immediately) { + if (mBuilder.mSliderView.getHeight() > 0) { + mBuilder.mSliderView.setTranslationY(0); notifyVisibilityChanged(VISIBLE); - }else { - mStartState = SHOWED; + } else { + mBuilder.mStartState = SHOWED; } - }else { - setValuesAndStart(mSliderView.getTranslationY(), 0); + } else { + mAnimationProcessor.setValuesAndStart(mBuilder.mSliderView.getTranslationY(), 0); } case BOTTOM: - if (immediately){ - if (mSliderView.getHeight() > 0){ - mSliderView.setTranslationY(0); + if (immediately) { + if (mBuilder.mSliderView.getHeight() > 0) { + mBuilder.mSliderView.setTranslationY(0); notifyVisibilityChanged(VISIBLE); - }else { - mStartState = SHOWED; + } else { + mBuilder.mStartState = SHOWED; } - }else { - setValuesAndStart(mSliderView.getTranslationY(), 0); + } else { + mAnimationProcessor.setValuesAndStart(mBuilder.mSliderView.getTranslationY(), 0); } break; case START: - if (immediately){ - if (mSliderView.getWidth() > 0){ - mSliderView.setTranslationX(0); + if (immediately) { + if (mBuilder.mSliderView.getWidth() > 0) { + mBuilder.mSliderView.setTranslationX(0); notifyVisibilityChanged(VISIBLE); - }else { - mStartState = SHOWED; + } else { + mBuilder.mStartState = SHOWED; } - }else { - setValuesAndStart(mSliderView.getTranslationX(), 0); + } else { + mAnimationProcessor.setValuesAndStart(mBuilder.mSliderView.getTranslationX(), 0); } case END: - if (immediately){ - if (mSliderView.getWidth() > 0){ - mSliderView.setTranslationX(0); + if (immediately) { + if (mBuilder.mSliderView.getWidth() > 0) { + mBuilder.mSliderView.setTranslationX(0); notifyVisibilityChanged(VISIBLE); - }else { - mStartState = SHOWED; + } else { + mBuilder.mStartState = SHOWED; } - }else { - setValuesAndStart(mSliderView.getTranslationX(), 0); + } else { + mAnimationProcessor.setValuesAndStart(mBuilder.mSliderView.getTranslationX(), 0); } break; } } - private void setValuesAndStart(float from, float to){ - mSlideAnimationTo = to; - mValueAnimator.setFloatValues(from, to); - mValueAnimator.start(); - } - - private void createAnimation(){ - mValueAnimator = ValueAnimator.ofFloat(); - mValueAnimator.setDuration(mAutoSlideDuration); - mValueAnimator.setInterpolator(mInterpolator); - mValueAnimator.addUpdateListener(this); - mValueAnimator.addListener(this); - } - @Override public final boolean onTouch(View v, MotionEvent event) { - if (!mGesturesEnabled) return false; - if (isAnimationRunning()) return false; - switch (mStartGravity){ + if (mAnimationProcessor.isAnimationRunning()) return false; + if (!mBuilder.mGesturesEnabled){ + mBuilder.mSliderView.performClick(); + } + boolean consumed; + switch (mBuilder.mStartGravity) { case TOP: - return onTouchUpToDown(event); + consumed = mVerticalTouchConsumer.consumeTopToBottom(event); + break; case BOTTOM: - return onTouchDownToUp(event); + consumed = mVerticalTouchConsumer.consumeBottomToTop(event); + break; case START: - return onTouchStartToEnd(event); + consumed = mHorizontalTouchConsumer.consumeStartToEnd(event); + break; case END: - return onTouchEndToStart(event); + consumed = mHorizontalTouchConsumer.consumeEndToStart(event); + break; default: - e("onTouchListener", "(onTouch)", "You are using not supportable gravity"); - return false; + throw new IllegalArgumentException("You are using not supported gravity"); } - } - - private boolean onTouchEndToStart(MotionEvent event){ - float touchedArea = event.getRawX() - getEnd(); - switch (event.getActionMasked()){ - case MotionEvent.ACTION_DOWN: - mViewWidth = mSliderView.getWidth(); - mStartPositionX = event.getRawX(); - mViewStartPositionX = mSliderView.getTranslationX(); - if (mTouchableArea < touchedArea){ - mCanSlide = false; - } - break; - case MotionEvent.ACTION_MOVE: - float difference = event.getRawX() - mStartPositionX; - float moveTo = mViewStartPositionX + difference; - float percents = moveTo * 100 / mSliderView.getWidth(); - - if (moveTo > 0 && mCanSlide){ - notifyPercentChanged(percents); - mSliderView.setTranslationX(moveTo); - } - if (event.getRawX() > mMaxSlidePosition) { - mMaxSlidePosition = event.getRawX(); - } - break; - case MotionEvent.ACTION_UP: - float slideAnimationFrom = mSliderView.getTranslationX(); - if (slideAnimationFrom == mViewStartPositionX) return false; - boolean mustShow = mMaxSlidePosition > event.getRawX(); - boolean scrollableAreaConsumed = mSliderView.getTranslationX() > mSliderView.getWidth() / 5; - - if (scrollableAreaConsumed && !mustShow){ - mSlideAnimationTo = mSliderView.getWidth(); - }else { - mSlideAnimationTo = 0; - } - mValueAnimator.setFloatValues(slideAnimationFrom, mSlideAnimationTo); - mValueAnimator.start(); - mCanSlide = true; - mMaxSlidePosition = 0; - break; + if (!consumed){ + mBuilder.mSliderView.performClick(); } return true; } - - private boolean onTouchStartToEnd(MotionEvent event){ - float touchedArea = getEnd() - event.getRawX(); - switch (event.getActionMasked()){ - case MotionEvent.ACTION_DOWN: - mMaxSlidePosition = mViewWidth; - mViewWidth = mSliderView.getWidth(); - mStartPositionX = event.getRawX(); - mViewStartPositionX = mSliderView.getTranslationX(); - if (mTouchableArea < touchedArea){ - mCanSlide = false; - } - break; - case MotionEvent.ACTION_MOVE: - float difference = event.getRawX() - mStartPositionX; - float moveTo = mViewStartPositionX + difference; - float percents = moveTo * 100 / -mSliderView.getWidth(); - - if (moveTo < 0 && mCanSlide){ - notifyPercentChanged(percents); - mSliderView.setTranslationX(moveTo); - } - if (event.getRawX() < mMaxSlidePosition) { - mMaxSlidePosition = event.getRawX(); - } - break; - case MotionEvent.ACTION_UP: - float slideAnimationFrom = -mSliderView.getTranslationX(); - if (slideAnimationFrom == mViewStartPositionX) return false; - boolean mustShow = mMaxSlidePosition < event.getRawX(); - boolean scrollableAreaConsumed = mSliderView.getTranslationX() < -mSliderView.getHeight() / 5; - - if (scrollableAreaConsumed && !mustShow){ - mSlideAnimationTo = mSliderView.getWidth(); - }else { - mSlideAnimationTo = 0; - } - mValueAnimator.setFloatValues(slideAnimationFrom, mSlideAnimationTo); - mValueAnimator.start(); - mCanSlide = true; - mMaxSlidePosition = 0; + + @Override + public final void onAnimationUpdate(ValueAnimator animation) { + float value = (float) animation.getAnimatedValue(); + switch (mBuilder.mStartGravity) { + case TOP: + onAnimationUpdateTopToBottom(value); break; - } - return true; - } - - private boolean onTouchDownToUp(MotionEvent event){ - float touchedArea = event.getRawY() - mSliderView.getTop(); - switch (event.getActionMasked()){ - case MotionEvent.ACTION_DOWN: - mViewHeight = mSliderView.getHeight(); - mStartPositionY = event.getRawY(); - mViewStartPositionY = mSliderView.getTranslationY(); - mCanSlide = mTouchableArea >= touchedArea; + case BOTTOM: + onAnimationUpdateBottomToTop(value); break; - case MotionEvent.ACTION_MOVE: - float difference = event.getRawY() - mStartPositionY; - float moveTo = mViewStartPositionY + difference; - float percents = moveTo * 100 / mSliderView.getHeight(); - - if (moveTo > 0 && mCanSlide){ - notifyPercentChanged(percents); - mSliderView.setTranslationY(moveTo); - } - if (event.getRawY() > mMaxSlidePosition) { - mMaxSlidePosition = event.getRawY(); - } + case START: + onAnimationUpdateStartToEnd(value); break; - case MotionEvent.ACTION_UP: - float slideAnimationFrom = mSliderView.getTranslationY(); - if (slideAnimationFrom == mViewStartPositionY) return false; - boolean mustShow = mMaxSlidePosition > event.getRawY(); - boolean scrollableAreaConsumed = mSliderView.getTranslationY() > mSliderView.getHeight() / 5; - - if (scrollableAreaConsumed && !mustShow){ - mSlideAnimationTo = mSliderView.getHeight(); - }else { - mSlideAnimationTo = 0; - } - mValueAnimator.setFloatValues(slideAnimationFrom, mSlideAnimationTo); - mValueAnimator.start(); - mCanSlide = true; - mMaxSlidePosition = 0; + case END: + onAnimationUpdateEndToStart(value); break; } - return true; } - private boolean onTouchUpToDown(MotionEvent event){ - float touchedArea = event.getRawY() - mSliderView.getBottom(); - switch (event.getActionMasked()){ - case MotionEvent.ACTION_DOWN: - mViewHeight = mSliderView.getHeight(); - mStartPositionY = event.getRawY(); - mViewStartPositionY = mSliderView.getTranslationY(); - mMaxSlidePosition = mViewHeight; - if (mTouchableArea < touchedArea){ - mCanSlide = false; - } - break; - case MotionEvent.ACTION_MOVE: - float difference = event.getRawY() - mStartPositionY; - float moveTo = mViewStartPositionY + difference; - float percents = moveTo * 100 / -mSliderView.getHeight(); - - if (moveTo < 0 && mCanSlide){ - notifyPercentChanged(percents); - mSliderView.setTranslationY(moveTo); - } - if (event.getRawY() < mMaxSlidePosition) { - mMaxSlidePosition = event.getRawY(); - } - break; - case MotionEvent.ACTION_UP: - float slideAnimationFrom = -mSliderView.getTranslationY(); - if (slideAnimationFrom == mViewStartPositionY) return false; - boolean mustShow = mMaxSlidePosition < event.getRawY(); - boolean scrollableAreaConsumed = mSliderView.getTranslationY() < -mSliderView.getHeight() / 5; - - if (scrollableAreaConsumed && !mustShow){ - mSlideAnimationTo = mSliderView.getHeight() + mSliderView.getTop(); - }else { - mSlideAnimationTo = 0; - } - mValueAnimator.setFloatValues(slideAnimationFrom, mSlideAnimationTo); - mValueAnimator.start(); - mCanSlide = true; - mMaxSlidePosition = 0; - break; - } - return true; - } - - @Override - public final void onAnimationUpdate(ValueAnimator animation) { - float value = (float) animation.getAnimatedValue(); - switch (mStartGravity){ - case TOP: onAnimationUpdateUpToDown(value); break; - case BOTTOM: onAnimationUpdateDownToUp(value); break; - case START: onAnimationUpdateStartToEnd(value); break; - case END: onAnimationUpdateEndToStart(value); break; - } - } - - private void onAnimationUpdateUpToDown(float value){ - mSliderView.setTranslationY(-value); - float visibleDistance = mSliderView.getTop() - mSliderView.getY(); + private void onAnimationUpdateTopToBottom(float value) { + mBuilder.mSliderView.setTranslationY(-value); + float visibleDistance = mBuilder.mSliderView.getTop() - mBuilder.mSliderView.getY(); float percents = (visibleDistance) * 100 / mViewHeight; notifyPercentChanged(percents); } - - private void onAnimationUpdateDownToUp(float value){ - mSliderView.setTranslationY(value); - float visibleDistance = mSliderView.getY() - mSliderView.getTop(); + + private void onAnimationUpdateBottomToTop(float value) { + mBuilder.mSliderView.setTranslationY(value); + float visibleDistance = mBuilder.mSliderView.getY() - mBuilder.mSliderView.getTop(); float percents = (visibleDistance) * 100 / mViewHeight; notifyPercentChanged(percents); } - - private void onAnimationUpdateStartToEnd(float value){ - mSliderView.setTranslationX(-value); - float visibleDistance = mSliderView.getX() - getStart(); + + private void onAnimationUpdateStartToEnd(float value) { + mBuilder.mSliderView.setTranslationX(-value); + float visibleDistance = mBuilder.mSliderView.getX() - getStart(); float percents = (visibleDistance) * 100 / -mViewWidth; notifyPercentChanged(percents); } - - private void onAnimationUpdateEndToStart(float value){ - mSliderView.setTranslationX(value); - float visibleDistance = mSliderView.getX() - getStart(); + + private void onAnimationUpdateEndToStart(float value) { + mBuilder.mSliderView.setTranslationX(value); + float visibleDistance = mBuilder.mSliderView.getX() - getStart(); float percents = (visibleDistance) * 100 / mViewWidth; notifyPercentChanged(percents); } - - private int getStart(){ - if (mIsRTL){ - return mSliderView.getRight(); - }else { - return mSliderView.getLeft(); - } - } - - private int getEnd(){ - if (mIsRTL){ - return mSliderView.getLeft(); - }else { - return mSliderView.getRight(); + + private int getStart() { + if (mBuilder.mIsRTL) { + return mBuilder.mSliderView.getRight(); + } else { + return mBuilder.mSliderView.getLeft(); } } @Override - public void notifyPercentChanged(float percent){ + public void notifyPercentChanged(float percent) { percent = percent > 100 ? 100 : percent; percent = percent < 0 ? 0 : percent; - if (mSlideAnimationTo == 0 && mHideKeyboard) + if (mAnimationProcessor.getSlideAnimationTo() == 0 && mBuilder.mHideKeyboard) hideSoftInput(); - if (mListeners != null && !mListeners.isEmpty()){ - for (int i = 0; i < mListeners.size(); i++) { - Listener l = mListeners.get(i); - if (l != null){ + if (!mBuilder.mListeners.isEmpty()) { + for (int i = 0; i < mBuilder.mListeners.size(); i++) { + Listener l = mBuilder.mListeners.get(i); + if (l != null) { if (l instanceof Listener.Slide) { Listener.Slide slide = (Listener.Slide) l; slide.onSlide(percent); - d("Listener(" + i + ")", "(onSlide)", "value = " + percent); + logValue(i, "onSlide", percent); } - }else { - e("Listener(" + i + ")", "(onSlide)", "Listener is null, skip notification..."); + } else { + logError(i, "onSlide"); } } } } @Override - public void notifyVisibilityChanged(int visibility){ - mSliderView.setVisibility(visibility); - if (mListeners != null && !mListeners.isEmpty()){ - for (int i = 0; i < mListeners.size(); i++) { - Listener l = mListeners.get(i); + public void notifyVisibilityChanged(int visibility) { + mBuilder.mSliderView.setVisibility(visibility); + if (!mBuilder.mListeners.isEmpty()) { + for (int i = 0; i < mBuilder.mListeners.size(); i++) { + Listener l = mBuilder.mListeners.get(i); if (l != null) { if (l instanceof Listener.Visibility) { - Listener.Visibility vis = (Listener.Visibility)l; + Listener.Visibility vis = (Listener.Visibility) l; vis.onVisibilityChanged(visibility); - d("Listener(" + i + ")", "(onVisibilityChanged)", "value = " + (visibility == VISIBLE ? "VISIBLE" : visibility == GONE ? "GONE" : visibility)); + logValue(i, "onVisibilityChanged", visibility == VISIBLE ? "VISIBLE" : visibility == GONE ? "GONE" : visibility); } - }else { - e("Listener(" + i + ")", "(onVisibilityChanged)", "Listener is null, skip notify for him..."); + } else { + logError(i, "onVisibilityChanged"); } } } - switch (visibility){ - case VISIBLE: mCurrentState = SHOWED; break; - case GONE: mCurrentState = HIDDEN; break; + switch (visibility) { + case VISIBLE: + mCurrentState = SHOWED; + break; + case GONE: + mCurrentState = HIDDEN; + break; } } - + @Override public final void onAnimationStart(Animator animator) { - if (mSliderView.getVisibility() != VISIBLE) { + if (mBuilder.mSliderView.getVisibility() != VISIBLE) { notifyVisibilityChanged(VISIBLE); } } - + @Override public final void onAnimationEnd(Animator animator) { - if (mSlideAnimationTo != 0){ - if (mSliderView.getVisibility() != GONE) { + if (mAnimationProcessor.getSlideAnimationTo() != 0) { + if (mBuilder.mSliderView.getVisibility() != GONE) { notifyVisibilityChanged(GONE); } } } - + @Override - public final void onAnimationCancel(Animator animator) {} - + public final void onAnimationCancel(Animator animator) { + } + @Override - public final void onAnimationRepeat(Animator animator) {} + public final void onAnimationRepeat(Animator animator) { + } - private void e(String listener, String method, String message){ - if (mDebug) { - Log.e(TAG, String.format("%1$-15s %2$-23s %3$s", listener, method, message)); + private void logValue(int listener, String method, Object message) { + if (mBuilder.mDebug) { + Log.e(TAG, String.format("Listener(%1s) (%2$-23s) value = %3$s", listener, method, message)); } } - private void d(String listener, String method, String value){ - if (mDebug) { - Log.d(TAG, String.format("%1$-15s %2$-23s %3$s", listener, method, value)); + private void logError(int listener, String method) { + if (mBuilder.mDebug) { + Log.d(TAG, String.format("Listener(%1s) (%2$-23s) Listener is null, skip notification...", listener, method)); } } } diff --git a/library/src/main/java/com/mancj/slideup/SlideUpBuilder.java b/library/src/main/java/com/mancj/slideup/SlideUpBuilder.java new file mode 100644 index 0000000..bfe3ebe --- /dev/null +++ b/library/src/main/java/com/mancj/slideup/SlideUpBuilder.java @@ -0,0 +1,201 @@ +package com.mancj.slideup; + +import android.animation.TimeInterpolator; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.Gravity; +import android.view.View; +import android.view.animation.DecelerateInterpolator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + *

Default constructor for {@link SlideUp}

+ */ +public final class SlideUpBuilder { + private boolean mStateRestored = false; + + View mSliderView; + float mDensity; + float mTouchableArea; + boolean mIsRTL; + SlideUp.State mStartState = SlideUp.State.HIDDEN; + List mListeners = new ArrayList<>(); + boolean mDebug = false; + int mAutoSlideDuration = 300; + int mStartGravity = Gravity.BOTTOM; + boolean mGesturesEnabled = true; + boolean mHideKeyboard = false; + TimeInterpolator mInterpolator = new DecelerateInterpolator(); + + /** + *

Construct a SlideUp by passing the view or his child to use for the generation

+ */ + public SlideUpBuilder(View sliderView) { + Internal.checkNonNull(sliderView, "View can't be null"); + mSliderView = sliderView; + mDensity = sliderView.getResources().getDisplayMetrics().density; + mIsRTL = sliderView.getResources().getBoolean(R.bool.is_right_to_left); + } + + /** + *

Define a start state on screen

+ * + * @param startState (default - {@link SlideUp.State#HIDDEN}) + */ + public SlideUpBuilder withStartState(@NonNull SlideUp.State startState) { + if (!mStateRestored) { + mStartState = startState; + } + return this; + } + + /** + *

Define a start gravity, this parameter affects the motion vector slider

+ * + * @param gravity (default - {@link android.view.Gravity#BOTTOM}) + */ + public SlideUpBuilder withStartGravity(@SlideUp.StartVector int gravity) { + if (!mStateRestored) { + mStartGravity = gravity; + } + return this; + } + + /** + *

Define a {@link SlideUp.Listener} for this SlideUp

+ * + * @param listeners {@link List} of listeners + */ + public SlideUpBuilder withListeners(@NonNull List listeners) { + if (listeners != null) { + mListeners.addAll(listeners); + } + return this; + } + + /** + *

Define a {@link SlideUp.Listener} for this SlideUp

+ * + * @param listeners array of listeners + */ + public SlideUpBuilder withListeners(@NonNull SlideUp.Listener... listeners) { + List listeners_list = new ArrayList<>(); + Collections.addAll(listeners_list, listeners); + return withListeners(listeners_list); + } + + /** + *

Turning on/off debug logging for all handled events

+ * + * @param enabled (default - false) + */ + public SlideUpBuilder withLoggingEnabled(boolean enabled) { + if (!mStateRestored) { + mDebug = enabled; + } + return this; + } + + /** + *

Define duration of animation (whenever you use {@link SlideUp#hide()} or {@link SlideUp#show()} methods)

+ * + * @param duration (default - 300) + */ + public SlideUpBuilder withAutoSlideDuration(int duration) { + if (!mStateRestored) { + mAutoSlideDuration = duration; + } + return this; + } + + /** + *

Define touchable area (in px) for interaction

+ * + * @param area (default - 300dp) + */ + public SlideUpBuilder withTouchableAreaPx(float area) { + if (!mStateRestored) { + mTouchableArea = area; + } + return this; + } + + /** + *

Define touchable area (in dp) for interaction

+ * + * @param area (default - 300dp) + */ + public SlideUpBuilder withTouchableAreaDp(float area) { + if (!mStateRestored) { + mTouchableArea = area * mDensity; + } + return this; + } + + /** + *

Turning on/off sliding on touch event

+ * + * @param enabled (default - true) + */ + public SlideUpBuilder withGesturesEnabled(boolean enabled) { + mGesturesEnabled = enabled; + return this; + } + + /** + *

Define behavior of soft input

+ * + * @param hide (default - false) + */ + public SlideUpBuilder withHideSoftInputWhenDisplayed(boolean hide) { + if (!mStateRestored) { + mHideKeyboard = hide; + } + return this; + } + + /** + *

Define interpolator for animation (whenever you use {@link SlideUp#hide()} or {@link SlideUp#show()} methods)

+ * + * @param interpolator (default - Decelerate interpolator) + */ + public SlideUpBuilder withInterpolator(TimeInterpolator interpolator) { + mInterpolator = interpolator; + return this; + } + + /** + * @param savedState parameters will be restored from this bundle, if it contains them + */ + public SlideUpBuilder withSavedState(@Nullable Bundle savedState) { + restoreParams(savedState); + return this; + } + + /** + *

Build the SlideUp and add behavior to view

+ */ + public SlideUp build() { + return new SlideUp(this); + } + + /** + *

Trying restore saved state

+ */ + private void restoreParams(@Nullable Bundle savedState) { + if (savedState == null) return; + mStateRestored = savedState.getBoolean(SlideUp.KEY_STATE_SAVED, false); + if (savedState.getSerializable(SlideUp.KEY_STATE) != null) { + mStartState = (SlideUp.State) savedState.getSerializable(SlideUp.KEY_STATE); + } + mStartGravity = savedState.getInt(SlideUp.KEY_START_GRAVITY, mStartGravity); + mDebug = savedState.getBoolean(SlideUp.KEY_DEBUG, mDebug); + mTouchableArea = savedState.getFloat(SlideUp.KEY_TOUCHABLE_AREA, mTouchableArea) * mDensity; + mAutoSlideDuration = savedState.getInt(SlideUp.KEY_AUTO_SLIDE_DURATION, mAutoSlideDuration); + mHideKeyboard = savedState.getBoolean(SlideUp.KEY_HIDE_SOFT_INPUT, mHideKeyboard); + } +} \ No newline at end of file diff --git a/library/src/main/java/com/mancj/slideup/TouchConsumer.java b/library/src/main/java/com/mancj/slideup/TouchConsumer.java new file mode 100644 index 0000000..8036718 --- /dev/null +++ b/library/src/main/java/com/mancj/slideup/TouchConsumer.java @@ -0,0 +1,52 @@ +package com.mancj.slideup; + +/** + * @author pa.gulko zTrap (12.07.2017) + */ +class TouchConsumer { + SlideUpBuilder mBuilder; + AnimationProcessor mAnimationProcessor; + float mMaxSlidePosition; + + boolean mCanSlide = true; + LoggerNotifier mNotifier; + + float mViewHeight; + float mViewWidth; + + float mStartPositionY; + float mStartPositionX; + float mViewStartPositionY; + float mViewStartPositionX; + + TouchConsumer(SlideUpBuilder builder, LoggerNotifier notifier, + AnimationProcessor animationProcessor){ + mBuilder = builder; + mAnimationProcessor = animationProcessor; + mNotifier = notifier; + } + + int getEnd(){ + if (mBuilder.mIsRTL){ + return mBuilder.mSliderView.getLeft(); + }else { + return mBuilder.mSliderView.getRight(); + } + } + + int getStart(){ + if (mBuilder.mIsRTL){ + return mBuilder.mSliderView.getRight(); + }else { + return mBuilder.mSliderView.getLeft(); + } + } + + int getTop(){ + return mBuilder.mSliderView.getTop(); + } + + int getBottom(){ + return mBuilder.mSliderView.getBottom(); + } +} diff --git a/library/src/main/java/com/mancj/slideup/VerticalConsumer.java b/library/src/main/java/com/mancj/slideup/VerticalConsumer.java deleted file mode 100644 index a4e362d..0000000 --- a/library/src/main/java/com/mancj/slideup/VerticalConsumer.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.mancj.slideup; - -import android.animation.ValueAnimator; -import android.support.annotation.NonNull; -import android.view.MotionEvent; -import android.view.View; - -/** - * @author pa.gulko zTrap (05.07.2017) - */ -public class VerticalConsumer { - private float mSlideAnimationTo; - private ValueAnimator mValueAnimator; - private LoggerNotifier mNotifier; - private boolean mCanSlide = true; - private float mMaxSlidePosition; - private float mTouchableArea; - - private float mStartPositionY; - private float mViewStartPositionY; - - private float mViewHeight; - private View mSliderView; - - public VerticalConsumer(@NonNull View sliderView, float touchableArea, LoggerNotifier notifier, - ValueAnimator valueAnimator){ - mValueAnimator = valueAnimator; - mTouchableArea = touchableArea; - mSliderView = sliderView; - mNotifier = notifier; - } - - public boolean consumeUpToDown(MotionEvent event){ - float touchedArea = event.getRawY() - mSliderView.getBottom(); - switch (event.getActionMasked()){ - case MotionEvent.ACTION_DOWN: - mViewHeight = mSliderView.getHeight(); - mStartPositionY = event.getRawY(); - mViewStartPositionY = mSliderView.getTranslationY(); - mMaxSlidePosition = mViewHeight; - if (mTouchableArea < touchedArea){ - mCanSlide = false; - } - break; - case MotionEvent.ACTION_MOVE: - float difference = event.getRawY() - mStartPositionY; - float moveTo = mViewStartPositionY + difference; - float percents = moveTo * 100 / -mSliderView.getHeight(); - - if (moveTo < 0 && mCanSlide){ - mNotifier.notifyPercentChanged(percents); - mSliderView.setTranslationY(moveTo); - } - if (event.getRawY() < mMaxSlidePosition) { - mMaxSlidePosition = event.getRawY(); - } - break; - case MotionEvent.ACTION_UP: - float slideAnimationFrom = -mSliderView.getTranslationY(); - if (slideAnimationFrom == mViewStartPositionY){ - return false; - } - boolean mustShow = mMaxSlidePosition < event.getRawY(); - boolean scrollableAreaConsumed = mSliderView.getTranslationY() < -mSliderView.getHeight() / 5; - - if (scrollableAreaConsumed && !mustShow){ - mSlideAnimationTo = mSliderView.getHeight() + mSliderView.getTop(); - }else { - mSlideAnimationTo = 0; - } - mValueAnimator.setFloatValues(slideAnimationFrom, mSlideAnimationTo); - mValueAnimator.start(); - mCanSlide = true; - mMaxSlidePosition = 0; - break; - } - return true; - } -} diff --git a/library/src/main/java/com/mancj/slideup/VerticalTouchConsumer.java b/library/src/main/java/com/mancj/slideup/VerticalTouchConsumer.java new file mode 100644 index 0000000..23b070f --- /dev/null +++ b/library/src/main/java/com/mancj/slideup/VerticalTouchConsumer.java @@ -0,0 +1,98 @@ +package com.mancj.slideup; + +import android.view.MotionEvent; + +/** + * @author pa.gulko zTrap (05.07.2017) + */ +class VerticalTouchConsumer extends TouchConsumer { + + VerticalTouchConsumer(SlideUpBuilder builder, LoggerNotifier notifier, AnimationProcessor animationProcessor) { + super(builder, notifier, animationProcessor); + } + + boolean consumeBottomToTop(MotionEvent event){ + float touchedArea = event.getY(); + switch (event.getActionMasked()){ + case MotionEvent.ACTION_DOWN: + mViewHeight = mBuilder.mSliderView.getHeight(); + mStartPositionY = event.getRawY(); + mViewStartPositionY = mBuilder.mSliderView.getTranslationY(); + mCanSlide = mBuilder.mTouchableArea >= touchedArea; + break; + case MotionEvent.ACTION_MOVE: + float difference = event.getRawY() - mStartPositionY; + float moveTo = mViewStartPositionY + difference; + float percents = moveTo * 100 / mBuilder.mSliderView.getHeight(); + + if (moveTo > 0 && mCanSlide){ + mNotifier.notifyPercentChanged(percents); + mBuilder.mSliderView.setTranslationY(moveTo); + } + if (event.getRawY() > mMaxSlidePosition) { + mMaxSlidePosition = event.getRawY(); + } + break; + case MotionEvent.ACTION_UP: + float slideAnimationFrom = mBuilder.mSliderView.getTranslationY(); + if (slideAnimationFrom == mViewStartPositionY){ + return !Internal.isUpEventInView(mBuilder.mSliderView, event); + } + boolean mustShow = mMaxSlidePosition > event.getRawY(); + boolean scrollableAreaConsumed = mBuilder.mSliderView.getTranslationY() > mBuilder.mSliderView.getHeight() / 5; + + if (scrollableAreaConsumed && !mustShow){ + mAnimationProcessor.setValuesAndStart(slideAnimationFrom, mBuilder.mSliderView.getHeight()); + } else { + mAnimationProcessor.setValuesAndStart(slideAnimationFrom, 0); + } + mCanSlide = true; + mMaxSlidePosition = 0; + break; + } + return true; + } + + boolean consumeTopToBottom(MotionEvent event){ + float touchedArea = event.getY(); + switch (event.getActionMasked()){ + case MotionEvent.ACTION_DOWN: + mViewHeight = mBuilder.mSliderView.getHeight(); + mStartPositionY = event.getRawY(); + mViewStartPositionY = mBuilder.mSliderView.getTranslationY(); + mMaxSlidePosition = mViewHeight; + mCanSlide = getBottom() - mBuilder.mTouchableArea <= touchedArea; + break; + case MotionEvent.ACTION_MOVE: + float difference = event.getRawY() - mStartPositionY; + float moveTo = mViewStartPositionY + difference; + float percents = moveTo * 100 / -mBuilder.mSliderView.getHeight(); + + if (moveTo < 0 && mCanSlide){ + mNotifier.notifyPercentChanged(percents); + mBuilder.mSliderView.setTranslationY(moveTo); + } + if (event.getRawY() < mMaxSlidePosition) { + mMaxSlidePosition = event.getRawY(); + } + break; + case MotionEvent.ACTION_UP: + float slideAnimationFrom = -mBuilder.mSliderView.getTranslationY(); + if (slideAnimationFrom == mViewStartPositionY){ + return !Internal.isUpEventInView(mBuilder.mSliderView, event); + } + boolean mustShow = mMaxSlidePosition < event.getRawY(); + boolean scrollableAreaConsumed = mBuilder.mSliderView.getTranslationY() < -mBuilder.mSliderView.getHeight() / 5; + + if (scrollableAreaConsumed && !mustShow){ + mAnimationProcessor.setValuesAndStart(slideAnimationFrom, mBuilder.mSliderView.getHeight() + mBuilder.mSliderView.getTop()); + }else { + mAnimationProcessor.setValuesAndStart(slideAnimationFrom, 0); + } + mCanSlide = true; + mMaxSlidePosition = 0; + break; + } + return true; + } +} From de93cd7a2f357ce5099ef7116cd20e2287d85ce8 Mon Sep 17 00:00:00 2001 From: "pa.gulko" Date: Wed, 12 Jul 2017 20:41:21 +0300 Subject: [PATCH 3/6] Update MIGRATION.md --- MIGRATION.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 797c81c..e839c69 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,4 +1,4 @@ # v2.2.6 -> ... - `SlideUp.Builder` was moved into separated class and renamed to `SlideUpBuilder` - method `setTouchebleArea(float touchableArea)` was splitted to two methods: `setTouchebleAreaDp(float touchableArea)` and `setTouchebleAreaPx(float touchableArea)` - method `getTouchebleArea()` was splitted to two methods: `getTouchebleAreaDp()` and `getTouchebleAreaPx()` \ No newline at end of file +`SlideUp.Builder` was moved into separated class and renamed to `SlideUpBuilder` +method `setTouchebleArea(float touchableArea)` was splitted to two methods: `setTouchebleAreaDp(float touchableArea)` and `setTouchebleAreaPx(float touchableArea)` +method `getTouchebleArea()` was splitted to two methods: `getTouchebleAreaDp()` and `getTouchebleAreaPx()` \ No newline at end of file From 2d0f5e8efde6e66dd94f414b73b4e717e601a570 Mon Sep 17 00:00:00 2001 From: "pa.gulko" Date: Wed, 12 Jul 2017 20:45:31 +0300 Subject: [PATCH 4/6] Update MIGRATION.md --- MIGRATION.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index e839c69..a417f9b 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,4 +1,13 @@ -# v2.2.6 -> ... -`SlideUp.Builder` was moved into separated class and renamed to `SlideUpBuilder` -method `setTouchebleArea(float touchableArea)` was splitted to two methods: `setTouchebleAreaDp(float touchableArea)` and `setTouchebleAreaPx(float touchableArea)` -method `getTouchebleArea()` was splitted to two methods: `getTouchebleAreaDp()` and `getTouchebleAreaPx()` \ No newline at end of file +# v2.2.6 → ... + - `SlideUp.Builder` was moved into separated class and renamed to `SlideUpBuilder` + ## SlideUp + - method `setTouchebleArea(float touchableArea)` was split to two methods: + - `setTouchebleAreaDp(float touchableArea)` + - `setTouchebleAreaPx(float touchableArea)` + - method `getTouchebleArea()` was split to two methods: + - `getTouchebleAreaDp()` + - `getTouchebleAreaPx()` + ## SlideUpBuilder + - method `withTouchebleArea(float touchableArea)` was split to two methods: + - `withTouchebleAreaDp(float touchableArea)` + - `withTouchebleAreaPx(float touchableArea)` \ No newline at end of file From 14dcd8d2400058afc634302352f21453efefb8d0 Mon Sep 17 00:00:00 2001 From: "pa.gulko" Date: Wed, 12 Jul 2017 20:46:24 +0300 Subject: [PATCH 5/6] Update MIGRATION.md --- MIGRATION.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index a417f9b..4305f5a 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,13 +1,13 @@ # v2.2.6 → ... - `SlideUp.Builder` was moved into separated class and renamed to `SlideUpBuilder` - ## SlideUp + #### SlideUp - method `setTouchebleArea(float touchableArea)` was split to two methods: - `setTouchebleAreaDp(float touchableArea)` - `setTouchebleAreaPx(float touchableArea)` - method `getTouchebleArea()` was split to two methods: - `getTouchebleAreaDp()` - `getTouchebleAreaPx()` - ## SlideUpBuilder + #### SlideUpBuilder - method `withTouchebleArea(float touchableArea)` was split to two methods: - `withTouchebleAreaDp(float touchableArea)` - `withTouchebleAreaPx(float touchableArea)` \ No newline at end of file From dbad811bced969eab97061f8c7c69247c486dfab Mon Sep 17 00:00:00 2001 From: zTrap Date: Wed, 12 Jul 2017 22:48:28 +0300 Subject: [PATCH 6/6] supports AS 2.2 --- app/build.gradle | 6 +++--- .../com/example/slideup/SlideDownViewActivity.java | 12 ++++++++++++ .../com/example/slideup/SlideEndViewActivity.java | 12 ++++++++++++ .../com/example/slideup/SlideStartViewActivity.java | 12 ++++++++++++ .../com/example/slideup/SlideUpViewActivity.java | 4 ++-- build.gradle | 8 ++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- 7 files changed, 46 insertions(+), 10 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7f3cd83..e5645bb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 26 - buildToolsVersion '26.0.0' + compileSdkVersion 25 + buildToolsVersion '25.0.3' defaultConfig { applicationId "com.example.slideup" minSdkVersion 14 - targetSdkVersion 26 + targetSdkVersion 25 versionCode 1 versionName "1.0" diff --git a/app/src/main/java/com/example/slideup/SlideDownViewActivity.java b/app/src/main/java/com/example/slideup/SlideDownViewActivity.java index e4e7356..c959ef5 100644 --- a/app/src/main/java/com/example/slideup/SlideDownViewActivity.java +++ b/app/src/main/java/com/example/slideup/SlideDownViewActivity.java @@ -9,6 +9,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.widget.Toast; import com.mancj.slideup.SlideUp; import com.mancj.slideup.SlideUpBuilder; @@ -18,6 +19,7 @@ public class SlideDownViewActivity extends AppCompatActivity { private View dim; private View sliderView; private FloatingActionButton fab; + Toast toast; @Override protected void onCreate(Bundle savedInstanceState) { @@ -27,6 +29,16 @@ protected void onCreate(Bundle savedInstanceState) { setSupportActionBar(toolbar); sliderView = findViewById(R.id.slideView); + sliderView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (toast != null){ + toast.cancel(); + } + toast = Toast.makeText(SlideDownViewActivity.this, "click", Toast.LENGTH_SHORT); + toast.show(); + } + }); dim = findViewById(R.id.dim); fab = (FloatingActionButton) findViewById(R.id.fab); diff --git a/app/src/main/java/com/example/slideup/SlideEndViewActivity.java b/app/src/main/java/com/example/slideup/SlideEndViewActivity.java index e20ccfa..db81e07 100644 --- a/app/src/main/java/com/example/slideup/SlideEndViewActivity.java +++ b/app/src/main/java/com/example/slideup/SlideEndViewActivity.java @@ -9,6 +9,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.widget.Toast; import com.mancj.slideup.SlideUp; import com.mancj.slideup.SlideUpBuilder; @@ -18,6 +19,7 @@ public class SlideEndViewActivity extends AppCompatActivity { private View dim; private View sliderView; private FloatingActionButton fab; + Toast toast; @Override protected void onCreate(Bundle savedInstanceState) { @@ -27,6 +29,16 @@ protected void onCreate(Bundle savedInstanceState) { setSupportActionBar(toolbar); sliderView = findViewById(R.id.slideView); + sliderView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (toast != null){ + toast.cancel(); + } + toast = Toast.makeText(SlideEndViewActivity.this, "click", Toast.LENGTH_SHORT); + toast.show(); + } + }); dim = findViewById(R.id.dim); fab = (FloatingActionButton) findViewById(R.id.fab); diff --git a/app/src/main/java/com/example/slideup/SlideStartViewActivity.java b/app/src/main/java/com/example/slideup/SlideStartViewActivity.java index 23dbd23..3b4a6e8 100644 --- a/app/src/main/java/com/example/slideup/SlideStartViewActivity.java +++ b/app/src/main/java/com/example/slideup/SlideStartViewActivity.java @@ -9,6 +9,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.widget.Toast; import com.mancj.slideup.SlideUp; import com.mancj.slideup.SlideUpBuilder; @@ -18,6 +19,7 @@ public class SlideStartViewActivity extends AppCompatActivity { private View dim; private View sliderView; private FloatingActionButton fab; + Toast toast; @Override protected void onCreate(Bundle savedInstanceState) { @@ -27,6 +29,16 @@ protected void onCreate(Bundle savedInstanceState) { setSupportActionBar(toolbar); sliderView = findViewById(R.id.slideView); + sliderView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (toast != null){ + toast.cancel(); + } + toast = Toast.makeText(SlideStartViewActivity.this, "click", Toast.LENGTH_SHORT); + toast.show(); + } + }); dim = findViewById(R.id.dim); fab = (FloatingActionButton) findViewById(R.id.fab); diff --git a/app/src/main/java/com/example/slideup/SlideUpViewActivity.java b/app/src/main/java/com/example/slideup/SlideUpViewActivity.java index 73f4c53..1703ac1 100644 --- a/app/src/main/java/com/example/slideup/SlideUpViewActivity.java +++ b/app/src/main/java/com/example/slideup/SlideUpViewActivity.java @@ -25,7 +25,7 @@ public class SlideUpViewActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_slide_up_view); - Toolbar toolbar = findViewById(R.id.toolbar); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); sliderView = findViewById(R.id.slideView); @@ -40,7 +40,7 @@ public void onClick(View v) { } }); dim = findViewById(R.id.dim); - fab = findViewById(R.id.fab); + fab = (FloatingActionButton) findViewById(R.id.fab); slideUp = new SlideUpBuilder(sliderView) .withListeners(new SlideUp.Listener.Events() { diff --git a/build.gradle b/build.gradle index d6901b9..37a6137 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { versions = [ - support : '26.0.0-beta2', + support : '25.4.0', SlideUp : '2.2.1', Rx : [ Java : '1.3.0' @@ -10,17 +10,17 @@ buildscript { } repositories { jcenter() - google() + maven { url 'https://maven.google.com' } } dependencies { - classpath 'com.android.tools.build:gradle:3.0.0-alpha6' + classpath 'com.android.tools.build:gradle:2.3.3' } } allprojects { repositories { jcenter() - google() + maven { url 'https://maven.google.com' } } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 64532f6..673e008 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-milestone-1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip