android ListView 可缩放,支持左右上下手势

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"

原文地址:https://www.cnblogs.com/nanfei/p/8675813.html