android实现图片缩放、移动、单击退出、双击缩放

实现思路

思路:重写用于显示图片的ImageView,定义ScaleGestureDetector(缩放手势检测)类型、GestureDetector(双击手势检测)类型的变量进行手势检测并重写方法实现图片的缩放、移动、单击退出、双击放大等功能。
重写的方法implements OnTouchListener接口,重写onTouch方法对手势进行监听

ScaleGestureDetector mScaleGestureDetector ;
GestureDetector mGestureDetector;
public boolean onTouch(View v, MotionEvent event)
{
    if (mGestureDetector.onTouchEvent(event))
        return true;                           //单双击监听
    mScaleGestureDetector.onTouchEvent(event); //缩放监听
    图片移动处理的code;                           //移动监听及处理    
}

为什么单双击监听的时候检测到单双击直接就返回而不执行后面的代码?这里主要考虑到但双击的时候是自动缩放,一般我们不会在自动缩放的时候还会移动图片,但是如果是人手动考手势缩放图片,往往还会伴随着移动图片的处理,所以检测到手动缩放的代码时不返回,仍然要执行后面的移动图片的代码。

单双击处理代码:

mGestureDetector = new GestureDetector(context,
                new SimpleOnGestureListener()
                {
                    //单击退出
                    @Override
                    public boolean onSingleTapConfirmed(MotionEvent e) {
                        EventBus.getDefault().post(new ZoomBus());
                        return super.onSingleTapConfirmed(e);
                    }

                    //双击放大缩小
                    @Override
                    public boolean onDoubleTap(MotionEvent e)
                    {
                        if (isAutoScale == true)  //首先判断是否正在自动缩放,如果是直接返回true
                            return true;

                        float x = e.getX();
                        float y = e.getY();
                        Log.e("DoubleTap", getScale() + " , " + initScale);
                        //postDelayed每16ms执行一个AutoScaleRunnable方法
                        if (getScale() < SCALE_MID)
                        {
                            ZoomImageView.this.postDelayed(
                                    new AutoScaleRunnable(SCALE_MID, x, y), 16);//如果获得的缩放比例小于SCALE_MID,那么就设置缩放比例为SCALE_MID
                            isAutoScale = true;
                        } else if (getScale() >= SCALE_MID
                                && getScale() < SCALE_MAX)
                        {
                            ZoomImageView.this.postDelayed(
                                    new AutoScaleRunnable(SCALE_MAX, x, y), 16);//如果获得的缩放比例大于SCALE_MID并且小于SCALE_MAX,就设置缩放比例为SCALE_MAX
                            isAutoScale = true;
                        } else
                        {
                            ZoomImageView.this.postDelayed(
                                    new AutoScaleRunnable(initScale, x, y), 16);//同理
                            isAutoScale = true;
                        }

                        return true;
                    }
                });


/*AutoScaleRunnable()*/
private class AutoScaleRunnable implements Runnable
    {
        static final float BIGGER = 1.07f;
        static final float SMALLER = 0.93f;
        private float mTargetScale;
        private float tmpScale;

        /**
         * 缩放的中心
         */
        private float x;
        private float y;

        /**
         * 传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小
         * 
         * @param targetScale
         */
        public AutoScaleRunnable(float targetScale, float x, float y)
        {
            this.mTargetScale = targetScale;
            this.x = x;
            this.y = y;
            if (getScale() < mTargetScale)
            {
                tmpScale = BIGGER;
            } else
            {
                tmpScale = SMALLER;
            }

        }

        @Override
        public void run()
        {
            // 进行缩放
            mScaleMatrix.postScale(tmpScale, tmpScale, x, y);
            checkBorderAndCenterWhenScale();//如果不用,图片宽高大于屏幕时,图片与屏幕间出现白边;图片小于屏幕,但是不居中。
            setImageMatrix(mScaleMatrix);

            final float currentScale = getScale();
            // 如果值在合法范围内,继续缩放
            if (((tmpScale > 1f) && (currentScale < mTargetScale))
                    || ((tmpScale < 1f) && (mTargetScale < currentScale)))
            {
                Log.d("合理范围",currentScale+" "+mTargetScale+" "+tmpScale);
                ZoomImageView.this.postDelayed(this, 16);
            } else
            // 设置为目标的缩放比例
            {
                Log.d("目标范围","目标范围"+currentScale+" "+mTargetScale);
                final float deltaScale = mTargetScale / currentScale;
                Log.d("deltaScale",deltaScale+" ");
                mScaleMatrix.postScale(deltaScale, deltaScale, x, y);
                checkBorderAndCenterWhenScale();
                setImageMatrix(mScaleMatrix);
                isAutoScale = false;
            }
        }
    }

