1. 程式人生 > >一行程式碼實現view拖拽移動、雙指縮放效果

一行程式碼實現view拖拽移動、雙指縮放效果

實現原理:通過重寫View.OnTouchListener實現拖拽與縮放效果;

注意:如果對含有子類的viewGroup設定,子類的大小可能不變,因為縮放的效果程式碼用的是view.layout(),只是改變了當前設定觸控監聽view的大小,裡面子類的大小是沒有變的;

如果想子類一起改變,需要用到View.setScaleX(),View.setScaleY()這兩個縮放大小方法去縮放,但是這個方法有個問題,在同一個view的onTouch中如果調這兩個方法,縮放會造成頻閃效果,原因是第二個觸控點座標發生了異常變化,所以不能再同一個view中呼叫,想出來的解決方案就是,在需要縮放的view上覆蓋一個觸控層,大小就是觸控操作的範圍,對這個觸控層設定觸控監聽,目的是得到縮放比,然後在效果view上進行縮放,下面類裡面也寫了這種方案,找到mEffectView註釋去掉就行了;

一行程式碼對你的view加上觸控效果:

myView.setOnTouchListener(new DragTouchListener());

設定屬性:關閉拖拽

DragTouchListener dragTouchListener =  new DragTouchListener(rlParent);
dragTouchListener.setCancleTouchDrag(false);

設定屬性:關閉縮放

dragTouchListener.setTouchTwoZoomEnable(false);

自定義OnTouchListener:DragTouchListener



import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;

/**
 * 對view實現拖拽移動、雙指縮放效果(預設全開啟)
 * 使用方法:1建立DragTouchListener例項 ;2設定監聽 view.setOnTouchListener(DragTouchListener);
 * Created by alan on 2019/1/3 0007.
 */
public class DragTouchListener implements View.OnTouchListener {

    private DisplayMetrics dm;
    private int maxWidth;
    private int maxHeight;
    private int lastX;
    private int lastY;
    //剛觸控時的view座標(用來獲取按下時view的大小)
    private int oriLeft;
    private int oriRight;
    private int oriTop;
    private int oriBottom;
    private float baseValue;
    private DragListener dragListener;
    float originalScale;
    private static final int TOUCH_NONE = 0x00;
    private static final int TOUCH_ONE = 0x20;
    private static final int TOUCH_TWO = 0x21;
    /**
     * 當前觸控模式:
     * 無觸控;
     * 單指觸控;
     * 雙指觸控;
     */
    private int currentTouchMode = TOUCH_NONE ;
    /**
     * 是否開啟:雙指觸控縮放
     */
    private boolean touchTwoZoomEnable = true;
    /**
     * 是否取消:觸控移動
     */
    private boolean isCancleTouchDrag = false;

    /**
     * 產生效果的view(縮放、拖拽效果)
     */
    private View mEffectView ;

    /**
     * 控制是否開啟兩指觸控縮放
     * @param touchTwoZoomEnable
     */
    public DragTouchListener setTouchTwoZoomEnable(boolean touchTwoZoomEnable) {
        this.touchTwoZoomEnable = touchTwoZoomEnable;
        return this;
    }

    /**
     * 設定:是否取消拖拽移動
     * @param cancleTouchDrag
     */
    public DragTouchListener setCancleTouchDrag(boolean cancleTouchDrag) {
        isCancleTouchDrag = cancleTouchDrag;
        return this;
    }

    public interface DragListener {
        void actionDown(View v);

        void actionUp(View v);

        void dragging(View listenerView, int left, int top, int right, int bottom);

        void zooming(float scale);
    }

//    public void setDragListener(DragListener dragListener) {
//        this.dragListener = dragListener;
//    }

    //    public DragTouchListener(DisplayMetrics dm) {
//        this.dm = dm;
//        maxWidth = dm.widthPixels;
//        maxHeight = dm.heightPixels - 50;
//    }

    public DragTouchListener(final ViewGroup limitParent,DragListener dragListener) {
//        maxHeight = viewGroup.getHeight();
//        maxWidth = viewGroup.getWidth();
        this(limitParent);
        this.dragListener = dragListener;
    }

    public DragTouchListener(){
        this(null);
    }

