ViewDragHelper側滑關閉再升級-仿微信

繼上篇文章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();

由於我本人只是喜歡寫Demo玩,所以一般都以功能爲主,其實可以添加更多的細節,不過我覺得沒有多大意義,因爲需求總是多變的,只要知道核心思想,改造將會輕而易舉。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章