new SimpleOnGestureListener()方法处理单击退出,这里用了EventBus,有关EventBus的使用可以搜索看一下,有handler的效果。在显示这个图片的Activity中的代码如下:

public class PictureShowActivity extends Activity {
    private ImageView imageView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_picture);
        EventBus.getDefault().register(this);
        ....
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onUserEvent(ZoomBus zoomBus) {  //自己定义个ZoomBuS实体,可以是空实体
        Log.d("zoomBus",zoomBus+"");
       finish();
    }
}

public boolean onDoubleTap(MotionEvent e)处理的是双击缩放,双击时首先判断当前缩放比例getScale(),然后拿当前缩放比例和initScale、SCALE_MID、SCALE_MAX进行比较以定出自动缩放的缩放比例,然后调用AutoScaleRunnable()方法,注意AutoScaleRunnable()方法进行了边界检测checkBorderAndCenterWhenScale()

手动缩放代码:

@SuppressLint("NewApi")
    @Override
    public boolean onScale(ScaleGestureDetector detector)
    {
        Log.d("scale","scale");
        float scale = getScale();//当前已经缩放的缩放比例
        float scaleFactor = detector.getScaleFactor();//通过手势检测检测到的缩放比例

        if (getDrawable() == null)
            return true;


        /**
         * 缩放的范围控制
         */
        if ((scale < SCALE_MAX && scaleFactor > 1.0f)
                || (scale > initScale && scaleFactor < 1.0f))
        {
            /**
             * 最大值最小值判断
             */
            if (scaleFactor * scale < initScale)
            {
                scaleFactor = initScale / scale;
            }
            if (scaleFactor * scale > SCALE_MAX)
            {
                scaleFactor = SCALE_MAX / scale;
            }
            /**
             * 设置缩放比例
             */
            mScaleMatrix.postScale(scaleFactor, scaleFactor,
                    detector.getFocusX(), detector.getFocusY());
            checkBorderAndCenterWhenScale();
            setImageMatrix(mScaleMatrix);
        }
        return true;
    }

具体流程如下:
这里写图片描述

移动代码:(在onTouch方法里面)

        float x = 0, y = 0;
        // 拿到触摸点的个数
        final int pointerCount = event.getPointerCount();
        // 得到多个触摸点的x与y均值
        for (int i = 0; i < pointerCount; i++)
        {
            x += event.getX(i);
            y += event.getY(i);
        }
        x = x / pointerCount;
        y = y / pointerCount;

        /**
         * 每当触摸点发生变化时,重置mLasX , mLastY
         */
        if (pointerCount != lastPointerCount)
        {
            isCanDrag = false;
            mLastX = x;
            mLastY = y;
        }

        lastPointerCount = pointerCount;
        RectF rectF = getMatrixRectF();
        switch (event.getAction())
        {
//      case MotionEvent.ACTION_DOWN:
//          if (rectF.width() > getWidth() || rectF.height() > getHeight())
//          {
//              getParent().requestDisallowInterceptTouchEvent(true);
//          }
//          break;

        case MotionEvent.ACTION_MOVE:
//          if (rectF.width() > getWidth() || rectF.height() > getHeight())
//          {
//              getParent().requestDisallowInterceptTouchEvent(true);
//          }
            Log.e(TAG, "ACTION_MOVE");
            float dx = x - mLastX;
            float dy = y - mLastY;

            if (!isCanDrag)
            {
                isCanDrag = isCanDrag(dx, dy);
            }
            if (isCanDrag)
            {
                if (getDrawable() != null)
                {
                    // if (getMatrixRectF().left == 0 && dx > 0)
                    // {
                    // getParent().requestDisallowInterceptTouchEvent(false);
                    // }
                    //
                    // if (getMatrixRectF().right == getWidth() && dx < 0)
                    // {
                    // getParent().requestDisallowInterceptTouchEvent(false);
                    // }
                    isCheckLeftAndRight = isCheckTopAndBottom = true;
                    // 如果宽度小于屏幕宽度,则禁止左右移动
                    if (rectF.width() < getWidth())
                    {
                        dx = 0;
                        isCheckLeftAndRight = false;
                    }
                    // 如果高度小雨屏幕高度,则禁止上下移动
                    if (rectF.height() < getHeight())
                    {
                        dy = 0;
                        isCheckTopAndBottom = false;
                    }


                    mScaleMatrix.postTranslate(dx, dy);
                    checkMatrixBounds();
                    setImageMatrix(mScaleMatrix);
                }
            }
            mLastX = x;
            mLastY = y;
            break;

        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            Log.e(TAG, "ACTION_UP");
            lastPointerCount = 0;
            break;
        }
        return true;

