Android 佈局手勢縮放,可滑動檢視,實現類似Imgview圖片縮放效果
阿新 • • 發佈:2019-02-08
Android 自定義View 佈局手勢縮放,可滑動檢視,子View實現類似Imgview圖片縮放效果
自定義View,使用ViewDragHelper和ScaleGestureDetector結合簡單的實現類似PhotoView的效果的控制元件,使佈局內的子view能隨著手勢進行縮放,滑動檢視的效果
package com.example.lbq.one.view;
import android.content.Context;
import android.graphics.Canvas;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
/**
* Created by lbq on 2018/1/9.
*/
public class GestureView extends RelativeLayout {
private ViewDragHelper mViewDragHelper;//用於處理子View 的滑動
private ScaleGestureDetector mGesture;//用與處理雙手的縮放手勢
private float mZoomScale = 1.0f;//預設的縮放比為1
private Context mContext;
public GestureView(Context context) {
this(context, null);
}
public GestureView(Context context, AttributeSet attrs) {
this(context, attrs, 0 );
}
public GestureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
private void init() {
initViewDrag();
mGesture = new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {
//隨著手勢操作,回撥的方法,
@Override
public boolean onScale(ScaleGestureDetector detector) {
float previousSpan = detector.getPreviousSpan();//縮放發生前的兩點距離
float currentSpan = detector.getCurrentSpan();//縮放發生時的兩點距離
if (previousSpan < currentSpan)//放大
{
mZoomScale = mZoomScale + (currentSpan - previousSpan) / previousSpan;
} else {
mZoomScale = mZoomScale - (previousSpan - currentSpan) / previousSpan;
}
//確保放大最多為2倍,最少不能小於原圖
if (mZoomScale > 2) {
mZoomScale = 2;
} else if (mZoomScale < 1) {
mZoomScale = 1;
}
setScaleX(mZoomScale);
setScaleY(mZoomScale);
//這裡呼叫的是本自定義View的方法,是對本自定義view進行的縮放
/*在這裡呼叫getChildView(index)的進行縮放,雖然控制元件顯示大小改變了,但是在ViewDragHelper的回撥方法中獲得的View child的getWidth()和getHeigit()是原來的大小,不會發生改變*/
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
/**
* @param detector
*/
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
});
}
//將需要的view放置在佈局中
public void addChildView(final View view) {
//保證控制元件只有一個子View,方便操作
if (getChildCount() != 0) {
throw new IllegalStateException("this view can only have one child!");
} else {
addView(view);
//確保子view永遠鋪滿控制元件
RelativeLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
view.setLayoutParams(params);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//將事件交給ViewDragHelper和ScaleGestureDetector 處理
mViewDragHelper.processTouchEvent(event);
return mGesture.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//由ViewDragHelper處理攔截
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
private void initViewDrag() {
mViewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
//這裡的return true表示自view可以滑動,false表示不處理滑動
return true;
}
/*這裡是控制子view左右滑動的回撥,child為本自定義view的子控制元件,left表示意圖從手指操作子view從左邊界滑動的距離,大於0表示向右移動,小於0表示向左移動,是從手指的移動測出的理論值。方法的返回值表示實際上控制元件移動的距離,可以用返回值控制邊界*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//放大後控制元件的寬度-主佈局的寬度 因為View chlid的控制元件使用的是match_parent,所以直接用父佈局的寬取值
if (left < (getWidth() - getWidth() * mZoomScale) / 2) {
return (int) ((getWidth() - getWidth() * mZoomScale) / 2);
}
if (left > ((mZoomScale * getWidth() - getWidth()) / 2)) {
return (int) ((mZoomScale * getWidth() - getWidth()) / 2);
}
return left;
/** 這裡進行邊界處理,因為這裡實際最大能放大2倍,那麼實際會有3/4的左右滑動空間才會把子View(child)完全劃出可見範圍,這裡為了保證view的可見性,使用1/2確保子view始終在可見範圍之內*/
}
/*這個方法是控制上下滑動的,跟上面的方法意義一樣,只是方向上不一樣,top表示從頂部邊界計算的距離,向下為正,向上為負*/
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
if (top < (getHeight() - getHeight() * mZoomScale) / 2) {
return ((int) (getHeight() - getHeight() * mZoomScale) / 2);
}
if (top > (mZoomScale * getHeight() - getHeight()) / 2) {
return (int) ((mZoomScale * getHeight() - getHeight()) / 2);
}
return top;
}
});
}
}