首页必威体育betway › 图形操作序列 —(1)手势缩放图片效率

图形操作序列 —(1)手势缩放图片效率

概述

类型开发中,我们APP开发一般都会用到上传图片,比如是上传了团结的生活照,然后在某个界面处查看上传的图样,这时候一般在这多少个查看详情的界面,会有手势放大收缩效能,手势进行旋转效用,双击放大图片等等。

偏偏,我原先也有亟待以此要求的时候,而且特别提议了要用手势进行图片的选项效能。

于是自己翻看了BiliBili的开源库:

Boxing

image

利用了那些Demo后发觉其中有手势控制图片大小,手势控制图片旋转等功能,看了代码后自己意识BiliBili那几个demo中也是用了第三方的库:

RotatePhotoView

image

image

我们得以观察介绍:在PhotoView的基础上添加了通过二个指头来旋转图片的效能,所以这些库又是用了其它的第三方库:

PhotoView

俺们得以见见这一个PhotoView的库有一万三个star了。表明依旧很正确的。

就此经过本次。我就来看PhotoView怎么着举办落实那么多职能。


正题

世家在看正文以前倘使对于Matrix不是很精通的,可以先看看:
android matrix
最全方法详解与进阶(完整篇)

Android
Matrix

Float中的这一个常量
Infinity、NaN

自然是想一贯拿着PhotoView
的源码,贴上源码分析一个个切实的功能,不过因为源码是考虑到众多功力,所以有无数代码量,而且太多看着很乱,所以我的方案是一直自己写个demo,然后依据我们要上课的法力,仿照PhotoView的源码,在大团结一个个现实的机能demo分别实现。所以本文我先来促成实现基于手势来促成图片的缩放效率:

1.添加图形布局

PhotoView是继承了ImageView,然后间接在layout中使用<PhotoView/>,为了更便于的上课,我就直接或者拔取<ImageView/>,然后让我们看看是哪些对ImageView做拍卖实现相应的效益。

先添加我们要的demo布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.dialog.photoviewdemo.MainActivity">

    <ImageView
        android:id="@+id/photo_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/black"
        />

</LinearLayout>

2. 对图片设置手势监听

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

    //对我们的ImageView设置相应的一张图片
    ivPhoto = (ImageView) findViewById(R.id.photo_view);
    drawable = ContextCompat.getDrawable(this, R.mipmap.ic_launcher);
    ivPhoto.setImageDrawable(drawable);

    //对我们的ImageView设置触摸事件监听,并且把监听交给了GestureDetector.
    ivPhoto.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
           return scaleGestureDetector.onTouchEvent(event);
        }
    }); 

    //GestureDetector的实例生成
    scaleGestureDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.OnScaleGestureListener() {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float scaleFactor = detector.getScaleFactor();
            float focusX = detector.getFocusX();
            float focusY = detector.getFocusY();
            if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor)) {
                return false;
            }

            mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
            if(checkMatrixBounds()) {
                ivPhoto.setImageMatrix(getDrawMatrix());
            }

            return true;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            return true;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
        }
    });
}    

基于上边的代码我们同样样来分析:

1.GestureDetector和ScaleGestureDetector

当用户触摸屏幕的时候,会时有发生许多手势,例如down,up,scroll,filing等等。
诚如景象下,我们清楚View类有个View.OnTouchListener内部接口,通过重写他的onTouch(View
v, Motion伊芙(Eve)nt
event)方法,大家可以拍卖部分touch事件,不过这一个模式太过简单,固然急需处理部分复杂的手势,用这么些接口就会很劳累(因为大家要协调依照用户触摸的轨道去判断是何等手势)。
Android
sdk给大家提供了GestureDetector(Gesture:手势Detector:识别)类,通过这一个类我们可以识别很多的手势,主如果因此他的onTouch伊芙nt(event)方法成功了不同手势的辨认。尽管她能鉴别手势,但是不同的手势要怎么处理,应该是提供给程序员实现的。
切实现实可以看这篇作品,写的很详细:用户手势检测-GestureDetector使用详解

而这边我们因为做的功用是透过手势来缩放图片,所以大家就要监听二个指头缩放动作,所以我们应用的是ScaleGestureDetector

ScaleGestureDetector介绍:</br>用于拍卖缩放的工具类,用法与GestureDetector类似,都是经过onTouch伊芙(Eve)nt()关联相应的Motion伊芙(Eve)nt的。使用该类时,用户需要传入一个完好无损的接连不停地motion事件(包含ACTION_DOWN,ACTION_MOVE和ACTION_UP事件)。

俺们看下面的代码就会发觉ScaleGestureDetector有三个主意:

@Override
public boolean onScale(ScaleGestureDetector detector) {
    return true;
}

@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
    return true;
}

@Override
public void onScaleEnd(ScaleGestureDetector detector) {}

