不說(shuō)廢話了,進(jìn)入我們今天的主題吧。

創(chuàng)新互聯(lián)主要從事成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)通城,十年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):13518219792
先貼上前面內(nèi)容的地址:
Android手勢(shì)ImageView三部曲(一)
Android手勢(shì)ImageView三部曲(二)
Android手勢(shì)ImageView三部曲(三)
前面我們講到了ScaleGestureDetector這個(gè)工具類,我在疑惑,為什么搞出一個(gè)ScaleGestureDetector,不順帶把什么旋轉(zhuǎn)、移動(dòng)、做了呢? 好吧~! 谷歌肯定還是想給開(kāi)發(fā)者留一點(diǎn)自己的空間哈。
仿照ScaleGestureDetector,我們來(lái)定義一個(gè)叫MoveGestureDetector的工具類(專門(mén)用于檢測(cè)滑動(dòng)手勢(shì)),在定義MoveGestureDetector之前,因?yàn)槲覀冞€要考慮到之后的RotateGestureDetector等等..于是我們定一個(gè)叫BaseGestureDetector把一些公共的方法抽取出來(lái):
public abstract class BaseGestureDetector {
protected final Context mContext;
protected boolean mGestureInProgress;
protected MotionEvent mPrevEvent;
protected MotionEvent mCurrEvent;
protected float mCurrPressure;
protected float mPrevPressure;
protected long mTimeDelta;
/**
* 上一次event的pressure/這一次的pressure,這是一個(gè)什么概念呢?
* 我們想象一下當(dāng)你手指按下然后滑動(dòng)并且到離開(kāi)屏幕,
* 手指觸碰到屏幕的壓力會(huì)越來(lái)越小,直到手指移開(kāi)屏幕
*/
protected static final float PRESSURE_THRESHOLD = 0.67f;
public BaseGestureDetector(Context context) {
mContext = context;
}
/**
* 跟ScaleGesture一樣,我們也把事件的處理放在此方法中
* @param event
* @return
*/
public boolean onTouchEvent(MotionEvent event){
//為了獲取到ACTION_POINTER_UP等事件必須加上& MotionEvent.ACTION_MASK
final int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
/**
* 是否調(diào)用handleInProgressEvent方法
*/
if (!mGestureInProgress) {
//如果mGestureInProgress為false的時(shí)候,執(zhí)行開(kāi)始操作
handleStartProgressEvent(actionCode, event);
} else {
//處理手勢(shì)
handleInProgressEvent(actionCode, event);
}
return true;
}
/**
* 準(zhǔn)備處理手勢(shì)
* @param actionCode
* @param event
*/
protected abstract void handleStartProgressEvent(int actionCode, MotionEvent event);
/**
* 正在處理手勢(shì)
* @param actionCode
* @param event
*/
protected abstract void handleInProgressEvent(int actionCode, MotionEvent event);
/**
* 更新event的狀態(tài),保存之前的event,獲取當(dāng)前event
* @param curr
*/
protected void updateStateByEvent(MotionEvent curr){
final MotionEvent prev = mPrevEvent;
// Reset mCurrEvent
if (mCurrEvent != null) {
mCurrEvent.recycle();
mCurrEvent = null;
}
mCurrEvent = MotionEvent.obtain(curr);
// 之前的event跟現(xiàn)在的event之間的時(shí)間差
mTimeDelta = curr.getEventTime() - prev.getEventTime();
// 之前的event跟腺癌的event之間的手指壓力值
mCurrPressure = curr.getPressure(curr.getActionIndex());
mPrevPressure = prev.getPressure(prev.getActionIndex());
}
/**
* 重置所有狀態(tài)
*/
protected void resetState() {
if (mPrevEvent != null) {
mPrevEvent.recycle();
mPrevEvent = null;
}
if (mCurrEvent != null) {
mCurrEvent.recycle();
mCurrEvent = null;
}
mGestureInProgress = false;
}
/**
* Returns {@code true} if a gesture is currently in progress.
* @return {@code true} if a gesture is currently in progress, {@code false} otherwise.
*/
public boolean isInProgress() {
return mGestureInProgress;
}
/**
* Return the time difference in milliseconds between the previous accepted
* GestureDetector event and the current GestureDetector event.
*
* @return Time difference since the last move event in milliseconds.
*/
public long getTimeDelta() {
return mTimeDelta;
}
/**
* Return the event time of the current GestureDetector event being
* processed.
*
* @return Current GestureDetector event time in milliseconds.
*/
public long getEventTime() {
return mCurrEvent.getEventTime();
}
}
然后我們定義一個(gè)叫MoveGestureDetector的類去繼承BaseGestureDetector,然后事件兩個(gè)抽象方法:
public class MoveGestureDetector extends BaseGestureDetector{
@Override
protected void handleStartProgressEvent(int actionCode, MotionEvent event){
}
@Override
protected void handleInProgressEvent(int actionCode, MotionEvent event){
}
}
那我們?nèi)绻麢z測(cè)到了事件的話該怎么通知調(diào)用者呢?是的,我們需要用到回調(diào),我們看看ScaleGestureDetector的回調(diào)接口咋定義的:
public interface OnScaleGestureListener {
public boolean onScale(ScaleGestureDetector detector);
public boolean onScaleBegin(ScaleGestureDetector detector);
public void onScaleEnd(ScaleGestureDetector detector);
}
public static class SimpleOnScaleGestureListener implements OnScaleGestureListener {
public boolean onScale(ScaleGestureDetector detector) {
return false;
}
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
public void onScaleEnd(ScaleGestureDetector detector) {
// Intentionally empty
}
}
里面定義了一個(gè)接口一個(gè)叫OnScaleGestureListener,一個(gè)類叫SimpleOnScaleGestureListener,SimpleOnScaleGestureListener是實(shí)現(xiàn)了OnScaleGestureListener,于是我們MoveGestureDetector的接口可以這么定義了:
/**
* 仿照ScaleGestureDetector我們也定義三個(gè)方法
*/
public interface OnMoveGestureListener {
/**
* 移動(dòng)的時(shí)候回調(diào)
*/
public boolean onMove(MoveGestureDetector detector);
/**
* 移動(dòng)開(kāi)始的時(shí)候回調(diào)
*/
public boolean onMoveBegin(MoveGestureDetector detector);
/**
* 移動(dòng)結(jié)束的時(shí)候回調(diào)
*/
public void onMoveEnd(MoveGestureDetector detector);
}
public static class SimpleOnMoveGestureListener implements OnMoveGestureListener {
public boolean onMove(MoveGestureDetector detector) {
return false;
}
public boolean onMoveBegin(MoveGestureDetector detector) {
return true;
}
public void onMoveEnd(MoveGestureDetector detector) {
// Do nothing, overridden implementation may be used
}
}
好啦!框子都搭好了,我們用的時(shí)候呢,就可以這么用了:
1、創(chuàng)建一個(gè)MoveGestureDetector
public MatrixImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
//創(chuàng)建一個(gè)縮放手勢(shì)監(jiān)測(cè)器
scaleDetector=new ScaleGestureDetector(context,onScaleGestureListener);
//創(chuàng)建一個(gè)MoveGestureDetector
moveGestureDetector=new MoveGestureDetector(context,onMoveGestureListener);
}
2、把事件給MoveGestureDetector
@Override
public boolean onTouchEvent(MotionEvent event) {
//把事件給scaleDetector
scaleDetector.onTouchEvent(event);
//把事件給moveGestureDetector
moveGestureDetector.onTouchEvent(event);
return true;
}
3、獲取回調(diào)值
private MoveGestureDetector.SimpleOnMoveGestureListener onMoveGestureListener=new MoveGestureDetector.SimpleOnMoveGestureListener(){
@Override
public boolean onMove(MoveGestureDetector detector) {
return super.onMove(detector);
}
};
怎么樣?是不是跟ScaleGestureDetector一樣了呢?清晰明了哈,框子是搭起來(lái)了,下面我們來(lái)實(shí)現(xiàn)下它的邏輯(也就是實(shí)現(xiàn)下handleStartProgressEvent跟handleInProgressEvent方法):
每行都有注釋,我就直接上代碼了
*/
public class MoveGestureDetector extends BaseGestureDetector {
/**
* 仿照ScaleGestureDetector我們也定義三個(gè)方法
*/
public interface OnMoveGestureListener {
/**
* 移動(dòng)的時(shí)候回調(diào)
*/
public boolean onMove(MoveGestureDetector detector);
/**
* 移動(dòng)開(kāi)始的時(shí)候回調(diào)
*/
public boolean onMoveBegin(MoveGestureDetector detector);
/**
* 移動(dòng)結(jié)束的時(shí)候回調(diào)
*/
public void onMoveEnd(MoveGestureDetector detector);
}
public static class SimpleOnMoveGestureListener implements OnMoveGestureListener {
public boolean onMove(MoveGestureDetector detector) {
return false;
}
public boolean onMoveBegin(MoveGestureDetector detector) {
return true;
}
public void onMoveEnd(MoveGestureDetector detector) {
// Do nothing, overridden implementation may be used
}
}
private static final PointF FOCUS_DELTA_ZERO = new PointF();
private final OnMoveGestureListener mListener;
private PointF mCurrFocusInternal;
private PointF mPrevFocusInternal;
private PointF mFocusExternal = new PointF();
private PointF mFocusDeltaExternal = new PointF();
public MoveGestureDetector(Context context, OnMoveGestureListener listener) {
super(context);
mListener = listener;
}
@Override
protected void handleStartProgressEvent(int actionCode, MotionEvent event){
switch (actionCode) {
//當(dāng)手指按下的時(shí)候
case MotionEvent.ACTION_DOWN:
//重置一下所有狀態(tài)(currevent跟preevent)
resetState(); // In case we missed an UP/CANCEL event
//獲取當(dāng)前event作為mPrevEvent
mPrevEvent = MotionEvent.obtain(event);
//重置兩次event的時(shí)間間隔
mTimeDelta = 0;
//更新state
updateStateByEvent(event);
break;
case MotionEvent.ACTION_MOVE:
//回調(diào)onMoveBegin,mGestureInProgress決定是否繼續(xù)處理事件(執(zhí)行handleInProgressEvent)
//mGestureInProgress由調(diào)用者決定
mGestureInProgress = mListener.onMoveBegin(this);
break;
}
}
/**
* 處理移動(dòng)事件
*/
@Override
protected void handleInProgressEvent(int actionCode, MotionEvent event){
switch (actionCode) {
//當(dāng)抬起或者取消的時(shí)候
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
//回調(diào)onMoveEnd,move處理結(jié)束
mListener.onMoveEnd(this);
//重置所有的state
resetState();
break;
case MotionEvent.ACTION_MOVE:
//更新?tīng)顟B(tài)
updateStateByEvent(event);
//當(dāng)上一次event的press值/這一次event值大于臨界值的時(shí)候開(kāi)始觸發(fā)onMove
//因?yàn)槿绻鸆urrPressure / mPrevPressure很小的話,可能手指已經(jīng)離開(kāi)屏幕了
if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
/**
* 回調(diào)onMove方法,并獲取updatePrevious
* updatePrevious標(biāo)記是由調(diào)用者決定,
* updatePrevious是否更新之前的event,
* 如果為false的話mPrevEvent一直是我們?cè)赿own的時(shí)候賦值的event
* 如果為true的話,每次move事件處理完都會(huì)把最新的event賦給mPrevEvent
*/
final boolean updatePrevious = mListener.onMove(this);
if (updatePrevious) {
mPrevEvent.recycle();
mPrevEvent = MotionEvent.obtain(event);
}
}
break;
}
}
/**
* 參考ScaleGestureDetector
* move核心處理方法
* 重寫(xiě)父類的updateStateByEvent
*
*/
protected void updateStateByEvent(MotionEvent curr) {
super.updateStateByEvent(curr);
final MotionEvent prev = mPrevEvent;
// 獲取當(dāng)前所有手指的中心點(diǎn)
mCurrFocusInternal = determineFocalPoint(curr);
//獲取之前event所有手指的中心點(diǎn)
mPrevFocusInternal = determineFocalPoint(prev);
//判斷是否有手指中途添加或者移除
boolean mSkipNextMoveEvent = prev.getPointerCount() != curr.getPointerCount();
//有移除的話mFocusDeltaExternal就等于空(0,0),沒(méi)有的話就算出前面event跟當(dāng)前event中心點(diǎn)距離
mFocusDeltaExternal = mSkipNextMoveEvent ? FOCUS_DELTA_ZERO : new PointF(mCurrFocusInternal.x - mPrevFocusInternal.x, mCurrFocusInternal.y - mPrevFocusInternal.y);
//累加距離值
mFocusExternal.x += mFocusDeltaExternal.x;
mFocusExternal.y += mFocusDeltaExternal.y;
}
/**
* 獲取所有手指的中間點(diǎn)坐標(biāo)(參考ScaleGestureDetector)
*/
private PointF determineFocalPoint(MotionEvent e){
// Number of fingers on screen
final int pCount = e.getPointerCount();
float x = 0f;
float y = 0f;
for(int i = 0; i < pCount; i++){
x += e.getX(i);
y += e.getY(i);
}
return new PointF(x/pCount, y/pCount);
}
/**
* 獲取距離值累加過(guò)后的值
*/
public float getFocusX() {
return mFocusExternal.x;
}
public float getFocusY() {
return mFocusExternal.y;
}
/**
* 獲取上一個(gè)事件到下一個(gè)事件之間的x跟y的距離值
*/
public PointF getFocusDelta() {
return mFocusDeltaExternal;
}
}
好啦!!寫(xiě)完哈,我們來(lái)使用一下:
package com.leo.gestureimageview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;
import com.leo.gestureimageview.GestureDetectors.MoveGestureDetector;
public class MatrixImageView extends ImageView {
private Matrix currMatrix;
private float scaleFactor=1f;//當(dāng)前圖片的縮放值
private float transX,transY;
private ScaleGestureDetector scaleDetector;
private MoveGestureDetector moveGestureDetector;
public MatrixImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
//創(chuàng)建一個(gè)縮放手勢(shì)監(jiān)測(cè)器
scaleDetector=new ScaleGestureDetector(context,onScaleGestureListener);
//創(chuàng)建一個(gè)MoveGestureDetector
moveGestureDetector=new MoveGestureDetector(context,onMoveGestureListener);
}
private void initView() {
currMatrix = new Matrix();
DisplayMetrics dm = getResources().getDisplayMetrics();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
bitmap = Bitmap.createScaledBitmap(bitmap, dm.widthPixels, dm.heightPixels, true);
setImageBitmap(bitmap);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//把事件給scaleDetector
scaleDetector.onTouchEvent(event);
//把事件給moveGestureDetector
moveGestureDetector.onTouchEvent(event);
return true;
}
private void setMatrix(){
currMatrix.reset();
currMatrix.postScale(scaleFactor,scaleFactor,getMeasuredWidth()/2,getMeasuredHeight()/2);
currMatrix.postTranslate(transX,transY);
setImageMatrix(currMatrix);
}
private ScaleGestureDetector.SimpleOnScaleGestureListener onScaleGestureListener=new ScaleGestureDetector.SimpleOnScaleGestureListener(){
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor(); // scale change since previous event
// Don't let the object get too small or too large.
scaleFactor = Math.max(0.1f, Math.min(scaleFactor, 10.0f));
setMatrix();
/**
* 因?yàn)間etScaleFactor=當(dāng)前兩個(gè)手指之間的距離(preEvent)/手指按下時(shí)候兩個(gè)點(diǎn)的距離(currEvent)
* 這里如果返回true的話,會(huì)在move操作的時(shí)候去更新之前的event,
* 如果為false的話,不會(huì)去更新之前按下時(shí)候保存的event
*/
return true;
}
};
private MoveGestureDetector.SimpleOnMoveGestureListener onMoveGestureListener=new MoveGestureDetector.SimpleOnMoveGestureListener(){
@Override
public boolean onMove(MoveGestureDetector detector) {
transX=detector.getFocusX();
transY=detector.getFocusY();
setMatrix();
return true;
}
};
}
好啦~!! 短短幾行代碼就可以玩起來(lái)了,效果圖我就不附了哈,小伙伴自己運(yùn)行一下,那么MoveGestureDetector我們實(shí)現(xiàn)了,想必RotateGestureDetector也是很快就會(huì)實(shí)現(xiàn)了,哈哈~~! 我就直接用貼上國(guó)外大神寫(xiě)的代碼了:
public class RotateGestureDetector extends TwoFingerGestureDetector {
/**
* Listener which must be implemented which is used by RotateGestureDetector
* to perform callbacks to any implementing class which is registered to a
* RotateGestureDetector via the constructor.
*
* @see SimpleOnRotateGestureListener
*/
public interface OnRotateGestureListener {
public boolean onRotate(RotateGestureDetector detector);
public boolean onRotateBegin(RotateGestureDetector detector);
public void onRotateEnd(RotateGestureDetector detector);
}
/**
* Helper class which may be extended and where the methods may be
* implemented. This way it is not necessary to implement all methods
* of OnRotateGestureListener.
*/
public static class SimpleOnRotateGestureListener implements OnRotateGestureListener {
public boolean onRotate(RotateGestureDetector detector) {
return false;
}
public boolean onRotateBegin(RotateGestureDetector detector) {
return true;
}
public void onRotateEnd(RotateGestureDetector detector) {
// Do nothing, overridden implementation may be used
}
}
private final OnRotateGestureListener mListener;
private boolean mSloppyGesture;
public RotateGestureDetector(Context context, OnRotateGestureListener listener) {
super(context);
mListener = listener;
}
@Override
protected void handleStartProgressEvent(int actionCode, MotionEvent event){
switch (actionCode) {
case MotionEvent.ACTION_POINTER_DOWN:
// At least the second finger is on screen now
resetState(); // In case we missed an UP/CANCEL event
mPrevEvent = MotionEvent.obtain(event);
mTimeDelta = 0;
updateStateByEvent(event);
// See if we have a sloppy gesture
mSloppyGesture = isSloppyGesture(event);
if(!mSloppyGesture){
// No, start gesture now
mGestureInProgress = mListener.onRotateBegin(this);
}
break;
case MotionEvent.ACTION_MOVE:
if (!mSloppyGesture) {
break;
}
// See if we still have a sloppy gesture
mSloppyGesture = isSloppyGesture(event);
if(!mSloppyGesture){
// No, start normal gesture now
mGestureInProgress = mListener.onRotateBegin(this);
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (!mSloppyGesture) {
break;
}
break;
}
}
@Override
protected void handleInProgressEvent(int actionCode, MotionEvent event){
switch (actionCode) {
case MotionEvent.ACTION_POINTER_UP:
// Gesture ended but
updateStateByEvent(event);
if (!mSloppyGesture) {
mListener.onRotateEnd(this);
}
resetState();
break;
case MotionEvent.ACTION_CANCEL:
if (!mSloppyGesture) {
mListener.onRotateEnd(this);
}
resetState();
break;
case MotionEvent.ACTION_MOVE:
updateStateByEvent(event);
// Only accept the event if our relative pressure is within
// a certain limit. This can help filter shaky data as a
// finger is lifted.
if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
final boolean updatePrevious = mListener.onRotate(this);
if (updatePrevious) {
mPrevEvent.recycle();
mPrevEvent = MotionEvent.obtain(event);
}
}
break;
}
}
@Override
protected void resetState() {
super.resetState();
mSloppyGesture = false;
}
/**
* Return the rotation difference from the previous rotate event to the current
* event.
*
* @return The current rotation //difference in degrees.
*/
public float getRotationDegreesDelta() {
double diffRadians = Math.atan2(mPrevFingerDiffY, mPrevFingerDiffX) - Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX);
return (float) (diffRadians * 180 / Math.PI);
}
}
最后把我們結(jié)合了ScaleDetector、MoveDetector、RotateDetector的一個(gè)手勢(shì)縮放ImageView的代碼給大家:
package com.leo.gestureimageview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;
import com.leo.gestureimageview.GestureDetectors.MoveGestureDetector;
import com.leo.gestureimageview.GestureDetectors.RotateGestureDetector;
public class MatrixImageView2 extends ImageView {
private Matrix mMatrix = new Matrix();
private float mScaleFactor =1f;
private float mRotationDegrees = 0.f;
private float mFocusX = 0.f;
private float mFocusY = 0.f;
private ScaleGestureDetector mScaleDetector;
private RotateGestureDetector mRotateDetector;
private MoveGestureDetector mMoveDetector;
public MatrixImageView2(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
private void initView() {
//初始化模式為初始狀態(tài)
DisplayMetrics dm = getResources().getDisplayMetrics();
//給ImageView設(shè)置一張圖片(此處為了測(cè)試直接在imageview里面設(shè)置了一張測(cè)試圖片)
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
bitmap = Bitmap.createScaledBitmap(bitmap, dm.widthPixels, dm.heightPixels, true);
setImageBitmap(bitmap);
mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
mRotateDetector = new RotateGestureDetector(getContext(), new RotateListener());
mMoveDetector = new MoveGestureDetector(getContext(), new MoveListener());
mFocusX = dm.widthPixels/2f;
mFocusY = dm.heightPixels/2f;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//把縮放事件給mScaleDetector
mScaleDetector.onTouchEvent(event);
//把旋轉(zhuǎn)事件個(gè)mRotateDetector
mRotateDetector.onTouchEvent(event);
//把移動(dòng)事件給mMoveDetector
mMoveDetector.onTouchEvent(event);
return true;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor(); // scale change since previous event
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
changeMatrix();
return true;
}
}
private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
@Override
public boolean onRotate(RotateGestureDetector detector) {
mRotationDegrees -= detector.getRotationDegreesDelta();
changeMatrix();
return true;
}
}
private class MoveListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
@Override
public boolean onMove(MoveGestureDetector detector) {
PointF d = detector.getFocusDelta();
mFocusX += d.x;
mFocusY += d.y;
changeMatrix();
return true;
}
}
private void changeMatrix(){
float scaledImageCenterX = (getDrawable().getIntrinsicWidth()*mScaleFactor)/2;
float scaledImageCenterY = (getDrawable().getIntrinsicHeight()*mScaleFactor)/2;
mMatrix.reset();
mMatrix.postScale(mScaleFactor, mScaleFactor);
mMatrix.postRotate(mRotationDegrees, scaledImageCenterX, scaledImageCenterY);
mMatrix.postTranslate(mFocusX - scaledImageCenterX, mFocusY - scaledImageCenterY);
setImageMatrix(mMatrix);
}
}
好啦~~~小伙伴也可以自己下載一下這個(gè)框架的代碼去研究,我這呢也只是把自己學(xué)習(xí)的心得分享給大家。
https://github.com/Almeros/android-gesture-detectors
嗯嗯!說(shuō)了那么多,最后讓我們看看傳說(shuō)中的PhotoView到底是咋實(shí)現(xiàn)的。
photoview的github鏈接:
https://github.com/chrisbanes/PhotoViewary/
看完我們之前的內(nèi)容,再去看PhotoView的話,你可能不會(huì)那么迷茫了,下面讓我們一起揭開(kāi)它的神秘面紗:
首先PhotoView的用法呢,很簡(jiǎn)單,小伙伴像用ImageView一樣用它就可以了:
<uk.co.senab.photoview.PhotoView android:clickable="true" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="fitxy" />
好啦!!現(xiàn)在就可以對(duì)圖片進(jìn)行縮放、旋轉(zhuǎn)、移動(dòng)操作啦~是不是很爽呢?
但是注意:
photoview的縮放類型不支持,不然就直接報(bào)錯(cuò)退出了:
android:scaleType="matrix"
我們來(lái)看看它的源碼:
public class PhotoView extends ImageView implements IPhotoView {
private PhotoViewAttacher mAttacher;
private ScaleType mPendingScaleType;
public PhotoView(Context context) {
this(context, null);
}
public PhotoView(Context context, AttributeSet attr) {
this(context, attr, 0);
}
public PhotoView(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
super.setScaleType(ScaleType.MATRIX);
init();
}
protected void init() {
if (null == mAttacher || null == mAttacher.getImageView()) {
mAttacher = new PhotoViewAttacher(this);
}
if (null != mPendingScaleType) {
setScaleType(mPendingScaleType);
mPendingScaleType = null;
}
}
@Override
public void setRotationTo(float rotationDegree) {
mAttacher.setRotationTo(rotationDegree);
}
@Override
public void setRotationBy(float rotationDegree) {
mAttacher.setRotationBy(rotationDegree);
}
@Override
public boolean canZoom() {
return mAttacher.canZoom();
}
@Override
public RectF getDisplayRect() {
return mAttacher.getDisplayRect();
}
@Override
public void getDisplayMatrix(Matrix matrix) {
mAttacher.getDisplayMatrix(matrix);
}
@Override
public boolean setDisplayMatrix(Matrix finalRectangle) {
return mAttacher.setDisplayMatrix(finalRectangle);
}
@Override
public float getMinimumScale() {
return mAttacher.getMinimumScale();
}
@Override
public float getMediumScale() {
return mAttacher.getMediumScale();
}
@Override
public float getMaximumScale() {
return mAttacher.getMaximumScale();
}
@Override
public float getScale() {
return mAttacher.getScale();
}
@Override
public ScaleType getScaleType() {
return mAttacher.getScaleType();
}
@Override
public Matrix getImageMatrix() {
return mAttacher.getImageMatrix();
}
@Override
public void setAllowParentInterceptOnEdge(boolean allow) {
mAttacher.setAllowParentInterceptOnEdge(allow);
}
@Override
public void setMinimumScale(float minimumScale) {
mAttacher.setMinimumScale(minimumScale);
}
@Override
public void setMediumScale(float mediumScale) {
mAttacher.setMediumScale(mediumScale);
}
@Override
public void setMaximumScale(float maximumScale) {
mAttacher.setMaximumScale(maximumScale);
}
@Override
public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {
mAttacher.setScaleLevels(minimumScale, mediumScale, maximumScale);
}
@Override
// setImageBitmap calls through to this method
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
if (null != mAttacher) {
mAttacher.update();
}
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
if (null != mAttacher) {
mAttacher.update();
}
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
if (null != mAttacher) {
mAttacher.update();
}
}
@Override
protected boolean setFrame(int l, int t, int r, int b) {
boolean changed = super.setFrame(l, t, r, b);
if (null != mAttacher) {
mAttacher.update();
}
return changed;
}
@Override
public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
mAttacher.setOnMatrixChangeListener(listener);
}
@Override
public void setOnLongClickListener(OnLongClickListener l) {
mAttacher.setOnLongClickListener(l);
}
@Override
public void setOnPhotoTapListener(OnPhotoTapListener listener) {
mAttacher.setOnPhotoTapListener(listener);
}
@Override
public void setOnViewTapListener(OnViewTapListener listener) {
mAttacher.setOnViewTapListener(listener);
}
@Override
public void setScale(float scale) {
mAttacher.setScale(scale);
}
@Override
public void setScale(float scale, boolean animate) {
mAttacher.setScale(scale, animate);
}
@Override
public void setScale(float scale, float focalX, float focalY, boolean animate) {
mAttacher.setScale(scale, focalX, focalY, animate);
}
@Override
public void setScaleType(ScaleType scaleType) {
if (null != mAttacher) {
mAttacher.setScaleType(scaleType);
} else {
mPendingScaleType = scaleType;
}
}
@Override
public void setZoomable(boolean zoomable) {
mAttacher.setZoomable(zoomable);
}
@Override
public Bitmap getVisibleRectangleBitmap() {
return mAttacher.getVisibleRectangleBitmap();
}
@Override
public void setZoomTransitionDuration(int milliseconds) {
mAttacher.setZoomTransitionDuration(milliseconds);
}
@Override
public IPhotoView getIPhotoViewImplementation() {
return mAttacher;
}
@Override
public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {
mAttacher.setOnDoubleTapListener(newOnDoubleTapListener);
}
@Override
public void setOnScaleChangeListener(PhotoViewAttacher.OnScaleChangeListener onScaleChangeListener) {
mAttacher.setOnScaleChangeListener(onScaleChangeListener);
}
@Override
public void setOnSingleFlingListener(PhotoViewAttacher.OnSingleFlingListener onSingleFlingListener) {
mAttacher.setOnSingleFlingListener(onSingleFlingListener);
}
@Override
protected void onDetachedFromWindow() {
mAttacher.cleanup();
mAttacher = null;
super.onDetachedFromWindow();
}
@Override
protected void onAttachedToWindow() {
init();
super.onAttachedToWindow();
}
}
可以看到,代碼并不多,才200多行(哈哈!!我們自己實(shí)現(xiàn)的MatrixImageView 100行都還不到呢!!開(kāi)玩笑哈,PhotoView里面考慮的東西跟兼容性,我們寫(xiě)的MatrixImageView遠(yuǎn)遠(yuǎn)不及哈),主要的處理所及都在PhotoViewAttacher這個(gè)類中:
PhotoViewAttacher.java:
代碼太多,我們看看它的構(gòu)造方法
public PhotoViewAttacher(ImageView imageView, boolean zoomable) {
mImageView = new WeakReference<>(imageView);
imageView.setDrawingCacheEnabled(true);
imageView.setOnTouchListener(this);
ViewTreeObserver observer = imageView.getViewTreeObserver();
if (null != observer)
observer.addOnGlobalLayoutListener(this);
// Make sure we using MATRIX Scale Type
setImageViewScaleTypeMatrix(imageView);
if (imageView.isInEditMode()) {
return;
}
// Create Gesture Detectors...
mScaleDragDetector = VersionedGestureDetector.newInstance(
imageView.getContext(), this);
mGestureDetector = new GestureDetector(imageView.getContext(),
new GestureDetector.SimpleOnGestureListener() {
// forward long click listener
@Override
public void onLongPress(MotionEvent e) {
if (null != mLongClickListener) {
mLongClickListener.onLongClick(getImageView());
}
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
if (mSingleFlingListener != null) {
if (getScale() > DEFAULT_MIN_SCALE) {
return false;
}
if (MotionEventCompat.getPointerCount(e1) > SINGLE_TOUCH
|| MotionEventCompat.getPointerCount(e2) > SINGLE_TOUCH) {
return false;
}
return mSingleFlingListener.onFling(e1, e2, velocityX, velocityY);
}
return false;
}
});
mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
mBaseRotation = 0.0f;
// Finally, update the UI so that we're zoomable
setZoomable(zoomable);
}
可以看到,它也是創(chuàng)建了一個(gè)mScaleDragDetector跟一個(gè)mGestureDetector用于監(jiān)聽(tīng)手勢(shì)變幻,那么事件處理在什么地方呢?
我們?cè)跇?gòu)造方法還發(fā)現(xiàn)了一行代碼,給當(dāng)前imageView設(shè)置觸碰監(jiān)聽(tīng):
imageView.setOnTouchListener(this);
小伙伴猜都猜到了,現(xiàn)在就是把事件給事件監(jiān)聽(tīng)器了:
@Override
public boolean onTouch(View v, MotionEvent ev) {
boolean handled = false;
if (mZoomEnabled && hasDrawable((ImageView) v)) {
ViewParent parent = v.getParent();
switch (ev.getAction()) {
case ACTION_DOWN:
// First, disable the Parent from intercepting the touch
// event
if (null != parent) {
parent.requestDisallowInterceptTouchEvent(true);
} else {
LogManager.getLogger().i(LOG_TAG, "onTouch getParent() returned null");
}
// If we're flinging, and the user presses down, cancel
// fling
cancelFling();
break;
case ACTION_CANCEL:
case ACTION_UP:
// If the user has zoomed less than min scale, zoom back
// to min scale
if (getScale() < mMinScale) {
RectF rect = getDisplayRect();
if (null != rect) {
v.post(new AnimatedZoomRunnable(getScale(), mMinScale,
rect.centerX(), rect.centerY()));
handled = true;
}
}
break;
}
// Try the Scale/Drag detector
if (null != mScaleDragDetector) {
boolean wasScaling = mScaleDragDetector.isScaling();
boolean wasDragging = mScaleDragDetector.isDragging();
handled = mScaleDragDetector.onTouchEvent(ev);
boolean didntScale = !wasScaling && !mScaleDragDetector.isScaling();
boolean didntDrag = !wasDragging && !mScaleDragDetector.isDragging();
mBlockParentIntercept = didntScale && didntDrag;
}
// Check to see if the user double tapped
if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) {
handled = true;
}
}
return handled;
}
最后處理完畢事件后,就是一系列的回調(diào)了,回調(diào)完畢后就應(yīng)該給ImageView重新設(shè)置matrix對(duì)象了,比如縮放:
@Override
public void setScale(float scale, float focalX, float focalY,
boolean animate) {
ImageView imageView = getImageView();
if (null != imageView) {
// Check to see if the scale is within bounds
if (scale < mMinScale || scale > mMaxScale) {
LogManager
.getLogger()
.i(LOG_TAG,
"Scale must be within the range of minScale and maxScale");
return;
}
if (animate) {
imageView.post(new AnimatedZoomRunnable(getScale(), scale,
focalX, focalY));
} else {
mSuppMatrix.setScale(scale, scale, focalX, focalY);
checkAndDisplayMatrix();
}
}
}
其它的類似哈~~~ 代碼還是挺多的(考慮的情況比較多)可想而之,要寫(xiě)好一個(gè)自定義組件還不是那么簡(jiǎn)單的事哦,不過(guò)還是加油吧~!
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
文章題目:Android自定義GestureDetector實(shí)現(xiàn)手勢(shì)ImageView
URL地址:http://www.yijiale78.com/article18/ghdpdp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)、企業(yè)建站、微信小程序、關(guān)鍵詞優(yōu)化、移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站排名
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)