實現原理:通過重寫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();
}
}