onScaleBegin:缩放先导会履行的章程,可是我们发现这些办法需要再次来到一个Boolean值,那一个值决定是否处理后继的缩放事件,重回false时,不会实施onScale()

onScaleEnd:缩放停止执行

onScale:缩放时候实施的法门,用来做具体的逻辑处理。

大家现实来看望onScale方法:

@Override
public boolean onScale(ScaleGestureDetector detector) {
    return true;
}

咱俩得以见见此间是回到Boolean值,这这里再次来到true和false有怎样分别呢。

float scaleFactor = detector.getScaleFactor();

大家得以经过那么些措施获得到缩放因子,缩放因子会按照你的手势的变大会越来越大,虽然你回到了true,这就认证本次的缩放行为就已经完结了,假如你回去了false,这就证实没有完结,然后缩放因子越来越大。

public boolean onScale(ScaleGestureDetector detector) {  
    if(detector.getScaleFactor()< 2){  
        return false;  
    }  
    return true;  
}  

image

咱俩可以看出,我们设置了超过2才回来true,(前提二个指头是做放大手势)那么缩放因子就会直接变大到2,才会以为这一次缩放行为终止了,就再一次从1方始了。

(PS:假使二个手指做裁减的手势,那么那一个缩放因子就会低于1,假设回去false,那么就会从1起来越来越小。)

2.图纸最先化突显状态

假定我们前日的ImageView设置的是全屏,我们有个小图片,ImageView设置了图片后是这么的:

image

我们发现默认是在左上角,而且因为大家的ImageView设置的是全屏,而图片又特别小,这样的起初显示模式很不友好。
于是我们要做如下操作:
<1>把图片居中展现。
<2>图片和ImageView相适应(咱们这边是把图纸适当的拓宽,来适应这样大的ImageView.)

从而也就是大家地点提到过的代码:

drawableWidth = drawable.getIntrinsicWidth();
drawableHeight = drawable.getIntrinsicHeight();

viewWidth = ivPhoto.getWidth() - ivPhoto.getPaddingLeft() - ivPhoto.getPaddingRight();
viewHeight = ivPhoto.getHeight() - ivPhoto.getPaddingLeft() - ivPhoto.getPaddingRight();
RectF mTempScr = new RectF(0, 0, drawableWidth, drawableHeight);
RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight);
mBaseMatrix.setRectToRect(mTempScr, mTempDst, Matrix.ScaleToFit.CENTER);
mDrawableMatrix.set(mBaseMatrix);
ivPhoto.setImageMatrix(mDrawableMatrix);

收获图片的忠实宽高和ImageView用来展现图片的宽高我就不多说了。重点是setRectToRect方法:

public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf)

将rect变换成rect,通过stf参数来支配。

ScaleToFit 有如下几个值:
FILL: 可能会更换矩形的长宽比,保证变换和目的矩阵长宽一致。
START:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一头和对象矩形重叠。左上对齐。
CENTER:
保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一头和目的矩形重叠。
END:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一头和目的矩形重叠。右下对齐。

这边运用Google的api demo的图形作为例子:

image

咱俩很肯定发现,那些黑色的小球的生成不就是大家想要的变化么,并且我们是要从中,所以用的是Matrix.ScaleToFit.CENTER

我们看下大家最后的效能:

image

3.图纸实时手势缩放

咱俩眼前早已知道了。手势变化的时候会接触onScale措施,所以咱们假使把图片的切切实实的松手缩小的逻辑放在onScale内部即可。

@Override
public boolean onScale(ScaleGestureDetector detector) {
    //缩放因子
    float scaleFactor = detector.getScaleFactor();
    //返回组成该手势的两个触点的中点在组件上的x和y轴坐标,单位为像素。
    float focusX = detector.getFocusX();
    float focusY = detector.getFocusY();
    //如果为nan或者无强大,则无效
    if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor)) {
        return false;
    }
    //进行缩放,传入x轴缩放比例,y轴缩放比例,缩放中心点的x和y值
    mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
    if(checkMatrixBounds()) {
        ivPhoto.setImageMatrix(getDrawMatrix());
    }

    return true;
}

世家应该看到了自身这边有个checkMatrixBounds艺术,本来其实唯有的缩放就是先postScale下一场在间接setImageMatrix就足以了。

mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
ivPhoto.setImageMatrix(getDrawMatrix());

不过这么有什么欠好的地点吧。我来具体跟我们说下:

  • 缩放跟手势的二个触点的中坚关于,而且图片会随着那一个样子移动

image

例如自己是二个红点分别是自身的指尖,然后不停的紧缩图片动作,图片不仅变小,而且会趁机这多少个样子做活动。放大则相反。这不是我们想要的,我们想要的是均等是做缩放,同时,图片还在中等。

