android图片缩放手势检测类--ScaleGestureDetector
16lz
2021-01-23
此类位于 android.view包下,图片放大缩小的辅助类
源码如下:
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package android.view;import android.content.Context;import android.os.Handler;public class ScaleGestureDetector { public ScaleGestureDetector(Context context, ScaleGestureDetector.OnScaleGestureListener listener) { throw new RuntimeException("Stub!"); } public ScaleGestureDetector(Context context, ScaleGestureDetector.OnScaleGestureListener listener, Handler handler) { throw new RuntimeException("Stub!"); } public boolean onTouchEvent(MotionEvent event) { throw new RuntimeException("Stub!"); } public void setQuickScaleEnabled(boolean scales) { throw new RuntimeException("Stub!"); } public boolean isQuickScaleEnabled() { throw new RuntimeException("Stub!"); } public void setStylusScaleEnabled(boolean scales) { throw new RuntimeException("Stub!"); } public boolean isStylusScaleEnabled() { throw new RuntimeException("Stub!"); } public boolean isInProgress() { throw new RuntimeException("Stub!"); } public float getFocusX() { throw new RuntimeException("Stub!"); } public float getFocusY() { throw new RuntimeException("Stub!"); } public float getCurrentSpan() { throw new RuntimeException("Stub!"); } public float getCurrentSpanX() { throw new RuntimeException("Stub!"); } public float getCurrentSpanY() { throw new RuntimeException("Stub!"); } public float getPreviousSpan() { throw new RuntimeException("Stub!"); } public float getPreviousSpanX() { throw new RuntimeException("Stub!"); } public float getPreviousSpanY() { throw new RuntimeException("Stub!"); } public float getScaleFactor() { throw new RuntimeException("Stub!"); } public long getTimeDelta() { throw new RuntimeException("Stub!"); } public long getEventTime() { throw new RuntimeException("Stub!"); } public static class SimpleOnScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener { public SimpleOnScaleGestureListener() { throw new RuntimeException("Stub!"); } public boolean onScale(ScaleGestureDetector detector) { throw new RuntimeException("Stub!"); } public boolean onScaleBegin(ScaleGestureDetector detector) { throw new RuntimeException("Stub!"); } public void onScaleEnd(ScaleGestureDetector detector) { throw new RuntimeException("Stub!"); } } public interface OnScaleGestureListener { boolean onScale(ScaleGestureDetector var1); boolean onScaleBegin(ScaleGestureDetector var1); void onScaleEnd(ScaleGestureDetector var1); }}
Android通过ScaleGestureDetector实现图片缩放
介绍请参考:http://blog.csdn.net/true100/article/details/51141496
下边是一个github作者用其实现漫画浏览器的效果:github链接
xml布局:
<?xml version="1.0" encoding="utf-8"?>
调用的activity:
package zhan.scollzoomlistview;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.view.Window;import android.view.WindowManager;import android.widget.BaseAdapter;import android.widget.ImageView;import com.bumptech.glide.Glide;import java.util.ArrayList;import java.util.List;import zhan.scrollzoomlist.ScrollZoomListView;public class MainActivity extends AppCompatActivity { private List mData = new ArrayList<>(); private ScrollZoomListView list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //设置无标题 requestWindowFeature(Window.FEATURE_NO_TITLE); //设置全屏 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_main); mData.clear(); mData.add(R.mipmap.comic1); mData.add(R.mipmap.comic2); mData.add(R.mipmap.comic3); mData.add(R.mipmap.comic4); mData.add(R.mipmap.comic5); mData.add(R.mipmap.comic6); mData.add(R.mipmap.comic7); mData.add(R.mipmap.comic8); mData.add(R.mipmap.comic9); mData.add(R.mipmap.comic10); list = (ScrollZoomListView) findViewById(R.id.list); MyAdapter adapter = new MyAdapter(); adapter.setData(mData); list.setAdapter(adapter); } private class MyAdapter extends BaseAdapter { private List mData; public void setData(List data) { mData = data; notifyDataSetChanged(); } @Override public int getCount() { return mData == null ? 0 : mData.size(); } @Override public Object getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position + 1000; } @Override public View getView(int position, View convertView, ViewGroup parent) { MyHolder holder; if (convertView == null) { convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); holder = new MyHolder(); holder.picIv = (ImageView) convertView.findViewById(R.id.pic_iv); convertView.setTag(holder); } else { holder = (MyHolder) convertView.getTag(); } Glide.with(parent.getContext()) .load(mData.get(position)) .dontAnimate() .into(holder.picIv); return convertView; } } static class MyHolder { public ImageView picIv; }}
重要的滚动缩放listview
package zhan.scrollzoomlist;import android.animation.Animator;import android.animation.ValueAnimator;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.PointF;import android.support.annotation.NonNull;import android.support.v4.view.GestureDetectorCompat;import android.util.AttributeSet;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.animation.DecelerateInterpolator;import android.widget.ListView;import java.util.ArrayList;import java.util.LinkedList;import java.util.List;/** * Created by ruzhan on 17/1/9. */public class ScrollZoomListView extends ListView { private static final int INVALID_POINTER_ID = -1; private static final float DEFAULT_MIN_ZOOM_SCALE = 0.4f; private static final float DEFAULT_MAX_ZOOM_SCALE = 2.0f; private static final float DEFAULT_NORMAL_SCALE = 1.0f; private static final float DEFAULT_ZOOM_SCALE = 2.0f; private static final int DEFAULT_ZOOM_TO_SMALL_TIMES = 6; private static final int DEFAULT_ZOOM_SCALE_DURATION = 300; private static final int DEFAULT_ZOOM_TO_SMALL_SCALE_DURATION = 500; private static final int UN_LOADED_POINT = 10000; private static final int LOADED_POINT = 10001; private static int mActivePointerId = INVALID_POINTER_ID; private float mScaleFactor = DEFAULT_NORMAL_SCALE; private float mLastScale = DEFAULT_NORMAL_SCALE; private int mLoadedPointFlag = UN_LOADED_POINT; private float mMinZoomScale; private float mMaxZoomScale; private float mNormalScale; private float mZoomScale; private int mZoomToSmallTimes; private int mZoomScaleDuration; private int mZoomToSmallScaleDuration; private ScaleGestureDetector mScaleDetector; private GestureDetectorCompat mGestureDetectorCompat; private float maxWidth = 0.0f; private float maxHeight = 0.0f; private float mLastTouchX; private float mLastTouchY; private float mTranslateX; private float mTranslateY; private float mListViewWidth; private float mListViewHeight; private float mCenterX; private float mCenterY; private boolean isScaling = false; private boolean isPointerDown = false; private ValueAnimator mZoomValueAnimator; //synchronous ListView Zoom ScaleGestureDetector private List mOnScaleGestureListeners = new ArrayList<>(); //synchronous ListView Zoom GestureDetector private List mSimpleOnGestureListeners = new ArrayList<>(); //synchronous ListView Zoom Animation private List mOnListViewZoomListeners = new ArrayList<>(); private LinkedList mLinkPoints = new LinkedList<>(); public ScrollZoomListView(Context context) { this(context, null); } public ScrollZoomListView(Context context, AttributeSet attr) { super(context, attr); init(attr); } private void init(AttributeSet attr) { mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener()); mGestureDetectorCompat = new GestureDetectorCompat(getContext(), new ScrollReaderViewGestureListener()); TypedArray a = getContext().obtainStyledAttributes(attr, R.styleable.ScrollZoomListView, 0, 0); mMinZoomScale = a.getFloat(R.styleable.ScrollZoomListView_min_zoom_scale, DEFAULT_MIN_ZOOM_SCALE); mMaxZoomScale = a.getFloat(R.styleable.ScrollZoomListView_max_zoom_scale, DEFAULT_MAX_ZOOM_SCALE); mNormalScale = a.getFloat(R.styleable.ScrollZoomListView_normal_scale, DEFAULT_NORMAL_SCALE); mZoomScale = a.getFloat(R.styleable.ScrollZoomListView_zoom_scale, DEFAULT_ZOOM_SCALE); mZoomToSmallTimes = a.getInteger(R.styleable.ScrollZoomListView_zoom_to_small_times, DEFAULT_ZOOM_TO_SMALL_TIMES); mZoomScaleDuration = a.getInteger(R.styleable.ScrollZoomListView_zoom_scale_duration, DEFAULT_ZOOM_SCALE_DURATION); mZoomToSmallScaleDuration = a.getInteger(R.styleable.ScrollZoomListView_zoom_to_small_scale_duration, DEFAULT_ZOOM_TO_SMALL_SCALE_DURATION); a.recycle(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mZoomValueAnimator != null) { mZoomValueAnimator.cancel(); } //remove all listener removeOnScaleGestureListeners(); removeOnSimpleOnGestureListeners(); removeOnListViewZoomListeners(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mListViewWidth = MeasureSpec.getSize(widthMeasureSpec); mListViewHeight = MeasureSpec.getSize(heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override public boolean onTouchEvent(@NonNull MotionEvent ev) { mScaleDetector.onTouchEvent(ev); mGestureDetectorCompat.onTouchEvent(ev); int action = ev.getAction(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { isPointerDown = false; mLastTouchX = ev.getX(); mLastTouchY = ev.getY(); mActivePointerId = ev.getPointerId(0); break; } case MotionEvent.ACTION_POINTER_DOWN: { isPointerDown = true; break; } case MotionEvent.ACTION_MOVE: { int pointerIndex = ev.findPointerIndex(mActivePointerId); float x, y; try { x = ev.getX(pointerIndex); y = ev.getY(pointerIndex); } catch (IllegalArgumentException ex) { ex.printStackTrace(); return super.onTouchEvent(ev); } float dx = (x - mLastTouchX); float dy = (y - mLastTouchY); //ACTION_POINTER_DOWN ListView more distance * 6 if (isPointerDown) { dx = dx * mZoomToSmallTimes; dy = dy * mZoomToSmallTimes; } if (isScaling) { //ListView status is scaling float offsetX = mCenterX * (mLastScale - mScaleFactor); float offsetY = mCenterY * (mLastScale - mScaleFactor); //mTranslateX += offsetX; //mTranslateY += offsetY; //checkPointF(UN_LOADED_POINT, offsetX, offsetY); //mLastScale = mScaleFactor; } else if (mScaleFactor > mNormalScale) { //ListView not scaling, move ... mTranslateX += dx; mTranslateY += dy; checkPointF(UN_LOADED_POINT, dx, dy); correctTranslateValue(); } mLastTouchX = x; mLastTouchY = y; invalidate(); break; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { mActivePointerId = INVALID_POINTER_ID; break; } case MotionEvent.ACTION_POINTER_UP: { int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; int pointerId = ev.getPointerId(pointerIndex); if (pointerId == mActivePointerId) { int newPointerIndex = pointerIndex == 0 ? 1 : 0; mLastTouchX = ev.getX(newPointerIndex); mLastTouchY = ev.getY(newPointerIndex); mActivePointerId = ev.getPointerId(newPointerIndex); } break; } } return super.onTouchEvent(ev); } private void correctTranslateValue() { if (mTranslateX > 0.0f) { mTranslateX = 0.0f; } else if (mTranslateX < maxWidth) { mTranslateX = maxWidth; } if (mTranslateY > 0.0f) { mTranslateY = 0.0f; } else if (mTranslateY < maxHeight) { mTranslateY = maxHeight; } } @Override protected void dispatchDraw(@NonNull Canvas canvas) { canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.translate(mTranslateX, mTranslateY); canvas.scale(mScaleFactor, mScaleFactor); super.dispatchDraw(canvas); canvas.restore(); } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScaleBegin(ScaleGestureDetector detector) { for (ScaleGestureDetector.OnScaleGestureListener listener : mOnScaleGestureListeners) { listener.onScaleBegin(detector); } return super.onScaleBegin(detector); } @Override public boolean onScale(ScaleGestureDetector detector) { mScaleFactor *= detector.getScaleFactor(); float minFactor = Math.min(mScaleFactor, mMaxZoomScale); mScaleFactor = Math.max(mMinZoomScale, minFactor); maxWidth = mListViewWidth - (mListViewWidth * mScaleFactor); maxHeight = mListViewHeight - (mListViewHeight * mScaleFactor); mCenterX = detector.getFocusX(); mCenterY = detector.getFocusY(); float offsetX = mCenterX * (mLastScale - mScaleFactor); float offsetY = mCenterY * (mLastScale - mScaleFactor); mTranslateX += offsetX; mTranslateY += offsetY; checkPointF(UN_LOADED_POINT, offsetX, offsetY); mLastScale = mScaleFactor; isScaling = true; invalidate(); for (ScaleGestureDetector.OnScaleGestureListener listener : mOnScaleGestureListeners) { listener.onScale(detector); } return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { if (mScaleFactor < mNormalScale) { zoomList(mScaleFactor, mNormalScale, mZoomToSmallScaleDuration, LOADED_POINT); } isScaling = false; for (ScaleGestureDetector.OnScaleGestureListener listener : mOnScaleGestureListeners) { listener.onScaleEnd(detector); } } } private class ScrollReaderViewGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { //list view scroll call back... to outside for (GestureDetector.SimpleOnGestureListener listener : mSimpleOnGestureListeners) { listener.onScroll(e1, e2, distanceX, distanceY); } return super.onScroll(e1, e2, distanceX, distanceY); } @Override public boolean onSingleTapConfirmed(MotionEvent e) { // single click event,double call no call single for (GestureDetector.SimpleOnGestureListener listener : mSimpleOnGestureListeners) { listener.onSingleTapConfirmed(e); } return super.onSingleTapConfirmed(e); } @Override public boolean onDoubleTap(MotionEvent e) { //double click event if (mNormalScale < mScaleFactor) { zoomList(mScaleFactor, mNormalScale, mZoomScaleDuration, LOADED_POINT); } else if (mScaleFactor == mNormalScale) { mCenterX = e.getX(); mCenterY = e.getY(); zoomList(mScaleFactor, mZoomScale, mZoomScaleDuration, UN_LOADED_POINT); } for (GestureDetector.SimpleOnGestureListener listener : mSimpleOnGestureListeners) { listener.onDoubleTap(e); } return super.onDoubleTap(e); } } //let ListView zoom func private void zoomList(float startValue, float endValue, int duration, final int loadedPointFlag) { if (mZoomValueAnimator == null) { mZoomValueAnimator = new ValueAnimator(); mZoomValueAnimator.setInterpolator(new DecelerateInterpolator()); mZoomValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mScaleFactor = (Float) animation.getAnimatedValue(); float dx = mCenterX * (mLastScale - mScaleFactor); float dy = mCenterY * (mLastScale - mScaleFactor); PointF pointF = checkPointF(loadedPointFlag, dx, dy); if(pointF != null) { dx = -pointF.x; dy = -pointF.y; } mTranslateX += dx; mTranslateY += dy; maxWidth = mListViewWidth - (mListViewWidth * mScaleFactor); maxHeight = mListViewHeight - (mListViewHeight * mScaleFactor); correctZoomTranslateValue(); invalidate(); mLastScale = mScaleFactor; for (OnListViewZoomListener listener : mOnListViewZoomListeners) { listener.onListViewZoomUpdate(animation, mTranslateX, mTranslateY, mScaleFactor, mScaleFactor); } } }); mZoomValueAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { isScaling = true; for (OnListViewZoomListener listener : mOnListViewZoomListeners) { listener.onListViewStart(); } } @Override public void onAnimationEnd(Animator animation) { isScaling = false; mLoadedPointFlag = loadedPointFlag == UN_LOADED_POINT ? LOADED_POINT : UN_LOADED_POINT; for (OnListViewZoomListener listener : mOnListViewZoomListeners) { listener.onListViewCancel(); } } @Override public void onAnimationCancel(Animator animation) { isScaling = false; for (OnListViewZoomListener listener : mOnListViewZoomListeners) { listener.onListViewCancel(); } } @Override public void onAnimationRepeat(Animator animation) { } }); } if (!mZoomValueAnimator.isRunning()) { mZoomValueAnimator.setFloatValues(startValue, endValue); mZoomValueAnimator.setDuration(duration); mZoomValueAnimator.start(); } } private void correctZoomTranslateValue() { if (mTranslateX > 0.0f) { //zoom + if (mScaleFactor >= mNormalScale) { //params correct mTranslateX = 0.0f; } } else if (mTranslateX < maxWidth) { //zoom - if (mScaleFactor >= mNormalScale) { //params correct mTranslateX = maxWidth; } } if (mTranslateY > 0.0f) { //zoom + if (mScaleFactor >= mNormalScale) { //params correct mTranslateY = 0.0f; } } else if (mTranslateY < maxHeight) { //zoom - if (mScaleFactor >= mNormalScale) { //params correct mTranslateY = maxHeight; } } } private void putPointF(float dx, float dy) { if(mLoadedPointFlag == UN_LOADED_POINT) { PointF pointF = new PointF(dx, dy); mLinkPoints.addFirst(pointF); } } private PointF getPointF() { if(mLoadedPointFlag == LOADED_POINT) { return mLinkPoints.getLast(); } return null; } private PointF checkPointF(int loadedPointFlag, float dx, float dy) { PointF pointF = null; if(loadedPointFlag == UN_LOADED_POINT) { putPointF(dx, dy); }else if(loadedPointFlag == LOADED_POINT) { pointF = getPointF(); }else { throw new RuntimeException("ZoomListView loaded points error ! ! !"); } return pointF; } public float getMinZoomScale() { return mMinZoomScale; } public void setMinZoomScale(float mMinZoomScale) { this.mMinZoomScale = mMinZoomScale; } public float getMaxZoomScale() { return mMaxZoomScale; } public void setMaxZoomScale(float mMaxZoomScale) { this.mMaxZoomScale = mMaxZoomScale; } public float getNormalScale() { return mNormalScale; } public void setNormalScale(float mNormalScale) { this.mNormalScale = mNormalScale; } public float getZoomScale() { return mZoomScale; } public void setZoomScale(float mZoomScale) { this.mZoomScale = mZoomScale; } public int getZoomToSmallTimes() { return mZoomToSmallTimes; } public void setZoomToSmallTimes(int mZoomToSmallTimes) { this.mZoomToSmallTimes = mZoomToSmallTimes; } public int getZoomScaleDuration() { return mZoomScaleDuration; } public void setZoomScaleDuration(int mZoomScaleDuration) { this.mZoomScaleDuration = mZoomScaleDuration; } public void addOnScaleGestureListener(ScaleGestureDetector.SimpleOnScaleGestureListener listener) { if(listener != null) { if(!mOnScaleGestureListeners.contains(listener)) { mOnScaleGestureListeners.add(listener); } } } public void removeOnScaleGestureListener(ScaleGestureDetector.SimpleOnScaleGestureListener listener) { if(listener != null) { if(mOnScaleGestureListeners.contains(listener)) { mOnScaleGestureListeners.remove(listener); } } } public void removeOnScaleGestureListeners() { while (!mOnScaleGestureListeners.isEmpty()) { mOnScaleGestureListeners.remove(0); } } public void setSimpleOnGestureListener(GestureDetector.SimpleOnGestureListener listener) { if(listener != null) { if(!mSimpleOnGestureListeners.contains(listener)) { mSimpleOnGestureListeners.add(listener); } } } public void removeOnSimpleOnGestureListener(GestureDetector.SimpleOnGestureListener listener) { if(listener != null) { if(mSimpleOnGestureListeners.contains(listener)) { mSimpleOnGestureListeners.remove(listener); } } } public void removeOnSimpleOnGestureListeners() { while (!mSimpleOnGestureListeners.isEmpty()) { mSimpleOnGestureListeners.remove(0); } } public void setOnListViewZoomListener(OnListViewZoomListener listener) { if(listener != null) { if(!mOnListViewZoomListeners.contains(listener)) { mOnListViewZoomListeners.add(listener); } } } public void removeOnListViewZoomListener(OnListViewZoomListener listener) { if(listener != null) { if(mOnListViewZoomListeners.contains(listener)) { mOnListViewZoomListeners.remove(listener); } } } public void removeOnListViewZoomListeners() { while (!mOnListViewZoomListeners.isEmpty()) { mOnListViewZoomListeners.remove(0); } } public interface OnListViewZoomListener { void onListViewZoomUpdate(ValueAnimator animation, float translateX, float translateY, float scaleX, float scaleY); void onListViewStart(); void onListViewCancel(); }}
attrs.xml中的自定义元素:
<?xml version="1.0" encoding="utf-8"?>name="ScrollZoomListView"> name="min_zoom_scale" format="float" /> name="max_zoom_scale" format="float" /> name="normal_scale" format="float" /> name="zoom_scale" format="float" /> name="zoom_to_small_times" format="integer" /> name="zoom_scale_duration" format="integer" /> name="zoom_to_small_scale_duration" format="integer" />
类似此类的多点触控和手势识别还有很多,请自行搜索[android 多点触控和手势识别]学习
更多相关文章
- Android手势下拉抽屉效果
- Android 滑动手势侦测方法介绍
- Android中的手势
- 关于android多点触控
- Android图案密码,手势锁源码解析
- android中实现swipe的手势功能及页面拖动动画
- Android ImageView 图片等比缩放问题
- Android:GestureDetector手势识别类
- Android缩放图片Bitmap、Drawable