繼上篇文章Android 利用ViewDragHelper打造側滑關閉控件,因爲上次有事出門了,沒有擴展這個功能,今天剛好週末有點時間,索性補上吧。
效果展示:
思路梳理:執行當前頁面側滑關閉的時候,需要上個頁面同步滾動,從而達到聯動的效果,聯動效果很簡單,使用屬性動畫即可,問題是怎麼拿到上個頁面的View?我們知道Activity可以拿到當前頁面的View,所以我們需要拿到上個頁面的Activity,那麼有什麼好的做法呢?這裏提供兩種方法:
1.在基類BaseActivity中將自己的實例保存下來。
2.使用Application提供的ActivityLifecycleCallbacks,在Activity創建時將實例保存。
我這裏使用第二種方法:
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import java.util.Stack;
/**
* Created by Chao on 2018-09-15.
*/
public class SwipeBackManager implements Application.ActivityLifecycleCallbacks {
private Stack<Activity> mActivityStack = new Stack<>();
private static final SwipeBackManager instance = new SwipeBackManager();
private SwipeBackManager() {
}
public static SwipeBackManager getInstance() {
return instance;
}
/**
* 必須在 Application 的 onCreate 方法中調用
*/
public void init(Application application) {
application.registerActivityLifecycleCallbacks(this);
}
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
//Activity創建時,將其保存。
mActivityStack.add(activity);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
//Activity銷燬時,將其移除。
mActivityStack.remove(activity);
}
/**
* 獲取倒數第二個 Activity
*/
@Nullable
private Activity getPenultimateActivity() {
Activity activity = null;
try {
if (mActivityStack.size() > 1) {
activity = mActivityStack.get(mActivityStack.size() - 2);
}
} catch (Exception e) {
}
return activity;
}
/**
* 暴露Activity滑動動畫
*/
public void onPanelSlide(float slideOffset) {
try {
Activity activity = getPenultimateActivity();
if (activity != null) {
View decorView = activity.getWindow().getDecorView();
decorView.setTranslationX(-(decorView.getMeasuredWidth() - slideOffset) / 3);
}
} catch (Exception e) {
}
}
/**
* 暴露Activity關閉動畫
*/
public void onPanelClosed() {
try {
Activity activity = getPenultimateActivity();
if (activity != null) {
View decorView = activity.getWindow().getDecorView();
decorView.setTranslationX(0);
}
} catch (Exception e) {
}
}
}
然後在Application中註冊:
import android.app.Application;
import view.peakchao.view.view.SwipeBackManager;
/**
* Created by Chao on 2018-09-15.
*/
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
SwipeBackManager.getInstance().init(this);
}
}
在上篇文章中編寫的SwipeBackLayout中進行調用:
import android.app.Activity;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.widget.ViewDragHelper;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import view.peakchao.view.R;
/**
* 基於ViewDragHelper的側滑關閉View
* Created by Chao on 2018-09-09.
*/
public class SwipeBackLayout extends FrameLayout {
private ViewDragHelper mHelper;
private Activity mActivity;
private View mView;
public SwipeBackLayout(@NonNull Context context) {
super(context);
initViewDragHelper();
}
/**
* 綁定 Activity
*
* @param activity 容器 Activity
*/
public void attachActivity(Activity activity) {
mActivity = activity;
ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();
View content = decor.getChildAt(0);
decor.removeView(content);
mView = content;
addView(content);
decor.addView(this);
}
/**
* 初始化方法
*/
private void initViewDragHelper() {
mHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
// 默認不撲獲 View
return false;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
// 拖動限制(大於左邊界)
SwipeBackManager.getInstance().onPanelSlide(left);
return Math.max(0, left);
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//左右側滑時,限制不能上下拖動。
return 0;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
// 拖動距離大於屏幕的一半右移,拖動距離小於屏幕的一半左移
int left = releasedChild.getLeft();
if (left > getWidth() / 2) {
if (mActivity != null) {
mActivity.finish();
mActivity.overridePendingTransition(0, R.anim.right_out);
SwipeBackManager.getInstance().onPanelClosed();
}
} else {
mHelper.settleCapturedViewAt(0, 0);
}
invalidate();
}
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
// 移動子 View
mHelper.captureChildView(mView, pointerId);
}
});
// 跟蹤左邊界拖動
mHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 攔截代理
return mHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Touch Event 代理
mHelper.processTouchEvent(event);
return true;
}
@Override
public void computeScroll() {
// 子 View 需要更新狀態
if (mHelper.continueSettling(true)) {
invalidate();
}
}
}
在SwipeBackLayout中,我們只加入了兩句代碼,如下:
SwipeBackManager.getInstance().onPanelSlide(left);
SwipeBackManager.getInstance().onPanelClosed();