既然大家掌握了图片在做裁减放大的同时还在运动,这我们就做相应的反方向的运动处理不就好了

咱俩分为二种场地:

1— 图片在缩放过程中,宽或者高没有超过ImageView的宽或者高:

假定图片再缩放过程中没抢先ImageView的轻重缓急。大家只需要让图片一向居中具体即可。所以相比较简单:

image

一经算出我们在前边第二个大步里面的起始化后的图片的初叶状态后(即和ImageView相适应并且居中),相应的图形的矩阵的宽和高是不是超越ImageView。如若没有抢先,大家得以看看我们期望的图片放大和紧缩都是指望在正中间的职务,可是现在改成了青色的地点,我们只需要把黑色的地点活动到咖啡色的地点就行。

以Y轴为例(X轴同样处理):

image

观望离开是(实际图片的Top值) - (2分之一的ImageView的莫大) +
(2分之一的实在图片中度),因为是往上活动,所以Y轴实际上是要减弱值的,所以末了我们假设让实际的图形减去相应的距离值即可。

  • 实际图片的TOP值(先取得相应的实际图片的矩阵Rect,在赢得top属性):

    private RectF getDisplayRect(Matrix matrix) {
      Drawable d = drawable;
      if (d != null) {
          mDisplayRect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
          matrix.mapRect(mDisplayRect);
          return mDisplayRect;
      }
      return null;
    }
    
  • ImageView的高度:

viewHeight = ivPhoto.getHeight() - ivPhoto.getPaddingLeft() - ivPhoto.getPaddingRight();
  • 事实上变化后的图形的中度(rect为地方得到的实在图片的Rect):
    final float height = rect.height(), width = rect.width();

故而我们那边只需要:

private boolean checkMatrixBounds() {
    RectF rect = getDisplayRect(getDrawMatrix());
    if (rect == null) {
        return false;
    }

    final float height = rect.height(), width = rect.width();
    float deltaX = 0, deltaY = 0;

    if (height <= viewHeight) {
        deltaY = (viewHeight - height) / 2 - rect.top;
    }

    if (width <= viewWidth) {
        deltaX = (viewWidth - width) / 2 - rect.left;
    }

     mSuppMatrix.postTranslate(deltaX, deltaY);
    return true;
}

2— 图片在缩放过程中,宽或者高超越ImageView的宽或者高:

以此时候大家就丰富简单的在主题岗位就足以了。因为这时不可以反而不让他在主题岗位,为啥????大家前天的图片是一个安卓机器人,比如自己现在要拓宽它的图纸查看它的右眼,大家在右上角用手机不挺放大。变成这样:

})ZABEPQ(1V_}6461ME{O{N.png

这会儿就说了。这自己什么都不处理,放大这边就是其一效果啊。说的不易的确这样,但是比如现在曾经放大成这一个样子了。我压缩它,然而本人不是从右上角来开展裁减,而是在右边举办压缩,我们理解大家不做拍卖,这时候裁减的时候是按我们手势的职务展开,所以头像在收缩时候先是往右侧方向,然后当小于ImageView的可观时候,又陡然居中,效果很不佳。

于是大家以此例子里面处理形式是:倘若幅度都超越ImageView并且图片的左侧界还没出现在ImageView中的时候,先按照自己原来的方法缩短,当图片的左边界出现在了ImageView的限定内了,让它逐渐往左侧移动(也就是ImageView的增长率

Rect.right的偏离),这时候就会很和谐。最后宽度小于ImageView的时候居于中间。

PS:还有一种正好反过来。我们松开的图形是左眼!!(这时候移动的距离是
-rect.left)

由此最后变成这样:

private boolean checkMatrixBounds() {
    RectF rect = getDisplayRect(getDrawMatrix());
    if (rect == null) {
        return false;
    }

    final float height = rect.height(), width = rect.width();
    float deltaX = 0, deltaY = 0;

    if (height <= viewHeight) {
        deltaY = (viewHeight - height) / 2 - rect.top;
    } else if (rect.top > 0) {
        deltaY = -rect.top;
    } else if (rect.bottom < viewHeight) {
        deltaY = viewHeight - rect.bottom;
    }


    if (width <= viewWidth) {
        deltaX = (viewWidth - width) / 2 - rect.left;
    } else if (rect.left > 0) {
        deltaX = -rect.left;
    } else if (rect.right < viewWidth) {
        deltaX = viewWidth - rect.right;
    }

    mSuppMatrix.postTranslate(deltaX, deltaY);
    return true;
}

结尾

抑或老样子,希望大家不用吐槽。有题目留言哈哈。。O(∩_∩)O哈哈~

附上Demo地址:ScaleImageVewDemo

转载本站文章请注明出处:bway883.com https://www.piworx.com/?p=3989

上一篇:

下一篇:

相关文章

网站地图xml地图