1. 程式人生 > >【Android】一個適用於大圖介面向下拖拽返回的效果

【Android】一個適用於大圖介面向下拖拽返回的效果

GitHub:DragBottom

適用於檢視大圖的下拉拖拽返回控制元件
共享元素適用於5.0+




最初看到這個效果是在IOS系統的簡訊App,後來在IOS微信、騰訊新聞中都發現了類似的效果。

網路上好像也沒有類似的程式碼,所以決定自己動手寫一個。

一想到拖拽,首先會想到ViewDragHelper,關於這個類的使用方法可以參考

http://blog.csdn.net/lmj623565791/article/details/46858663

Hongyang的部落格解析

需求分析:在使用微信、騰訊新聞中該效果只允許嚮往下拖拽一定距離後處於觸控狀態時便可全屏拖拽。並且向上向下拖拽時,會對檢視的背景透明度、大小有一定變化,越往下越透明,可見上級介面。

當往下拖動一定距離鬆手時,觸發返回上級介面。反之距離不夠,檢視將返回原先位置。


1、建立ViewDragHelper

mDragHelper=ViewDragHelper.create(this,1.0f,new ViewDragHelper.Callback(){
            @Override
            public boolean tryCaptureView(View child, int pointerId) {
                //允許子檢視進行拖拽,這裡預設都允許
                return true;
            }
        });
2、設定拖拽邊界以及方向,只允許向下

@Override
            public int clampViewPositionVertical(View child, int top, int dy) {
                //不允許向上拖動,取值top表示允許上下拖拽-1920~1920
                //這裡設定假設top<0時,直接設定為0,即不允許向上拖拽
                return Math.max(top,0);
            }

            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                //0不允許水平拖拽,取值left表示允許左右拖拽left=-1080~1080
                return 0;
            }
3、如此就能設定向下拖拽了,接下來往下拖動一定距離時,釋放限制,這裡設定為100px,程式碼改造後如下
/**是否允許所有方向拖拽*/
            boolean needDrag;
            @Override
            public int clampViewPositionVertical(View child, int top, int dy) {
                if (needDrag) {
                    return top;
                }
                if (top < 0) {//只允許向下拖拽
                    top = 0;
                } else if (top > 100) {//向下拖拽超過100px後,釋放允許任何方向拖拽
                    needDrag = true;
                }
                return top;
            }

            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                return needDrag ? left : 0;
            }
4、 向下拖拽時要有縮放的一個效果,我們需要重寫另一個函式

/**
             *
             * @param changedView   被拖動的View
             * @param left          水平拖動距離
             * @param top           垂直拖動距離
             * @param dx            每次拖拽產生的水平距離x2-x1
             * @param dy            每次拖拽產生的垂直距離y2-y1
             */
            @Override
            public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
                super.onViewPositionChanged(changedView, left, top, dx, dy);
            }

4.1、為了計算出實時的拖動百分比

直接用top除以view的高度即可:

向下拖動100px後公示為:1-100/1920=0.95、

向下拖動300px後公示為:1-300/1920=0.85。

即:View從原始尺寸漸漸縮小,這裡我們給定最小縮放倍率0.5f,最大1.0f。當然這個倍率也可以隨時調整

@Override
            public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
                super.onViewPositionChanged(changedView, left, top, dx, dy);
                float present = 1 - (top * 1.0f) / (getHeight());

                float maxScale = Math.min(present, 1.0f);//Max,1.0f
                float minScale = Math.max(0.5f, maxScale);//Min,5.0f;

                changedView.setScaleX(minScale);
                changedView.setScaleY(minScale);
            }
5、接下來是背景透明度的變換,要達到的效果是能看到上一級Activity的介面。

所以首頁要將當期的Activity的主題樣式設定成透明化

<style name="AppTheme.drag">
        <item name="android:windowIsTranslucent">true</item>
</style>
應用

<activity android:name=".DragActivity"
          android:theme="@style/AppTheme.drag"/>
5.1、背景透明度的變化第一時間想到的就是View的背景顏色

瞭解過Activity佈局層級的都知道頂層佈局是DecorView

所以我們首先給DecorView設定為純黑色,然後拖動時修改其背景顏色即可

在佈局中取得DecorView:

if (getContext() instanceof Activity) {
            ((Activity) getContext()).getWindow().getDecorView().setBackgroundColor(DEF_BG_COLOR);
        }
和View縮放相同的道理,實時獲取百分比即可,最大不超過255

/**
             *
             * @param changedView   被拖動的View
             * @param left          水平拖動距離
             * @param top           垂直拖動距離
             * @param dx            每次拖拽產生的水平距離x2-x1
             * @param dy            每次拖拽產生的垂直距離y2-y1
             */
            @Override
            public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
                super.onViewPositionChanged(changedView, left, top, dx, dy);
                float present = 1 - (top * 1.0f) / (getHeight());

                if (getContext() instanceof Activity) {
                    int alpah = Math.min((int) (255 * present), 255);
                    ((Activity) getContext()).getWindow().getDecorView().setBackgroundColor(Color.argb(alpah, 0, 0, 0));
                }

                float maxScale = Math.min(present, 1.0f);//Max,1.0f
                float minScale = Math.max(0.5f, maxScale);//Min,5.0f;

                changedView.setScaleX(minScale);
                changedView.setScaleY(minScale);
            }

6接下來最後一個步驟要設定一個拖動觸發關閉的界限,這裡取了View拖動的1/4距離長度,當然可以隨意修改。

超過設定距離,觸發關閉

反之,將View歸位

定義變數

boolean mNeedRelease;
@Override
            public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
                super.onViewPositionChanged(changedView, left, top, dx, dy);

                mNeedRelease = top > getHeight() * 0.25;//Release
重寫釋放拖拽的函式,觸發onBackPressed方法finish掉當前Activity
boolean mNeedRelease;
            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel) {
                super.onViewReleased(releasedChild, xvel, yvel);

                if (mNeedRelease) {
                    if (getContext() instanceof Activity) {
                        ((Activity) getContext()).onBackPressed();
                    }
                } else {
                    needDrag = false;
                    //讓檢視歸位
                    mDragHelper.settleCapturedViewAt(finalLeft, finalTop);
                    releasedChild.setScaleX(1.0f);
                    releasedChild.setScaleY(1.0f);
                    invalidate();
                }
            }