首先我们拿到触摸点的数量,然后求出多个触摸点的平均值,设置给我们的mLastX , mLastY , 然后在移动的时候,得到dx ,dy 进行范围检查以后,调用mScaleMatrix.postTranslate进行设置偏移量,设置完成以后,还需要再次校验一下,不能把图片移动的与屏幕边界出现白边,校验完成后,调用setImageMatrix.

完整demo下载
这个其实也有好多库,比如:
http://p.codekk.com/detail/Android/Dreddik/AndroidTouchGallery

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要在 TextureView 上实现双指和单指移动,您可以使用 ScaleGestureDetector 和 GestureDetector 类。以下是一个简单的示例代码,演示如何在 TextureView 上实现这些手势: ```java public class MyActivity extends Activity implements OnGestureListener, OnScaleGestureListener { private TextureView mTextureView; private ScaleGestureDetector mScaleGestureDetector; private GestureDetector mGestureDetector; private float mScaleFactor = 1.0f; private float mPosX = 0.0f; private float mPosY = 0.0f; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化 TextureView、ScaleGestureDetector 和 GestureDetector mTextureView = new TextureView(this); mScaleGestureDetector = new ScaleGestureDetector(this, this); mGestureDetector = new GestureDetector(this, this); // 将 TextureView 添加到布局中 setContentView(mTextureView); } @Override public boolean onTouchEvent(MotionEvent event) { // 将触摸事件交给 ScaleGestureDetector 和 GestureDetector 处理 mScaleGestureDetector.onTouchEvent(event); mGestureDetector.onTouchEvent(event); return true; } @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onSingleTapConfirmed(MotionEvent e) { // 处理单击事件 return true; } @Override public boolean onDoubleTap(MotionEvent e) { // 处理双击事件 return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // 处理拖动事件 if (e2.getPointerCount() == 1) { mPosX -= distanceX; mPosY -= distanceY; updateTextureView(); } return true; } @Override public boolean onScale(ScaleGestureDetector detector) { // 处理事件 mScaleFactor *= detector.getScaleFactor(); mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); updateTextureView(); return true; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { } private void updateTextureView() { // 更新 TextureView 的移动 mTextureView.setScaleX(mScaleFactor); mTextureView.setScaleY(mScaleFactor); mTextureView.setTranslationX(mPosX); mTextureView.setTranslationY(mPosY); } } ``` 在上面的代码中,我们实现了 OnGestureListener 和 OnScaleGestureListener 接口,并在 onCreate 方法中初始化了 TextureView、ScaleGestureDetector 和 GestureDetector 对象。我们还重写了 onTouchEvent 方法,将触摸事件交给 ScaleGestureDetector 和 GestureDetector 处理。最后,我们实现了 OnGestureListener 接口中的方法,处理单指移动事件,实现了 OnScaleGestureListener 接口中的方法,处理双指事件,并添加了一个 updateTextureView 方法,用于更新 TextureView 的移动

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值