Android手指触摸实现文字放大缩小(多点触控)

效果展示:

多点触控讲解

●什么是多点触控

多点触控说白了就是一个以上的手指同时触摸屏幕,比如说你看一张图片的时候想要放大来看,这时你需要用两个手指从屏幕中心向四周扩张滑动,这个也是多点触控的应用。

●多点触控的触摸事件

多点触控的触摸事件的处理与正常的触摸事件一样都是在onTouchEvent方法中处理的,但是这里也有三个不同点:

1、多点触控需要用event.getActionMasked()来获取Action

2、第二根手指及第二根以上的手指触摸屏幕的时候匹配的是MotionEvent.ACTION_POINTER_DOWN,第一根手指触摸时还是匹配MotionEvent.ACTION_DOWN这个没变。

3、除最后一根手指的其他手指离开屏幕的时候匹配的是MotionEvent.ACTION_POINTER_UP,最后一根手离开屏幕时还是匹配MotionEvent.ACTION_UP这个没变。

具体代码如下:

  @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()){
            case MotionEvent.ACTION_DOWN:
                //第一根手指触摸触发
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                //第二根手指及以上触摸触发
                break;
            case MotionEvent.ACTION_MOVE:

                break;
            case MotionEvent.ACTION_POINTER_UP:
                //除最后一根手指其他手指离开时触发
                break;
            case MotionEvent.ACTION_UP:
                //最后一根手指离开时触发
                break;
        }
        return true;
    }

●触摸点的标识

某个触摸点可以通过两种方式来确定:
1.通过id(PointerId)
2.通过索引(PointerIndex)

这里有着对应的几个方法:

//根据PointerId获取索引,传参为PointerId
event.findPointerIndex();
//根据索引获取PointerId,传参为PointerIndex
event.getPointerId();
//获取当前触摸点的索引
event.getActionIndex();
//获取当前所有触摸点的数量
event.getPointerCount();

这里触摸点的id和索引的值有如下的规律:

触摸点的PointerId和索引PointerIndex都是从0开始取值的,但是PointerId的值是一直不变的而PointerIndex的值会随着屏幕上的触摸点的减少或增加而改变,具体入下图所示:

事件 PointerId PointerIndex
依次按下三根手指 三根手指的PointerId依次为0、1、2 三根手指的PointerIndex依次为0、1、2
抬起第二根手指 第一根手指的PointerId为0,第三根手指的PointerId为2 第一根手指的PointerIndex为0,第三根手指的PointerIndex变为1
抬起第一根手指 第三根手指的PointerId为2 第三根手指的PointerIndex变为0

另外我们也可以通过遍历event.getPointerCount()来获取所有的触摸点的信息,如下所示:

for (int i = 0; i < event.getPointerCount(); i++) {
      //根据上面表格展示的规律我们可以知道触摸点的PointerIndex与i是一致的
      //这里我们根据getX和getY的重载方法来获取x和y的值,其中的参数传的是PointerIndex
      float x = event.getX(i);
      float y = event.getY(i);
}

到这里我们也算是基本了解了多点触控了,接下来我们就通过案例的讲解来加深印象。

案例讲解

●逻辑确定

在动手写代码之前我们先来确定一下整个效果的实现逻辑,我们要实现的逻辑是触摸屏幕的两手指向四周扩散是放大,向中间聚拢是缩小,入下图所示:

两手指向中心聚拢
两手指向四周扩散

这里我们需要用来计算的值就是第一次触摸屏幕时两点之间的距离(AB)和滑动后两点之间的距离(A'B'):

图片

而AB的长度的计算我们可以根据勾股定理算出来:

图片

其中:
AC = 绝对值(A.getY - B.getY)
BC = 绝对值(B.getX - A.getX)

计算出滑动前两点的距离(AB)与滑动后两点的距离(A'B')后我们可以用A'B'与AB的比值来获取缩放的值即:
缩放值 = A'B' / AB

获取到缩放值之后我们就可以以当前文字的大小为基准根据缩放值来进行缩放了

●代码实现

确定了逻辑之后接下来我们就用代码来实现
根据效果图我们可以知道,我们要实现的效果是可以将文字放大缩小的,就类似于图片的放大缩小,这里为了方便我就直接继承了TextView来实现的,另外我们还需要记录初始的文字大小作为放大缩小的基准

public class ScaleTextView extends TextView {
    private float mTextSize;
    public ScaleTextView(Context context) {
        this(context,null);
    }
    public ScaleTextView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }
    public ScaleTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mTextSize = getTextSize();
    }
}

接下来我们先在onTouchEvent中计算出初始两触摸点之间的距离即:AB

