public class ZoomListView extends ListView implements SwipeListener { public static enum Action { LeftToRight, RightToLeft, TopToBottom, BottomToTop, None } private List<SwipeListener> actionListeners = new ArrayList<SwipeListener>(); private boolean SwipeFlag = false; private static final int HORIZONTAL_MIN_DISTANCE = 30; // The minimum private static final int VERTICAL_MIN_DISTANCE = 80; // The minimum distance private float downX, downY, upX, upY; // Coordinates private static final int INVALID_POINTER_ID = -1; private int mActivePointerId = INVALID_POINTER_ID; private ScaleGestureDetector mScaleDetector; private float mScaleFactor = 1.f; private float maxWidth = 0.0f; private float maxHeight = 0.0f; private float mLastTouchX; private float mLastTouchY; private float mPosX; private float mPosY; private float width; private float height; public ZoomListView(Context context) { super(context); mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener()); } public ZoomListView(Context context, AttributeSet attrs) { super(context, attrs); mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener()); } public ZoomListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener()); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { width = MeasureSpec.getSize(widthMeasureSpec); height = MeasureSpec.getSize(heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override public boolean onTouchEvent(@NonNull MotionEvent ev) { super.onTouchEvent(ev); final int action = ev.getAction(); mScaleDetector.onTouchEvent(ev); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { final float x = ev.getX(); final float y = ev.getY(); mLastTouchX = x; mLastTouchY = y; mActivePointerId = ev.getPointerId(0); downX = ev.getX(); downY = ev.getY(); SwipeFlag = true; break; } case MotionEvent.ACTION_MOVE: { final int pointerIndex = ev.findPointerIndex(mActivePointerId); final float x = ev.getX(pointerIndex); final float y = ev.getY(pointerIndex); final float dx = x - mLastTouchX; final float dy = y - mLastTouchY; float leftMargin = Math.abs(mPosX) / mScaleFactor; float rightMargin = this.getMeasuredWidth() - Math.abs(mPosX) / mScaleFactor; rightMargin -= this.getResources().getDisplayMetrics().widthPixels / mScaleFactor ; mPosX += dx; mPosY += dy; if (mPosX > 0.0f) mPosX = 0.0f; else if (mPosX < maxWidth) mPosX = maxWidth; if (mPosY > 0.0f) mPosY = 0.0f; else if (mPosY < maxHeight) mPosY = maxHeight; mLastTouchX = x; mLastTouchY = y; invalidate(); upX = ev.getX(); upY = ev.getY(); float deltaX = downX - upX; float deltaY = downY - upY; // horizontal swipe detection if (Math.abs(deltaX) > HORIZONTAL_MIN_DISTANCE) { // left or right if (deltaX < 0 && SwipeFlag && leftMargin < 10) { SwipeFlag = false; OnAction(Action.LeftToRight); break; } if (deltaX > 0 && SwipeFlag && rightMargin < 10) { SwipeFlag = false; OnAction(Action.RightToLeft); break; } } else { // vertical swipe detection if (Math.abs(deltaY) > VERTICAL_MIN_DISTANCE) { // top or down if (deltaY < 0 && SwipeFlag) { SwipeFlag = false; OnAction(Action.TopToBottom); break; } if (deltaY > 0 && SwipeFlag) { SwipeFlag = false; OnAction(Action.BottomToTop); break; } } } break; } case MotionEvent.ACTION_UP: { mActivePointerId = INVALID_POINTER_ID; break; } case MotionEvent.ACTION_CANCEL: { mActivePointerId = INVALID_POINTER_ID; break; } case MotionEvent.ACTION_POINTER_UP: { final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; final int pointerId = ev.getPointerId(pointerIndex); if (pointerId == mActivePointerId) { final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mLastTouchX = ev.getX(newPointerIndex); mLastTouchY = ev.getY(newPointerIndex); mActivePointerId = ev.getPointerId(newPointerIndex); } break; } } return true; } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.translate(mPosX, mPosY); canvas.scale(mScaleFactor, mScaleFactor); canvas.restore(); } @Override protected void dispatchDraw(@NonNull Canvas canvas) { canvas.save(Canvas.MATRIX_SAVE_FLAG); if (mScaleFactor == 1.0f) { mPosX = 0.0f; mPosY = 0.0f; } canvas.translate(mPosX, mPosY); canvas.scale(mScaleFactor, mScaleFactor); super.dispatchDraw(canvas); canvas.restore(); invalidate(); } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { mScaleFactor *= detector.getScaleFactor(); mScaleFactor = Math.max(1.0f, Math.min(mScaleFactor, 3.0f)); maxWidth = width - (width * mScaleFactor); maxHeight = height - (height * mScaleFactor); invalidate(); return true; } } @Override public void OnAction(Action action) { for(SwipeListener listener : actionListeners){ listener.OnAction(action); } } public void addActionListener(SwipeListener listener) { actionListeners.add(listener); } }
public interface SwipeListener extends EventListener { public void OnAction(Action action); }
如果想ListView中显示图片且自适应,需要设置 android:adjustViewBounds="true"