Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android ViewDragHelper解析

Android ViewDragHelper解析

編輯:關於Android編程

簡介: 一般我們在自定義ViewGroup 的時候會通常都會用到onInterceptTouchEvent ,onTouchEvent 這些方法去進行距離的判斷然後利用scroller 去進行目標的移動,從而實現ViewGroup 的自定義。此方法不但判斷麻煩,而且邏輯復雜,不易操作,今天給大家要價講的這個工具ViewDragHelper 是谷歌IO大會上推出的觸摸輔助開發工具,極大的簡化了開發自定義VIewGroup 的難度。
這裡寫圖片描述

1.廢話不多說 ,首先要初始化 ViewDragHelper 這個工具類:


  final float density = getResources().getDisplayMetrics().density;
        final float minVel = MIN_FLING_VELOCITY * density;
        //指定好需要處理拖動的ViewGroup和回調 就可以開始使用了
        mViewDragHelper = ViewDragHelper.create(this, new DefaultDragHelper());
        mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
        //設置minVelocity
        mViewDragHelper.setMinVelocity(minVel);

2.初始化 ViewDragHelper.Callback 這個觸摸操作類:並初始化 要操作VIew的位置控制方法回掉類:


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

            mLeft = left;
            mLeftSize =(-left)/ (dip2px(getContext(),80) * 1.0f);
            Log.d(TAG, "onViewPositionChanged()--" + "left:" + left + ",top:" + top + ",dx:" + dx + ",dy:" + dy + "mLeftSize--" + mLeftSize);
            invalidate();
        }

3.接下來我們會操作要移動VIew 的滑動限制距離 方法:


  /**
         * 限制子View水平拖拉操作。默認不支持水平操作,重寫該方法提供新的水平坐標(根據提供的渴望的水平坐標)
         * 不重寫就不會支持水平坐標變化
         *
         * @param child Child view being dragged
         * @param left  Attempted motion along the X axis
         * @param dx    Proposed change in position for left
         * @return The new clamped position for left
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            final int leftBound =v_content.getPaddingLeft()-dip2px(getContext(),80);
            final int rightBound =v_content.getPaddingRight();
            final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
            Log.d(TAG, "clampViewPositionHorizontal()--left:" + left + ",dx:" + dx+"v_content.getPaddingLeft(),"+v_content.getPaddingLeft());
            return newLeft;
        }
    }
限制View活動的范圍後 ,但我們釋放手指的時候我們會操作另外一個方法:
    /**
         * 手指離開屏幕
         * 後續View 的坐標處理
         * 比如  滑到超過一半 直接滑到滿屏 又或者滑到不到一半的時候
         * 還原坐標
         *
         * @param releasedChild
         * @param xvel
         * @param yvel
        * */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            Log.d(TAG, "onViewReleased()--xv:" + xvel + ",yv:" + yvel);
            //上拉
            if (mLeftSize > 0.5) {
                openCover();
            } else {
                closeCover();
            }
            invalidate();
        }

這樣一個基本的VIew滑動類就基本實現了。
接下來我貼出一個完整的示例:

 package com.example.administrator.myapplication.fragment;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.example.administrator.myapplication.R;
import com.example.administrator.myapplication.widget.BotomDragLayout;
import com.example.administrator.myapplication.widget.LeftScrollDeleteDragLayout;

/**
 * 側滑刪除列表
 * 2016/4/21
 * gxj
 */
public class LeftScrollDeleteLayoutFragment extends Fragment {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getActivity().setTitle("LeftScrollDeleteLayoutFragment");
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        final LeftScrollDeleteDragLayout view = (LeftScrollDeleteDragLayout) inflater.inflate(R.layout.left_scroll_delete_layout, container, false);
        View view_bg =view.findViewById(R.id.del);
        view_bg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(view.STATE == BotomDragLayout.CLOSEING){
                    view.openCover();
                }else{
                    view.closeCover();
                    Toast.makeText(getActivity(),"刪除",Toast.LENGTH_SHORT).show();
                }
            }
        });
        return view;
    }

}

所需要的xml布局:



        

完整的自定義側滑類代碼:

package com.example.administrator.myapplication.widget;
import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import com.example.administrator.myapplication.R;

/**
 *列表側滑刪除效果
 * author gxj
 * date 2016.5.22
 */
public class LeftScrollDeleteDragLayout extends ViewGroup {
    private final String TAG = this.getClass().getSimpleName();
    private ViewDragHelper mViewDragHelper;
    /**
     * Minimum velocity that will be detected as a fling
     */
    private static final int MIN_FLING_VELOCITY = 400; // dips per second
    /**
     * 菜單欄的狀態
     */
    public  int STATE = 0;
    public static final int CLOSEING = 0;
    public static final int OPENED = 1;
    private View v_content;
    private int viewWidth = 0;
    /**
     * 滑動view 的頂部位置
     */
    private int mLeft;
    /**
     * 滑動view 的滑動距離占自身高度的比例
     */
    private float mLeftSize;
    private View view_bg;

    public LeftScrollDeleteDragLayout(Context context) {
        this(context, null);
        init();
    }

    public LeftScrollDeleteDragLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        init();
    }

    public LeftScrollDeleteDragLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        v_content = findViewById(R.id.view_content);
        view_bg = findViewById(R.id.view_bg);
        viewWidth = v_content.getHeight();
    }

    @Override
    public void computeScroll() {
        if (mViewDragHelper.continueSettling(true)) {
            postInvalidateOnAnimation();
        }
    }

    public void openCover() {
        STATE = LeftScrollDeleteDragLayout.OPENED;
        mViewDragHelper.smoothSlideViewTo(v_content, -dip2px(getContext(),80), 0);
        postInvalidateOnAnimation();
    }

    public void closeCover() {
        STATE = LeftScrollDeleteDragLayout.CLOSEING;
        mViewDragHelper.smoothSlideViewTo(v_content, 0, 0);
        postInvalidateOnAnimation();
    }

    private void init() {
        final float density = getResources().getDisplayMetrics().density;
        final float minVel = MIN_FLING_VELOCITY * density;
        //指定好需要處理拖動的ViewGroup和回調 就可以開始使用了
        mViewDragHelper = ViewDragHelper.create(this, new DefaultDragHelper());
        mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
        //設置minVelocity
        mViewDragHelper.setMinVelocity(minVel);
    }

    private class DefaultDragHelper extends ViewDragHelper.Callback {
        @Override
        public boolean tryCaptureView(View view, int i) {
            return view == v_content;
        }

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

            mLeft = left;
            mLeftSize =(-left)/ (dip2px(getContext(),80) * 1.0f);
            Log.d(TAG, "onViewPositionChanged()--" + "left:" + left + ",top:" + top + ",dx:" + dx + ",dy:" + dy + "mLeftSize--" + mLeftSize);
            invalidate();
        }

        /**
         * 當captureview被捕獲時回調
         *
         * @param capturedChild
         * @param activePointerId
         */
        @Override
        public void onViewCaptured(View capturedChild, int activePointerId) {
            super.onViewCaptured(capturedChild, activePointerId);
            Log.d(TAG, "onViewCaptured()--:");
        }

        /**
         * 手指離開屏幕
         * 後續View 的坐標處理
         * 比如  滑到超過一半 直接滑到滿屏 又或者滑到不到一半的時候
         * 還原坐標
         *
         * @param releasedChild
         * @param xvel
         * @param yvel
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            Log.d(TAG, "onViewReleased()--xv:" + xvel + ",yv:" + yvel);
            //上拉
            if (mLeftSize > 0.5) {
                openCover();
            } else {
                closeCover();
            }
            invalidate();
        }

        /**
         * 當觸摸到邊緣的時候會調用
         * @param edgeFlags
         * @param pointerId
         */
        @Override
        public void onEdgeTouched(int edgeFlags, int pointerId) {
            super.onEdgeTouched(edgeFlags, pointerId);
            Log.d(TAG, "onEdgeTouched()");
        }

        @Override
        public boolean onEdgeLock(int edgeFlags) {
            Log.d(TAG, "onEdgeLock()");
            return super.onEdgeLock(edgeFlags);
        }

        /**
         * 當觸摸到邊緣的時候會調用
         *
         * @param edgeFlags
         * @param pointerId 可以指定觸摸邊緣的子View
         */
        @Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            super.onEdgeDragStarted(edgeFlags, pointerId);
            Log.d(TAG, "onEdgeDragStarted()");
            mViewDragHelper.captureChildView(v_content, pointerId);
        }

        /**
         * 返回當前移動的View 的position
         *
         * @param index
         * @return
         */
        @Override
        public int getOrderedChildIndex(int index) {
            return super.getOrderedChildIndex(index);
        }

        /**
         * 限制水平移動范圍
         * Return the magnitude of a draggable child view's horizontal range of motion in pixels.
         * 似乎作用不大,其他情況只用於判斷是否可以拖動
         * 具體返回值真正起作用在於{@link ViewDragHelper#smoothSlideViewTo(View, int, int)}
         *
         * @param child Child view to check
         * @return range of horizontal motion in pixels
         */
        @Override
        public int getViewHorizontalDragRange(View child) {
            return child ==v_content?dip2px(getContext(),80):0;
        }


        /**
         * 限制子View水平拖拉操作。默認不支持水平操作,重寫該方法提供新的水平坐標(根據提供的渴望的水平坐標)
         * 不重寫就不會支持水平坐標變化
         *
         * @param child Child view being dragged
         * @param left  Attempted motion along the X axis
         * @param dx    Proposed change in position for left
         * @return The new clamped position for left
         */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            final int leftBound =v_content.getPaddingLeft()-dip2px(getContext(),80);
            final int rightBound =v_content.getPaddingRight();
            final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
            Log.d(TAG, "clampViewPositionHorizontal()--left:" + left + ",dx:" + dx+"v_content.getPaddingLeft(),"+v_content.getPaddingLeft());
            return newLeft;
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        final int action = MotionEventCompat.getActionMasked(event);
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            mViewDragHelper.cancel();
            return false;
        }
        //通過這個方法判斷是否攔截 滑動事件
        boolean flag = mViewDragHelper.shouldInterceptTouchEvent(event);
        return flag;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //通過這個方法判斷是否處理攔截的觸摸事件
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

    /**
     * 丈量所有控件的高度
     * 可以得到每個控件的最終高度
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
        int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
                resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
        viewWidth = v_content.getMeasuredWidth();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.e("onLayout", l + "|" + t + "|" + r + "|" + b);
        v_content.layout(mLeft, t, mLeft+viewWidth,dip2px(getContext(),200) );
        view_bg.layout(0, 0, r, dip2px(getContext(),200));
    }
    /**
     * 根據手機的分辨率從 dp 的單位 轉成為 px(像素)
     */
    private  int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved