diff --git a/app/build.gradle b/app/build.gradle index 5aa2ec4..e37c77a 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,12 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" - + compileSdkVersion 28 defaultConfig { applicationId "me.yokeyword.swipebackfragment.sample" - minSdkVersion 14 - targetSdkVersion 23 + minSdkVersion 16 + targetSdkVersion 28 versionCode 1 versionName "1.0" } @@ -20,8 +19,12 @@ android { } dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') - compile 'com.android.support:appcompat-v7:25.2.0' - compile 'com.android.support:recyclerview-v7:25.2.0' - compile project(':swipeback') + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.recyclerview:recyclerview:1.0.0' + implementation project(':swipeback') + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} +repositories { + mavenCentral() } diff --git a/app/src/main/java/me/yokeyword/swipebackfragment/sample/BaseSwipeBackFragment.java b/app/src/main/java/me/yokeyword/swipebackfragment/sample/BaseSwipeBackFragment.java index 6080683..510b16c 100644 --- a/app/src/main/java/me/yokeyword/swipebackfragment/sample/BaseSwipeBackFragment.java +++ b/app/src/main/java/me/yokeyword/swipebackfragment/sample/BaseSwipeBackFragment.java @@ -1,10 +1,10 @@ package me.yokeyword.swipebackfragment.sample; import android.content.Context; -import android.support.v4.app.Fragment; -import android.support.v7.widget.Toolbar; +import androidx.appcompat.widget.Toolbar; import android.view.View; +import androidx.fragment.app.Fragment; import me.yokeyword.swipebackfragment.SwipeBackFragment; diff --git a/app/src/main/java/me/yokeyword/swipebackfragment/sample/MainActivity.java b/app/src/main/java/me/yokeyword/swipebackfragment/sample/MainActivity.java index 4c70cde..caf28f6 100644 --- a/app/src/main/java/me/yokeyword/swipebackfragment/sample/MainActivity.java +++ b/app/src/main/java/me/yokeyword/swipebackfragment/sample/MainActivity.java @@ -1,9 +1,9 @@ package me.yokeyword.swipebackfragment.sample; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.widget.Toast; +import androidx.fragment.app.Fragment; import me.yokeyword.swipebackfragment.SwipeBackActivity; import me.yokeyword.swipebackfragment.sample.fragment.FirstSwipeBackFragment; diff --git a/app/src/main/java/me/yokeyword/swipebackfragment/sample/PagerAdapter.java b/app/src/main/java/me/yokeyword/swipebackfragment/sample/PagerAdapter.java index 577b798..7461671 100644 --- a/app/src/main/java/me/yokeyword/swipebackfragment/sample/PagerAdapter.java +++ b/app/src/main/java/me/yokeyword/swipebackfragment/sample/PagerAdapter.java @@ -1,7 +1,6 @@ package me.yokeyword.swipebackfragment.sample; import android.content.Context; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -10,6 +9,8 @@ import java.util.ArrayList; import java.util.List; +import androidx.recyclerview.widget.RecyclerView; + /** * Created by YoKeyword on 16/4/21. diff --git a/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/FirstSwipeBackFragment.java b/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/FirstSwipeBackFragment.java index bfa0d7f..41487cb 100644 --- a/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/FirstSwipeBackFragment.java +++ b/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/FirstSwipeBackFragment.java @@ -1,8 +1,8 @@ package me.yokeyword.swipebackfragment.sample.fragment; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v7.widget.Toolbar; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/RecyclerSwipeBackFragment.java b/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/RecyclerSwipeBackFragment.java index 6882a8a..723e77b 100644 --- a/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/RecyclerSwipeBackFragment.java +++ b/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/RecyclerSwipeBackFragment.java @@ -1,9 +1,8 @@ package me.yokeyword.swipebackfragment.sample.fragment; +import androidx.appcompat.widget.Toolbar; + import android.os.Bundle; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -11,6 +10,8 @@ import java.util.ArrayList; import java.util.List; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import me.yokeyword.swipebackfragment.sample.BaseSwipeBackFragment; import me.yokeyword.swipebackfragment.sample.PagerAdapter; import me.yokeyword.swipebackfragment.sample.R; diff --git a/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/SecondSwipeBackFragment.java b/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/SecondSwipeBackFragment.java index 577bbac..53d4213 100644 --- a/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/SecondSwipeBackFragment.java +++ b/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/SecondSwipeBackFragment.java @@ -1,10 +1,10 @@ package me.yokeyword.swipebackfragment.sample.fragment; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.view.GravityCompat; -import android.support.v7.widget.PopupMenu; -import android.support.v7.widget.Toolbar; +import androidx.annotation.Nullable; +import androidx.core.view.GravityCompat; +import androidx.appcompat.widget.PopupMenu; +import androidx.appcompat.widget.Toolbar; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; diff --git a/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/ThirdSwipeBackFragment.java b/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/ThirdSwipeBackFragment.java index 1403880..d502409 100644 --- a/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/ThirdSwipeBackFragment.java +++ b/app/src/main/java/me/yokeyword/swipebackfragment/sample/fragment/ThirdSwipeBackFragment.java @@ -1,10 +1,10 @@ package me.yokeyword.swipebackfragment.sample.fragment; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.view.GravityCompat; -import android.support.v7.widget.PopupMenu; -import android.support.v7.widget.Toolbar; +import androidx.annotation.Nullable; +import androidx.core.view.GravityCompat; +import androidx.appcompat.widget.PopupMenu; +import androidx.appcompat.widget.Toolbar; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; diff --git a/app/src/main/res/layout/fragment_swipe_back_recy.xml b/app/src/main/res/layout/fragment_swipe_back_recy.xml index e04ac46..614689a 100644 --- a/app/src/main/res/layout/fragment_swipe_back_recy.xml +++ b/app/src/main/res/layout/fragment_swipe_back_recy.xml @@ -7,7 +7,7 @@ - diff --git a/app/src/main/res/layout/toolbar.xml b/app/src/main/res/layout/toolbar.xml index ada3ca3..5c5e295 100644 --- a/app/src/main/res/layout/toolbar.xml +++ b/app/src/main/res/layout/toolbar.xml @@ -1,5 +1,5 @@ - { + val background = windowBackground + view.setBackgroundResource(background) + } + else -> view.setBackgroundResource(defaultBg) + } + } + } + + override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? = if (mLocking) { + mNoAnim + } else super.onCreateAnimation(transit, enter, nextAnim) + + fun setSwipeBackEnable(enable: Boolean) { + swipeBackLayout!!.setEnableGesture(enable) + } + + companion object { + private const val SWIPEBACKFRAGMENT_STATE_SAVE_IS_HIDDEN = "SWIPEBACKFRAGMENT_STATE_SAVE_IS_HIDDEN" + } +} diff --git a/swipeback/src/main/java/me/yokeyword/swipebackfragment/SwipeBackLayout.java b/swipeback/src/main/java/me/yokeyword/swipebackfragment/SwipeBackLayout.java deleted file mode 100644 index a0dd626..0000000 --- a/swipeback/src/main/java/me/yokeyword/swipebackfragment/SwipeBackLayout.java +++ /dev/null @@ -1,510 +0,0 @@ -package me.yokeyword.swipebackfragment; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.support.annotation.IntDef; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.support.v4.view.ViewCompat; -import android.support.v4.widget.ViewDragHelper; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.FrameLayout; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; - - -/** - * Thx https://github.com/ikew0ng/SwipeBackLayout - * SwipeBackLayout - * Created by YoKeyword on 16/4/19. - */ -public class SwipeBackLayout extends FrameLayout { - /** - * Edge flag indicating that the left edge should be affected. - */ - public static final int EDGE_LEFT = ViewDragHelper.EDGE_LEFT; - - /** - * Edge flag indicating that the right edge should be affected. - */ - public static final int EDGE_RIGHT = ViewDragHelper.EDGE_RIGHT; - - public static final int EDGE_ALL = EDGE_LEFT | EDGE_RIGHT; - - - /** - * A view is not currently being dragged or animating as a result of a - * fling/snap. - */ - public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE; - - /** - * A view is currently being dragged. The position is currently changing as - * a result of user input or simulated user input. - */ - public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING; - - /** - * A view is currently settling into place as a result of a fling or - * predefined non-interactive motion. - */ - public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING; - - private static final int DEFAULT_SCRIM_COLOR = 0x99000000; - private static final int FULL_ALPHA = 255; - private static final float DEFAULT_SCROLL_THRESHOLD = 0.4f; - private static final int OVERSCROLL_DISTANCE = 10; - - private float mScrollFinishThreshold = DEFAULT_SCROLL_THRESHOLD; - - private ViewDragHelper mHelper; - - private float mScrollPercent; - private float mScrimOpacity; - - private FragmentActivity mActivity; - private View mContentView; - private SwipeBackFragment mFragment; - private Fragment mPreFragment; - - private Drawable mShadowLeft; - private Drawable mShadowRight; - private Rect mTmpRect = new Rect(); - - private int mEdgeFlag; - private boolean mEnable = true; - private int mCurrentSwipeOrientation; - - private Context context; - private EdgeLevel edgeLevel; - - public enum EdgeLevel { - MAX, MIN, MED - } - - /** - * The set of listeners to be sent events through. - */ - private List mListeners; - - public SwipeBackLayout(Context context) { - this(context, null); - } - - public SwipeBackLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public SwipeBackLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - this.context = context; - init(); - } - - private void init() { - mHelper = ViewDragHelper.create(this, new ViewDragCallback()); - setShadow(R.drawable.shadow_left, EDGE_LEFT); - setEdgeOrientation(EDGE_LEFT); - } - - /** - * Set scroll threshold, we will close the activity, when scrollPercent over - * this value - * - * @param threshold - */ - public void setScrollThresHold(float threshold) { - if (threshold >= 1.0f || threshold <= 0) { - throw new IllegalArgumentException("Threshold value should be between 0 and 1.0"); - } - mScrollFinishThreshold = threshold; - } - - /** - * Enable edge tracking for the selected edges of the parent view. - * The callback's {@link ViewDragHelper.Callback#onEdgeTouched(int, int)} and - * {@link ViewDragHelper.Callback#onEdgeDragStarted(int, int)} methods will only be invoked - * for edges for which edge tracking has been enabled. - * - * @param orientation Combination of edge flags describing the edges to watch - * @see #EDGE_LEFT - * @see #EDGE_RIGHT - */ - public void setEdgeOrientation(int orientation) { - mEdgeFlag = orientation; - mHelper.setEdgeTrackingEnabled(orientation); - - if (orientation == EDGE_RIGHT || orientation == EDGE_ALL) { - setShadow(R.drawable.shadow_right, EDGE_RIGHT); - } - } - - public void setEdgeLevel(EdgeLevel edgeLevel) { - this.edgeLevel = edgeLevel; - validateEdgeLevel(0, edgeLevel); - } - - public void setEdgeLevel(int widthPixel) { - validateEdgeLevel(widthPixel, null); - } - - public EdgeLevel getEdgeLevel() { - return edgeLevel; - } - - private void validateEdgeLevel(int widthPixel, EdgeLevel edgeLevel) { - try { - DisplayMetrics metrics = new DisplayMetrics(); - WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - windowManager.getDefaultDisplay().getMetrics(metrics); - Field mEdgeSize = mHelper.getClass().getDeclaredField("mEdgeSize"); - mEdgeSize.setAccessible(true); - if (widthPixel != 0) { - mEdgeSize.setInt(mHelper, widthPixel); - } else { - if (edgeLevel == EdgeLevel.MAX) { - mEdgeSize.setInt(mHelper, metrics.widthPixels); - } else if (edgeLevel == EdgeLevel.MED) { - mEdgeSize.setInt(mHelper, metrics.widthPixels / 2); - } else if (edgeLevel == EdgeLevel.MIN) { - mEdgeSize.setInt(mHelper, ((int) (20 * metrics.density + 0.5f))); - } - } - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - - @IntDef({EDGE_LEFT, EDGE_RIGHT, EDGE_ALL}) - @Retention(RetentionPolicy.SOURCE) - public @interface EdgeOrientation { - } - - /** - * Set a drawable used for edge shadow. - */ - public void setShadow(Drawable shadow, int edgeFlag) { - if ((edgeFlag & EDGE_LEFT) != 0) { - mShadowLeft = shadow; - } else if ((edgeFlag & EDGE_RIGHT) != 0) { - mShadowRight = shadow; - } - invalidate(); - } - - /** - * Set a drawable used for edge shadow. - */ - public void setShadow(int resId, int edgeFlag) { - setShadow(getResources().getDrawable(resId), edgeFlag); - } - - /** - * Add a callback to be invoked when a swipe event is sent to this view. - * - * @param listener the swipe listener to attach to this view - */ - public void addSwipeListener(OnSwipeListener listener) { - if (mListeners == null) { - mListeners = new ArrayList<>(); - } - mListeners.add(listener); - } - - /** - * Removes a listener from the set of listeners - * - * @param listener - */ - public void removeSwipeListener(OnSwipeListener listener) { - if (mListeners == null) { - return; - } - mListeners.remove(listener); - } - - public interface OnSwipeListener { - /** - * Invoke when state change - * - * @param state flag to describe scroll state - * @see #STATE_IDLE - * @see #STATE_DRAGGING - * @see #STATE_SETTLING - */ - void onDragStateChange(int state); - - /** - * Invoke when edge touched - * - * @param oritentationEdgeFlag edge flag describing the edge being touched - * @see #EDGE_LEFT - * @see #EDGE_RIGHT - */ - void onEdgeTouch(int oritentationEdgeFlag); - - /** - * Invoke when scroll percent over the threshold for the first time - * - * @param scrollPercent scroll percent of this view - */ - void onDragScrolled(float scrollPercent); - } - - @Override - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - boolean isDrawView = child == mContentView; - boolean drawChild = super.drawChild(canvas, child, drawingTime); - if (isDrawView && mScrimOpacity > 0 && mHelper.getViewDragState() != ViewDragHelper.STATE_IDLE) { - drawShadow(canvas, child); - drawScrim(canvas, child); - } - return drawChild; - } - - private void drawShadow(Canvas canvas, View child) { - final Rect childRect = mTmpRect; - child.getHitRect(childRect); - - if ((mCurrentSwipeOrientation & EDGE_LEFT) != 0) { - mShadowLeft.setBounds(childRect.left - mShadowLeft.getIntrinsicWidth(), childRect.top, childRect.left, childRect.bottom); - mShadowLeft.setAlpha((int) (mScrimOpacity * FULL_ALPHA)); - mShadowLeft.draw(canvas); - } else if ((mCurrentSwipeOrientation & EDGE_RIGHT) != 0) { - mShadowRight.setBounds(childRect.right, childRect.top, childRect.right + mShadowRight.getIntrinsicWidth(), childRect.bottom); - mShadowRight.setAlpha((int) (mScrimOpacity * FULL_ALPHA)); - mShadowRight.draw(canvas); - } - } - - private void drawScrim(Canvas canvas, View child) { - final int baseAlpha = (DEFAULT_SCRIM_COLOR & 0xff000000) >>> 24; - final int alpha = (int) (baseAlpha * mScrimOpacity); - final int color = alpha << 24; - - if ((mCurrentSwipeOrientation & EDGE_LEFT) != 0) { - canvas.clipRect(0, 0, child.getLeft(), getHeight()); - } else if ((mCurrentSwipeOrientation & EDGE_RIGHT) != 0) { - canvas.clipRect(child.getRight(), 0, getRight(), getHeight()); - } - canvas.drawColor(color); - } - - @Override - public void computeScroll() { - mScrimOpacity = 1 - mScrollPercent; - if (mScrimOpacity >= 0) { - if (mHelper.continueSettling(true)) { - ViewCompat.postInvalidateOnAnimation(this); - } - } - } - - public void setFragment(SwipeBackFragment fragment, View view) { - this.mFragment = fragment; - mContentView = view; - } - - public void hiddenFragment() { - if (mPreFragment != null && mPreFragment.getView() != null) { - mPreFragment.getView().setVisibility(GONE); - } - } - - public void attachToActivity(FragmentActivity activity) { - mActivity = activity; - TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{ - android.R.attr.windowBackground - }); - int background = a.getResourceId(0, 0); - a.recycle(); - - ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView(); - ViewGroup decorChild = (ViewGroup) decor.getChildAt(0); - decorChild.setBackgroundResource(background); - decor.removeView(decorChild); - addView(decorChild); - setContentView(decorChild); - decor.addView(this); - } - - public void attachToFragment(SwipeBackFragment swipeBackFragment, View view) { - addView(view); - setFragment(swipeBackFragment, view); - } - - private void setContentView(View view) { - mContentView = view; - } - - public void setEnableGesture(boolean enable) { - mEnable = enable; - } - - class ViewDragCallback extends ViewDragHelper.Callback { - - @Override - public boolean tryCaptureView(View child, int pointerId) { - boolean dragEnable = mHelper.isEdgeTouched(mEdgeFlag, pointerId); - if (dragEnable) { - if (mHelper.isEdgeTouched(EDGE_LEFT, pointerId)) { - mCurrentSwipeOrientation = EDGE_LEFT; - } else if (mHelper.isEdgeTouched(EDGE_RIGHT, pointerId)) { - mCurrentSwipeOrientation = EDGE_RIGHT; - } - - if (mListeners != null && !mListeners.isEmpty()) { - for (OnSwipeListener listener : mListeners) { - listener.onEdgeTouch(mCurrentSwipeOrientation); - } - } - - if (mPreFragment == null) { - if (mFragment != null) { - List fragmentList = mFragment.getFragmentManager().getFragments(); - if (fragmentList != null && fragmentList.size() > 1) { - int index = fragmentList.indexOf(mFragment); - for (int i = index - 1; i >= 0; i--) { - Fragment fragment = fragmentList.get(i); - if (fragment != null && fragment.getView() != null) { - fragment.getView().setVisibility(VISIBLE); - mPreFragment = fragment; - break; - } - } - } - } - } else { - View preView = mPreFragment.getView(); - if (preView != null && preView.getVisibility() != VISIBLE) { - preView.setVisibility(VISIBLE); - } - } - } - return dragEnable; - } - - @Override - public int clampViewPositionHorizontal(View child, int left, int dx) { - int ret = 0; - if ((mCurrentSwipeOrientation & EDGE_LEFT) != 0) { - ret = Math.min(child.getWidth(), Math.max(left, 0)); - } else if ((mCurrentSwipeOrientation & EDGE_RIGHT) != 0) { - ret = Math.min(0, Math.max(left, -child.getWidth())); - } - return ret; - } - - @Override - public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { - super.onViewPositionChanged(changedView, left, top, dx, dy); - if ((mCurrentSwipeOrientation & EDGE_LEFT) != 0) { - mScrollPercent = Math.abs((float) left / (getWidth() + mShadowLeft.getIntrinsicWidth())); - } else if ((mCurrentSwipeOrientation & EDGE_RIGHT) != 0) { - mScrollPercent = Math.abs((float) left / (mContentView.getWidth() + mShadowRight.getIntrinsicWidth())); - } - invalidate(); - - if (mListeners != null && !mListeners.isEmpty() - && mHelper.getViewDragState() == STATE_DRAGGING && mScrollPercent <= 1 && mScrollPercent > 0) { - for (OnSwipeListener listener : mListeners) { - listener.onDragScrolled(mScrollPercent); - } - } - - if (mScrollPercent > 1) { - if (mFragment != null) { - if (mPreFragment instanceof SwipeBackFragment) { - ((SwipeBackFragment) mPreFragment).mLocking = true; - } - if (!mFragment.isDetached()) { - mFragment.mLocking = true; - mFragment.getFragmentManager().popBackStackImmediate(); - mFragment.mLocking = false; - } - if (mPreFragment instanceof SwipeBackFragment) { - ((SwipeBackFragment) mPreFragment).mLocking = false; - } - } else { - if (!mActivity.isFinishing()) { - mActivity.finish(); - mActivity.overridePendingTransition(0, 0); - } - } - } - } - - @Override - public int getViewHorizontalDragRange(View child) { - if (mFragment != null) { - return 1; - } else if (mActivity != null && ((SwipeBackActivity) mActivity).swipeBackPriority()) { - return 1; - } - - return 0; - } - - @Override - public void onViewReleased(View releasedChild, float xvel, float yvel) { - final int childWidth = releasedChild.getWidth(); - - int left = 0, top = 0; - if ((mCurrentSwipeOrientation & EDGE_LEFT) != 0) { - left = xvel > 0 || xvel == 0 && mScrollPercent > mScrollFinishThreshold ? (childWidth - + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE) : 0; - } else if ((mCurrentSwipeOrientation & EDGE_RIGHT) != 0) { - left = xvel < 0 || xvel == 0 && mScrollPercent > mScrollFinishThreshold ? -(childWidth - + mShadowRight.getIntrinsicWidth() + OVERSCROLL_DISTANCE) : 0; - } - - mHelper.settleCapturedViewAt(left, top); - invalidate(); - } - - @Override - public void onViewDragStateChanged(int state) { - super.onViewDragStateChanged(state); - if (mListeners != null && !mListeners.isEmpty()) { - for (OnSwipeListener listener : mListeners) { - listener.onDragStateChange(state); - } - } - } - - @Override - public void onEdgeTouched(int edgeFlags, int pointerId) { - super.onEdgeTouched(edgeFlags, pointerId); - if ((mEdgeFlag & edgeFlags) != 0) { - mCurrentSwipeOrientation = edgeFlags; - } - } - - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (!mEnable) return super.onInterceptTouchEvent(ev); - return mHelper.shouldInterceptTouchEvent(ev); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (!mEnable) return super.onTouchEvent(event); - mHelper.processTouchEvent(event); - return true; - } -} \ No newline at end of file diff --git a/swipeback/src/main/java/me/yokeyword/swipebackfragment/SwipeBackLayout.kt b/swipeback/src/main/java/me/yokeyword/swipebackfragment/SwipeBackLayout.kt new file mode 100644 index 0000000..221f92a --- /dev/null +++ b/swipeback/src/main/java/me/yokeyword/swipebackfragment/SwipeBackLayout.kt @@ -0,0 +1,450 @@ +package me.yokeyword.swipebackfragment + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Canvas +import android.graphics.Rect +import android.graphics.drawable.Drawable +import androidx.annotation.IntDef +import androidx.core.view.ViewCompat +import androidx.customview.widget.ViewDragHelper +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity + +import android.util.AttributeSet +import android.util.DisplayMetrics +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import android.widget.FrameLayout +import kotlin.annotation.Retention +import java.lang.annotation.RetentionPolicy +import java.util.ArrayList + + +/** + * Thx https://github.com/ikew0ng/SwipeBackLayout + * SwipeBackLayout + * Created by YoKeyword on 16/4/19. + */ +open class SwipeBackLayout : FrameLayout { + + constructor(appContext: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0):super(appContext, attrs, defStyleAttr) + constructor(appContext: Context):super(appContext) + + private var mScrollFinishThreshold = DEFAULT_SCROLL_THRESHOLD + + private var mHelper: ViewDragHelper? = null + + private var mScrollPercent: Float = 0.toFloat() + private var mScrimOpacity: Float = 0.toFloat() + + private var mActivity: FragmentActivity? = null + private var mContentView: View? = null + private var mFragment: SwipeBackFragment? = null + private var mPreFragment: Fragment? = null + + private var mShadowLeft: Drawable? = null + private var mShadowRight: Drawable? = null + private val mTmpRect = Rect() + + private var mEdgeFlag: Int = 0 + private var mEnable = true + private var mCurrentSwipeOrientation: Int = 0 + var edgeLevel: EdgeLevel? = null + set(edgeLevel) { + field = edgeLevel + validateEdgeLevel(0, edgeLevel) + } + + /** + * The set of listeners to be sent events through. + */ + private var mListeners: MutableList? = null + + enum class EdgeLevel { + MAX, MIN, MED + } + + init { + mHelper = ViewDragHelper.create(this, ViewDragCallback()) + setShadow(R.drawable.shadow_left, EDGE_LEFT) + setEdgeOrientation(EDGE_LEFT) + } + + /** + * Set scroll threshold, we will close the activity, when scrollPercent over + * this value + * + * @param threshold + */ + fun setScrollThresHold(threshold: Float) { + if (threshold >= 1.0f || threshold <= 0) + throw IllegalArgumentException("Threshold value should be between 0 and 1.0") + mScrollFinishThreshold = threshold + } + + /** + * Enable edge tracking for the selected edges of the parent view. + * The callback's [ViewDragHelper.Callback.onEdgeTouched] and + * [ViewDragHelper.Callback.onEdgeDragStarted] methods will only be invoked + * for edges for which edge tracking has been enabled. + * + * @param orientation Combination of edge flags describing the edges to watch + * @see .EDGE_LEFT + * + * @see .EDGE_RIGHT + */ + fun setEdgeOrientation(orientation: Int) { + mEdgeFlag = orientation + mHelper!!.setEdgeTrackingEnabled(orientation) + if (orientation == EDGE_RIGHT || orientation == EDGE_ALL) + setShadow(R.drawable.shadow_right, EDGE_RIGHT) + } + + fun setEdgeLevel(widthPixel: Int) { + validateEdgeLevel(widthPixel, null) + } + + private fun validateEdgeLevel(widthPixel: Int, edgeLevel: EdgeLevel?) { + try { + val metrics = DisplayMetrics() + val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + windowManager.defaultDisplay.getMetrics(metrics) + val mEdgeSize = mHelper!!.javaClass.getDeclaredField("mEdgeSize") + mEdgeSize.isAccessible = true + if (widthPixel != 0) { + mEdgeSize.setInt(mHelper, widthPixel) + } else when (edgeLevel) { + EdgeLevel.MAX -> mEdgeSize.setInt(mHelper, metrics.widthPixels) + EdgeLevel.MED -> mEdgeSize.setInt(mHelper, metrics.widthPixels / 2) + EdgeLevel.MIN -> mEdgeSize.setInt(mHelper, (20 * metrics.density + 0.5f).toInt()) + } + } catch (e: NoSuchFieldException) { + e.printStackTrace() + } catch (e: IllegalAccessException) { + e.printStackTrace() + } + + } + + @IntDef(EDGE_LEFT, EDGE_RIGHT, EDGE_ALL) + @Retention(AnnotationRetention.SOURCE) + annotation class EdgeOrientation + + /** + * Set a drawable used for edge shadow. + */ + fun setShadow(shadow: Drawable, edgeFlag: Int) { + when { + edgeFlag and EDGE_LEFT != 0 -> mShadowLeft = shadow + edgeFlag and EDGE_RIGHT != 0 -> mShadowRight = shadow + } + invalidate() + } + + /** + * Set a drawable used for edge shadow. + */ + fun setShadow(resId: Int, edgeFlag: Int) { + setShadow(resources.getDrawable(resId), edgeFlag) + } + + /** + * Add a callback to be invoked when a swipe event is sent to this view. + * + * @param listener the swipe listener to attach to this view + */ + open fun addSwipeListener(listener: OnSwipeListener) { + if (mListeners == null) mListeners = ArrayList() + mListeners!!.add(listener) + } + + /** + * Removes a listener from the set of listeners + * + * @param listener + */ + open fun removeSwipeListener(listener: OnSwipeListener) { + if (mListeners == null) return + mListeners!!.remove(listener) + } + + interface OnSwipeListener { + /** + * Invoke when state change + * + * @param state flag to describe scroll state + * @see .STATE_IDLE + * + * @see .STATE_DRAGGING + * + * @see .STATE_SETTLING + */ + fun onDragStateChange(state: Int) + + /** + * Invoke when edge touched + * + * @param oritentationEdgeFlag edge flag describing the edge being touched + * @see .EDGE_LEFT + * + * @see .EDGE_RIGHT + */ + fun onEdgeTouch(oritentationEdgeFlag: Int) + + /** + * Invoke when scroll percent over the threshold for the first time + * + * @param scrollPercent scroll percent of this view + */ + fun onDragScrolled(scrollPercent: Float) + } + + override fun drawChild(canvas: Canvas, child: View, drawingTime: Long): Boolean { + val isDrawView = child === mContentView + val drawChild = super.drawChild(canvas, child, drawingTime) + if (isDrawView && mScrimOpacity > 0 && mHelper!!.viewDragState != ViewDragHelper.STATE_IDLE) { + drawShadow(canvas, child) + drawScrim(canvas, child) + } + return drawChild + } + + private fun drawShadow(canvas: Canvas, child: View) { + val childRect = mTmpRect + child.getHitRect(childRect) + when { + mCurrentSwipeOrientation and EDGE_LEFT != 0 -> { + mShadowLeft!!.setBounds(childRect.left - mShadowLeft!!.intrinsicWidth, childRect.top, childRect.left, childRect.bottom) + mShadowLeft!!.alpha = (mScrimOpacity * FULL_ALPHA).toInt() + mShadowLeft!!.draw(canvas) + } + mCurrentSwipeOrientation and EDGE_RIGHT != 0 -> { + mShadowRight!!.setBounds(childRect.right, childRect.top, childRect.right + mShadowRight!!.intrinsicWidth, childRect.bottom) + mShadowRight!!.alpha = (mScrimOpacity * FULL_ALPHA).toInt() + mShadowRight!!.draw(canvas) + } + } + } + + private fun drawScrim(canvas: Canvas, child: View) { + val baseAlpha = (DEFAULT_SCRIM_COLOR and -0x1000000).ushr(24) + val alpha = (baseAlpha * mScrimOpacity).toInt() + val color = alpha shl 24 + when { + mCurrentSwipeOrientation and EDGE_LEFT != 0 -> canvas.clipRect(0, 0, child.left, height) + mCurrentSwipeOrientation and EDGE_RIGHT != 0 -> canvas.clipRect(child.right, 0, right, height) + } + canvas.drawColor(color) + } + + override fun computeScroll() { + mScrimOpacity = 1 - mScrollPercent + if (mScrimOpacity >= 0 && mHelper!!.continueSettling(true)) { + ViewCompat.postInvalidateOnAnimation(this) + } + } + + open fun setFragment(fragment: SwipeBackFragment, view: View) { + this.mFragment = fragment + mContentView = view + } + + open fun hiddenFragment() { + when { + mPreFragment != null && mPreFragment!!.view != null -> mPreFragment!!.view!!.visibility = View.GONE + } + } + + fun attachToActivity(activity: FragmentActivity) { + mActivity = activity + val a = activity.theme.obtainStyledAttributes(intArrayOf(android.R.attr.windowBackground)) + val background = a.getResourceId(0, 0) + a.recycle() + + val decor = activity.window.decorView as ViewGroup + val decorChild = decor.getChildAt(0) as ViewGroup + decorChild.setBackgroundResource(background) + decor.removeView(decorChild) + addView(decorChild) + setContentView(decorChild) + decor.addView(this) + } + + fun attachToFragment(swipeBackFragment: SwipeBackFragment, view: View) { + addView(view) + setFragment(swipeBackFragment, view) + } + + private fun setContentView(view: View) { + mContentView = view + } + + fun setEnableGesture(enable: Boolean) { + mEnable = enable + } + + internal inner class ViewDragCallback : ViewDragHelper.Callback() { + + override fun tryCaptureView(child: View, pointerId: Int): Boolean { + val dragEnable = mHelper!!.isEdgeTouched(mEdgeFlag, pointerId) + if (dragEnable) { + when { + mHelper!!.isEdgeTouched(EDGE_LEFT, pointerId) -> mCurrentSwipeOrientation = EDGE_LEFT + mHelper!!.isEdgeTouched(EDGE_RIGHT, pointerId) -> mCurrentSwipeOrientation = EDGE_RIGHT + } + + if (mListeners != null && !mListeners!!.isEmpty()) for (listener in mListeners!!) + listener.onEdgeTouch(mCurrentSwipeOrientation) + + if (mPreFragment == null) when { + mFragment != null -> { + val fragmentList = mFragment!!.fragmentManager!!.fragments + if (fragmentList.size > 1) { + val index = fragmentList.indexOf(mFragment) + for (i in index - 1 downTo 0) { + val fragment = fragmentList[i] + if (fragment != null && fragment.view != null) { + fragment.view!!.visibility = View.VISIBLE + mPreFragment = fragment + break + } + } + } + } + } + else { + val preView = mPreFragment!!.view + if (preView != null && preView.visibility != View.VISIBLE) + preView.visibility = View.VISIBLE + } + } + return dragEnable + } + + override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int { + var ret = 0 + when { + mCurrentSwipeOrientation and EDGE_LEFT != 0 -> ret = Math.min(child.width, Math.max(left, 0)) + mCurrentSwipeOrientation and EDGE_RIGHT != 0 -> ret = Math.min(0, Math.max(left, -child.width)) + } + return ret + } + + override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) { + super.onViewPositionChanged(changedView, left, top, dx, dy) + when { + mCurrentSwipeOrientation and EDGE_LEFT != 0 -> mScrollPercent = Math.abs(left.toFloat() / (width + mShadowLeft!!.intrinsicWidth)) + mCurrentSwipeOrientation and EDGE_RIGHT != 0 -> mScrollPercent = Math.abs(left.toFloat() / (mContentView!!.width + mShadowRight!!.intrinsicWidth)) + } + invalidate() + + if (mListeners != null && !mListeners!!.isEmpty() && mHelper!!.viewDragState == STATE_DRAGGING && mScrollPercent <= 1 && mScrollPercent > 0) for (listener in mListeners!!) + listener.onDragScrolled(mScrollPercent) + + if (mScrollPercent > 1) when { + mFragment != null -> { + if (mPreFragment is SwipeBackFragment) + (mPreFragment as SwipeBackFragment).mLocking = true + if (!mFragment!!.isDetached) { + mFragment!!.mLocking = true + mFragment!!.fragmentManager!!.popBackStackImmediate() + mFragment!!.mLocking = false + } + if (mPreFragment is SwipeBackFragment) + (mPreFragment as SwipeBackFragment).mLocking = false + } + else -> if (!mActivity!!.isFinishing) { + mActivity!!.finish() + mActivity!!.overridePendingTransition(0, 0) + } + } + } + + override fun getViewHorizontalDragRange(child: View): Int = when { + mFragment != null -> 1 + mActivity != null && (mActivity as SwipeBackActivity).swipeBackPriority() -> 1 + else -> 0 + } + + override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) { + val childWidth = releasedChild.width + var left = 0 + val top = 0 + when { + mCurrentSwipeOrientation and EDGE_LEFT != 0 -> left = if (xvel > 0 || xvel == 0f && mScrollPercent > mScrollFinishThreshold) + childWidth + mShadowLeft!!.intrinsicWidth + OVERSCROLL_DISTANCE + else 0 + mCurrentSwipeOrientation and EDGE_RIGHT != 0 -> left = if (xvel < 0 || xvel == 0f && mScrollPercent > mScrollFinishThreshold) + -(childWidth + mShadowRight!!.intrinsicWidth + OVERSCROLL_DISTANCE) + else 0 + } + + mHelper!!.settleCapturedViewAt(left, top) + invalidate() + } + + override fun onViewDragStateChanged(state: Int) { + super.onViewDragStateChanged(state) + if (mListeners != null && !mListeners!!.isEmpty()) for (listener in mListeners!!) + listener.onDragStateChange(state) + } + + override fun onEdgeTouched(edgeFlags: Int, pointerId: Int) { + super.onEdgeTouched(edgeFlags, pointerId) + if (mEdgeFlag and edgeFlags != 0) + mCurrentSwipeOrientation = edgeFlags + } + + } + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean = + if (!mEnable) super.onInterceptTouchEvent(ev) else mHelper!!.shouldInterceptTouchEvent(ev) + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent): Boolean { + if (!mEnable) return super.onTouchEvent(event) + mHelper!!.processTouchEvent(event) + return true + } + + companion object { + /** + * Edge flag indicating that the left edge should be affected. + */ + const val EDGE_LEFT = ViewDragHelper.EDGE_LEFT + + /** + * Edge flag indicating that the right edge should be affected. + */ + const val EDGE_RIGHT = ViewDragHelper.EDGE_RIGHT + + const val EDGE_ALL = EDGE_LEFT or EDGE_RIGHT + + + /** + * A view is not currently being dragged or animating as a result of a + * fling/snap. + */ + val STATE_IDLE = ViewDragHelper.STATE_IDLE + + /** + * A view is currently being dragged. The position is currently changing as + * a result of user input or simulated user input. + */ + val STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING + + /** + * A view is currently settling into place as a result of a fling or + * predefined non-interactive motion. + */ + val STATE_SETTLING = ViewDragHelper.STATE_SETTLING + + private val DEFAULT_SCRIM_COLOR = -0x67000000 + private val FULL_ALPHA = 255 + private val DEFAULT_SCROLL_THRESHOLD = 0.4f + private val OVERSCROLL_DISTANCE = 10 + } +} \ No newline at end of file