1. 程式人生 > >Android筆記(一):ViewDragHelper實現底部上滑同時底部下滑

Android筆記(一):ViewDragHelper實現底部上滑同時底部下滑

先看看效果圖:

仿哆點

自定義佈局控制元件:

public class DragLayout extends FrameLayout {
    private int title;    //限制上滑後的頂部標題高度大小
    private Status mStatus = Status.Open;    //預設底部是不上滑的
    private View mTopContent;
    private View mMainContent;
    private ViewDragHelper mDragHelper;
    private int mDragRange,mMainTop,mWidth,mHeight;   //mDragRange為拖拽範圍,mMainTop為底部面板離父佈局頂部的長度
    public DragLayout(@NonNull Context context) {
        this(context,null);
    }

    public DragLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public DragLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDragHelper = ViewDragHelper.create(this,0.5f, mCallBack);
    }
    ViewDragHelper.Callback mCallBack=new ViewDragHelper.Callback() {
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            boolean directionCheck=mDragHelper.checkTouchSlop(ViewDragHelper.DIRECTION_VERTICAL,pointerId);
            return (child==mMainContent||child==mTopContent)&&directionCheck;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            if (mMainTop+dy<title){
                return title;
            }else if(mMainTop+dy>mDragRange+title){
                return mDragRange+title;
            }
            return top;
        }

        @Override
        public int getViewVerticalDragRange(View child) {
            return mDragRange;
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
            if(changedView==mMainContent){
                mMainTop=top;
            }else{
                mMainTop+=dy;
            }
            if (mMainTop < title) {
                mMainTop = title;
            } else if (mMainTop > mDragRange+title) {
                mMainTop = mDragRange+title;
            }
            if (changedView == mTopContent) {
                layoutContent();   //如果拖動的是頂部面板則進行強制佈局移動
            }
            dispatchDragEvent(mMainTop);
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            if (yvel > 0) {
                open();
            } else if (yvel == 0 && mMainTop > mDragRange * 0.5f) {
                open();
            } else {
                close();
            }
        }
    };
    public void close() {
        mMainTop = title;
        // 執行動畫,返回true代表有未完成的動畫, 需要繼續執行
        if (mDragHelper.smoothSlideViewTo(mMainContent, 0, mMainTop)) {
            // 注意:引數傳遞根ViewGroup
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }
    private void layoutContent() {
        mMainContent.layout(0, mMainTop,  mWidth, mHeight+mMainTop);
        mTopContent.layout(0, -mHeight/6, mWidth, mHeight);
    }
    public void open() {
        mMainTop = mDragRange+title;
        if (mDragHelper.smoothSlideViewTo(mMainContent, 0, mMainTop)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }
    protected void dispatchDragEvent(int mainTop) {
        float percent = mainTop / (float)mDragRange;
        ViewHelper.setTranslationY(mTopContent, mHeight / 8 - mHeight / 8 * percent);  //實現頂部下滑

        if (mListener != null) {
            mListener.onDraging(percent);
        }

        Status lastStatus = mStatus;
        if (updateStatus(mainTop) != lastStatus) {
            if (mListener == null) {
                return;
            }
            if (lastStatus == Status.Draging) {
                if (mStatus == Status.Close) {
                    mListener.onClose();
                } else if (mStatus == Status.Open) {
                    mListener.onOpen();
                }

            }
        }
    }

    public interface OnLayoutDragingListener {
        void onOpen();

        void onClose();

        void onDraging(float percent);
    }

    private OnLayoutDragingListener mListener;

    public void setOnLayoutDragingListener(OnLayoutDragingListener l) {
        mListener = l;
    }

    private Status updateStatus(int mainTop) {
        if (mainTop == title) {
            mStatus = Status.Close;
        } else if (mainTop == mDragRange+title) {
            mStatus = Status.Open;
        } else {
            mStatus = Status.Draging;
        }
        return mStatus;
    }

    public enum Status {
        Open, Close, Draging
    }

    public Status getStatus() {
        return mStatus;
    }

    public void setStatus(Status mStatus) {
        this.mStatus = mStatus;
    }
    @Override
    public boolean onInterceptTouchEvent(android.view.MotionEvent ev) {
        //將Touch事件傳遞給ViewDragHelper
        return mDragHelper.shouldInterceptTouchEvent(ev);
    };

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        try {
            //將Touch事件傳遞給ViewDragHelper
            mDragHelper.processTouchEvent(event);
        } catch (Exception e) {
        }
        return true;
    }
    @Override
    public void computeScroll() {
        // 高頻率呼叫,決定是否有下一個變動等待執行
        if (mDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }
    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
                            int bottom) {
        mMainContent.layout(0,mMainTop,  mWidth, mMainTop+mHeight);
        mTopContent.layout(0, -mHeight/6, mWidth, mHeight);
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //拿到寬高
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
        //設定拖動範圍
        mDragRange = (int)(mHeight*0.4);
        mMainTop=(int)(mHeight*0.5);
        title=mMainTop-mDragRange;
    }
    /**
     * 填充結束時獲得兩個子佈局的引用
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        int childCount = getChildCount();
        // 必要的檢驗
        if (childCount < 2) {
            throw new IllegalStateException(
                    "You need two childrens in your content");
        }

        if (!(getChildAt(0) instanceof ViewGroup)
                || !(getChildAt(1) instanceof ViewGroup)) {
            throw new IllegalArgumentException(
                    "Your childrens must be an instance of ViewGroup");
        }

        mTopContent = getChildAt(0);
        mMainContent = getChildAt(1);
    }
}

提示:可以在外部獲得DragLayout物件後設置OnLayoutDragingListener來監聽拖動過程

public class MainActivity extends AppCompatActivity {
    private FragmentManager fragmentManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fragmentManager=getSupportFragmentManager();
        fragmentManager.beginTransaction().add(R.id.container,new FirstFragment()).commit();
    }
}

public class FirstFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.first_fragment_layout,container,false);
        return view;
    }
}
注意:DragLayout必須要有且只有兩個子控制元件
<com.example.duodian.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:id="@+id/fl_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="40dp"
        android:paddingLeft="10dp"
        android:paddingTop="45dp"
        android:background="#f43">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="這是頂面板" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/main_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="40dp"
        android:paddingLeft="10dp"
        android:paddingTop="50dp"
        android:background="#ae3">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="這是主面板" />
    </LinearLayout>
</com.example.duodian.DragLayout>

本文章如有地方需要修改請在下方評論中指出。