Android:实现最简单的单指移动、双指缩放的图片组件

本例实现了最简单的单指移动、双指缩放的图片组件,效果图如下:

技术分享     技术分享


技术分享     技术分享


功能:

1.单指移动,双指缩放。

2.可控制缩放范围,防止过大或过小;初始化时自动缩放至组件大小,并居中显示。

3.边界控制,防止图片“移出去了”。

4.可使用在xml中,并自动适应组件大小。

5.代码简洁!!!技术分享


核心代码:DragScaleView.java

package com.sina.simplegestureimage;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

/**
 * DragScaleView
 * Created by hanswim on 15-1-23.
 */
public class DragScaleView extends View {
    //监听图片缩放
    private ScaleGestureDetector mScaleDetector;
    //监听图片移动
    private GestureDetector mGestureDetector;

    //当前的缩放比例
    private float mScaleFactor = 1.0f;

    public DragScaleView(Context context) {
        super(context);
    }

    public DragScaleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public DragScaleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void init(Context context) {
        mScaleDetector = new ScaleGestureDetector(context, new SimpleScaleListenerImpl());
        mGestureDetector = new GestureDetector(context, new SimpleGestureListenerImpl());
    }

    private Paint bmpPaint = new Paint();
    //图片资源
    private Bitmap bmp;
    //图片的宽高
    private int bmpWidth, bmpHeight;

    public void setImageResource(int id) {
        bmp = BitmapFactory.decodeResource(getResources(), id);
        bmpWidth = bmp.getWidth();
        bmpHeight = bmp.getHeight();

        initViewSize();

        invalidate();
    }

    //绘制图片的起始位置
    private float mPosX, mPosY;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (bmp == null) {
            return;
        }

        if (!hasGetViewSize) {
            initViewSize();
        }

        canvas.save();
        checkBounds();
        //以图片的中心为基点进行缩放
        canvas.scale(mScaleFactor, mScaleFactor, mPosX + bmpWidth / 2, mPosY + bmpHeight / 2);

        canvas.drawBitmap(bmp, mPosX, mPosY, bmpPaint);
        canvas.restore();
    }

//    private float lastX, lastY;

//    private static final int INVALID_POINTER_ID = -1;
//    private int mActivePointerId = INVALID_POINTER_ID;

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        //双指缩放
        mScaleDetector.onTouchEvent(event);
        //单指移动
        mGestureDetector.onTouchEvent(event);

        return true;

        //也可以自己实现“单指移动图片”
        /*
        int action = MotionEventCompat.getActionMasked(event);

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                final int pointerIndex = MotionEventCompat.getActionIndex(event);
                mActivePointerId = MotionEventCompat.getPointerId(event, pointerIndex);

                lastX = event.getX();
                lastY = event.getY();
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                // Find the index of the active pointer and fetch its position
                final int pointerIndex = MotionEventCompat.findPointerIndex(event, mActivePointerId);
                float currentX = MotionEventCompat.getX(event, pointerIndex);
                float currentY = MotionEventCompat.getY(event, pointerIndex);

                mPosX += (currentX - lastX);
                mPosY += (currentY - lastY);
                invalidate();

                lastX = currentX;
                lastY = currentY;

                break;
            }
            case MotionEvent.ACTION_POINTER_UP: {
                final int pointerIndex = MotionEventCompat.getActionIndex(event);
                final int pointerId = MotionEventCompat.getPointerId(event, pointerIndex);

                if (pointerId == mActivePointerId) {
                    // This was our active pointer going up. Choose a new
                    // active pointer and adjust accordingly.
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    lastX = MotionEventCompat.getX(event, newPointerIndex);
                    lastY = MotionEventCompat.getY(event, newPointerIndex);
                    mActivePointerId = MotionEventCompat.getPointerId(event, newPointerIndex);
                }


                break;
            }

            case MotionEvent.ACTION_UP: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }
        }
        */

    }

    /**
     * 不能超出边界.
     * 原则是:图片较小时任意一条边都不能出了边界,图片较大任意一条边都不能进入边界。宽度和高度分别独立计算。
     */
    private void checkBounds() {
        if (mScaleFactor > widthScale) {
            //宽度方向已经填满
            mPosX = Math.min(mPosX, (mScaleFactor - 1) * (bmpWidth / 2));
            mPosX = Math.max(mPosX, viewWidth - bmpWidth - (mScaleFactor - 1) * (bmpWidth / 2));
        } else {
            mPosX = Math.max(mPosX, (mScaleFactor - 1) * (bmpWidth / 2));
            mPosX = Math.min(mPosX, viewWidth - bmpWidth - (mScaleFactor - 1) * (bmpWidth / 2));
        }

        if (mScaleFactor > heightScale) {
            //高度方向已经填满
            mPosY = Math.min(mPosY, (mScaleFactor - 1) * (bmpHeight / 2));
            mPosY = Math.max(mPosY, viewHeight - bmpHeight - (mScaleFactor - 1) * (bmpHeight / 2));
        } else {
            mPosY = Math.max(mPosY, (mScaleFactor - 1) * (bmpHeight / 2));
            mPosY = Math.min(mPosY, viewHeight - bmpHeight - (mScaleFactor - 1) * (bmpHeight / 2));
        }
    }

    private int viewWidth, viewHeight;
    //组件尺寸只需要获取一次
    private boolean hasGetViewSize;

    private void initViewSize() {
        viewWidth = getWidth();
        viewHeight = getHeight();

        if (viewWidth > 0 && viewHeight > 0) {
            hasGetViewSize = true;

            widthScale = 1.0f * viewWidth / bmpWidth;
            heightScale = 1.0f * viewHeight / bmpHeight;
            //初始缩放比例(使组件刚好铺满)
            mScaleFactor = Math.min(widthScale, heightScale);

            //初始时图片居中绘制
            mPosX = viewWidth / 2 - bmpWidth / 2;
            mPosY = viewHeight / 2 - bmpHeight / 2;
        }
    }

    /**
     * 宽度和高度放大多少倍时,刚好填满此方向的屏幕
     */
    private float widthScale, heightScale;

    //缩放
    private class SimpleScaleListenerImpl extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();
            //缩放倍数范围:0.3~3
            mScaleFactor = Math.max(0.3f, Math.min(mScaleFactor, 3.0f));

            invalidate();
            return true;
        }
    }

    //移动
    private class SimpleGestureListenerImpl extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            mPosX -= distanceX;
            mPosY -= distanceY;

            invalidate();
            return true;
        }
    }
}

在Activity中使用:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.drag_scale);

        RadioButton landscapeRBtn = (RadioButton) findViewById(R.id.radio_landscape);
        final DragScaleView dragView = (DragScaleView) findViewById(R.id.drag_scale_view);

        dragView.setImageResource(R.drawable.cat_boarder);

        landscapeRBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    dragView.setImageResource(R.drawable.cat_boarder);
                } else {
                    dragView.setImageResource(R.drawable.cat_boarder_p);
                }
            }
        });
    }

======

源代码即将奉上,尽情期待。


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。