一行程式碼實現view拖拽移動、雙指縮放效果
阿新 • • 發佈:2019-01-03
實現原理:通過重寫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(); } }