private float mFirstTouchX;
private float mFirstTouchY;
private float mSecondTouchX;
private float mSecondTouchY;
private double firstPointerLength;
@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()){
            case MotionEvent.ACTION_DOWN:
                //获取第一个点(A点)的位置
                mFirstTouchX = event.getX();
                mFirstTouchY = event.getY();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if(event.getActionIndex() == 1){
                    //获取第二个点(B点)的位置
                    mSecondTouchX = event.getX(1);
                    mSecondTouchY = event.getY(1);

                    //根据两点的位置获取两个触摸点之间的距离(AB)
                    float firstLengthX = Math.abs(mFirstTouchX - mSecondTouchX);
                    float firstLengthY = Math.abs(mFirstTouchY - mSecondTouchY);
                    firstPointerLength = Math.sqrt(Math.pow(firstLengthX, 2) + Math.pow(firstLengthY, 2));
                }
                break;
        }
        return true;
    }

然后我们需要在滑动的时候获取滑动后的距离(A'B')

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()){
            case MotionEvent.ACTION_DOWN:
                //获取第一个点(A点)的位置
                mFirstTouchX = event.getX();
                mFirstTouchY = event.getY();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if(event.getActionIndex() == 1){
                    //获取第二个点(B点)的位置
                    mSecondTouchX = event.getX(1);
                    mSecondTouchY = event.getY(1);
                    //根据两点的位置获取两个触摸点之间的距离(AB)
                    float firstLengthX = Math.abs(mFirstTouchX - mSecondTouchX);
                    float firstLengthY = Math.abs(mFirstTouchY - mSecondTouchY);
                    firstPointerLength = Math.sqrt(Math.pow(firstLengthX, 2) + Math.pow(firstLengthY, 2));
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if(event.getPointerCount() >= 2){
                    //获取第一个点(A‘)的位置
                    float firstX = event.getX(0);
                    float firstY = event.getY(0);

                    //获取第二个点(B‘)的位置
                    float secondX = event.getX(1);
                    float secondY = event.getY(1);

                    //计算两点之间的距离(A'B')
                    float secondLengthX = Math.abs(firstX - secondX);
                    float secondLengthY = Math.abs(firstY - secondY);
                    double secondPointerLength = Math.sqrt(Math.pow(secondLengthX, 2) + Math.pow(secondLengthY, 2));
                }
                break;
        }
        return true;
    }

然后我们可以根据计算出来的AB与A'B'的距离来计算缩放值了,代码如下:


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()){
            case MotionEvent.ACTION_DOWN:
                //获取第一个点(A点)的位置
                mFirstTouchX = event.getX();
                mFirstTouchY = event.getY();
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if(event.getActionIndex() == 1){
                    //获取第二个点(B点)的位置
                    mSecondTouchX = event.getX(1);
                    mSecondTouchY = event.getY(1);
                    //根据两点的位置获取两个触摸点之间的距离(AB)
                    float firstLengthX = Math.abs(mFirstTouchX - mSecondTouchX);
                    float firstLengthY = Math.abs(mFirstTouchY - mSecondTouchY);
                    firstPointerLength = Math.sqrt(Math.pow(firstLengthX, 2) + Math.pow(firstLengthY, 2));
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if(event.getPointerCount() >= 2){
                    //获取第一个点(A‘)的位置
                    float firstX = event.getX(0);
                    float firstY = event.getY(0);

                    //获取第二个点(B‘)的位置
                    float secondX = event.getX(1);
                    float secondY = event.getY(1);

                    //计算两点之间的距离(A'B')
                    float secondLengthX = Math.abs(firstX - secondX);
                    float secondLengthY = Math.abs(firstY - secondY);
                    double secondPointerLength = Math.sqrt(Math.pow(secondLengthX, 2) + Math.pow(secondLengthY, 2));
                    //缩放文字
                    zoom(secondPointerLength);
                }
                break;
        }
        return true;
    }

    private void zoom(double secondPointerLength) {
        double scaleRate = secondPointerLength / firstPointerLength;
        float textSize = (float) (mTextSize * scaleRate);
        //这里也进行像素值赋值
        setTextSize(TypedValue.COMPLEX_UNIT_PX,textSize);
    }

最后也不要忘了在MotionEvent.ACTION_UP中保存当前文字的大小,否则下次缩放的时候文字会先一下子变小

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()){
            case MotionEvent.ACTION_UP:
                //手指抬起的时候记录当前的文字大小
                mTextSize = getTextSize();
                break;
        }
        return true;
    }

源码地址:

https://gitee.com/itfitness/scale-text-view

到这里就结束啦。
该文章来自于:
itfitness : https://www.jianshu.com/p/b27e04b787c3

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容