    /**
     *
     * @param limitParent 拖動限制區域,防止移出螢幕(null:拖動無限制)
     */
    public DragTouchListener(final ViewGroup limitParent) {
        if (limitParent !=null) {
            ViewTreeObserver vto = limitParent.getViewTreeObserver();
            vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    maxHeight = limitParent.getMeasuredHeight();
                    maxWidth = limitParent.getMeasuredWidth();
    //                Log.i("TAG", "maxHeight: "+maxHeight+", maxWidth"+maxWidth);
                    return true;
                }

            });
        }
        dragListener = new DragListener() {
            @Override
            public void actionDown(View v) {

            }
            @Override
            public void actionUp(View v) {
            }
            @Override
            public void dragging(View listenerView, int left, int top, int right, int bottom) {

            }
            @Override
            public void zooming(float scale) {
            }
        };
    }

    private boolean moveFlag ;
    @Override
    public boolean onTouch(View v, MotionEvent event) {

//        int action = event.getAction();
        int action = event.getAction()& MotionEvent.ACTION_MASK;
//        Log.i("TAG", "Touch:"+action);
        //遮蔽父控制元件攔截onTouch事件
        v.getParent().requestDisallowInterceptTouchEvent(true);
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                dragListener.actionDown(v);
                lastX = (int) event.getRawX();
                lastY = (int) event.getRawY();
                oriLeft = v.getLeft();
                oriRight = v.getRight();
                oriTop = v.getTop();
                oriBottom = v.getBottom();
                currentTouchMode = TOUCH_ONE;
                baseValue = 0;
                lastScale = 1;
//                originalScale =  v.getScaleX();
                break;
            case MotionEvent.ACTION_POINTER_DOWN://多指觸控
                oriLeft = v.getLeft();
                oriRight = v.getRight();
                oriTop = v.getTop();
                oriBottom = v.getBottom();
                currentTouchMode = TOUCH_TWO;
                baseValue = 0;
                lastScale = 1 ;
                break;
            /**
             * layout(l,t,r,b)
             * l  Left position, relative to parent
             t  Top position, relative to parent
             r  Right position, relative to parent
             b  Bottom position, relative to parent
             * */
            case MotionEvent.ACTION_MOVE:

                    moveFlag = !moveFlag;
                    if (event.getPointerCount() == 2) {
    //                if (currentTouchMode == TOUCH_TWO ) {
                        if (touchTwoZoomEnable) {
                            float x = event.getX(0) - event.getX(1);
                            float y = event.getY(0) - event.getY(1);

//                            Log.i("TAG", "---y:點1: "+event.getY(0)+"  點2: "+event.getY(1));
                            float value = (float) Math.sqrt(x * x + y * y);// 計算兩點的距離
                            if (baseValue == 0) {
                                baseValue = value;
//                                Log.i("TAG","  value: "+value+"  ; baseValue: "+baseValue);
                            } else {
                                if ((value - baseValue) >= 10 || value - baseValue <= -10) {
                                    // 當前兩點間的距離 除以 手指落下時兩點間的距離就是需要縮放的比例。
                                    float scale = value / baseValue;
    //                                Log.i("TAG", "onTouch-scale: "+scale+"  value: "+value+"  ; baseValue: "+baseValue);
                                    //縮放view(不能用當前touch方法裡的view,會造成頻閃效果)(只能在其他view呼叫)
//                                    mEffectView.setScaleX(scale);
//                                    mEffectView.setScaleY(scale);

                                    //改變大小進行縮放(只能縮放當前view的大小,如果是父佈局,則裡面的子控制元件無法縮小)
                                    touchZoom(v,scale);

                                    this.dragListener.zooming(scale);

                                }
                            }
                        }
                    } else if (currentTouchMode == TOUCH_ONE) {//1個手指
                        //如果取消拖拽,觸控就交給系統處理
                        if(isCancleTouchDrag){
                            return false;
                        }
                        //移動圖片位置
                        touchDrag(v, event);
                    }

                break;
            case MotionEvent.ACTION_UP:
                baseValue = 0;
                dragListener.actionUp(v);
                break;
            default:
                currentTouchMode = TOUCH_NONE;
                    break;
        }
        return true;
    }


    private float lastScale = 1;
    /**
     * 縮放view
     * @param v
     * @param scale  當前距離按下時的比例  (0.8:縮小到0.8倍)
     */
    private void touchZoom(View v,float scale){
        int oriWidth = Math.abs(oriRight - oriLeft);
        int oriHeight = Math.abs(oriBottom - oriTop);

//        if(lastScale == 0)lastScale = scale;
        //需要縮放的比例(1-0.9=0.1,需要縮小0.1倍;-0.1:放大0.1倍)
        float  zoomScale = (lastScale - scale);

        int dx = (int) (oriWidth*zoomScale/2f);
        int dy = (int) (oriHeight*zoomScale/2f);

//        Log.i("TAG", "---------------zoomScale: "+zoomScale);
//        Log.i("TAG", "--------------------------dx: "+dx);

        int left = v.getLeft() + dx;
        int top = v.getTop() + dy;
        int right = v.getRight() - dx;
        int bottom = v.getBottom() - dy;

        v.layout(left, top, right, bottom);

        lastScale = scale;

    }

    private void touchDrag(View v, MotionEvent event) {

        int dx = (int) event.getRawX() - lastX;
        int dy = (int) event.getRawY() - lastY;

        int left = v.getLeft() + dx;
        int top = v.getTop() + dy;
        int right = v.getRight() + dx;
        int bottom = v.getBottom() + dy;

        if (maxWidth !=0 && maxHeight!=0) {
            //防止移出螢幕
            if(left < 0){
                left = 0;
                right = left + v.getWidth();
            }
            if(right > maxWidth){
                right = maxWidth;
                left = right - v.getWidth();
            }
            if(top < 0){
                top = 0;
                bottom = top + v.getHeight();
            }
            if(bottom > maxHeight){
                bottom = maxHeight;
                top = bottom - v.getHeight();
            }
        }

        v.layout(left, top, right, bottom);

        dragListener.dragging(v, left, top, right, bottom);
//                Log.i("TAG", "position" + left +", " + top + ", " + right + ", " + bottom);
        lastX = (int) event.getRawX();
        lastY = (int) event.getRawY();
    }

}