美文网首页
PinchZoomTextView的源码解读

PinchZoomTextView的源码解读

作者: Elder | 来源:发表于2016-12-05 20:20 被阅读109次

这个自定义TextView效果非常简单,通过手势来缩放内部的文字大小。GitHub的地址点击
下面是我实现的代码,几乎没有区别只是增加了一些注释:

PinchZoomTextView:

public class PinchZoomTextView extends TextView {
    /**
     * 设置两个手指之间每移动200px,TextView将缩放一个比例
     */
    private static final float STEP = 200;
    /**
     * 缩放后与原始值的比例
     */
    private float ratio = 1.0f;
    /**
     * 手指刚触碰时的基础距离
     */
    private int baseDistance;
    /**
     * 手势刚开始时与原始字体比较的比例
     */
    private float baseRatio;
    /**
     * 是否允许缩放,默认允许
     */
    private boolean zoomEnabled = true;

    public PinchZoomTextView(Context context) {
        this(context, null);
    }

    public PinchZoomTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    /**
     * 处理手势具体缩放的方法
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            // 触摸事件开始设置Paint标志属性平滑缩放字体
            case MotionEvent.ACTION_DOWN:
                setPaintFlags(getPaintFlags() | (Paint.LINEAR_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG));
                break;
            // 触摸事件完成或取消应取消Paint的FLAG属性
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                setPaintFlags(getPaintFlags() & ~(Paint.LINEAR_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG));
                break;
        }
        // 必须是两个手指操作的手势
        if (zoomEnabled && event.getPointerCount() == 2) {
            int action = event.getAction();
            int distance = getDistance(event);
            // event.getAction() & MotionEvent.ACTION_MASK)就可以处理处理多点触摸的ACTION_POINTER_DOWN和ACTION_POINTER_UP事件
            int conversionAction = action & MotionEvent.ACTION_MASK;
            if (conversionAction == MotionEvent.ACTION_POINTER_DOWN) {
                // 得到每次手势开始时各数据的起始值
                baseDistance = distance;
                baseRatio = ratio;
            } else {
                // 得到缩放的增量值
                float delta = (distance - baseDistance) / STEP;
                // 换算成缩放比例
                float multi = (float) Math.pow(2, delta);
                ratio = Math.min(1024.0f, Math.max(0.1f, baseRatio * multi));
                setTextSize(ratio + 13);
            }
        }
        return true;
    }

    /**
     * 计算两根手指滑动距离的方法
     * @param event
     * @return
     */
    public int getDistance(MotionEvent event) {
        // 0,1分别代表两个触摸点
        int dx = (int) (event.getX(0) - event.getX(1));
        int dy = (int) (event.getY(0) - event.getY(1));
        return (int) Math.sqrt(dx * dx + dy * dy);
    }
    
    public boolean isZoomEnabled() {
        return zoomEnabled;
    }

    public void setZoomEnabled(boolean zoomEnabled) {
        this.zoomEnabled = zoomEnabled;
    }
}

通过注释同学们应该都能看懂语义,这里还有一些需要补充的知识:

  • Paint的FLAG:
    int getFlags()
    void setFlags(int flags)
    获取与设置Paint的一些属性flag,譬如抗锯齿、防抖等。这里介绍我们用到的flag。
    • LINEAR_TEXT_FLAG:Paint flag that enables smooth linear scaling of text.
      文本平滑性缩放。
    • SUBPIXEL_TEXT_FLAG:Paint flag that enables subpixel positioning of text.
      启用文本的子像素定位。
      以上两个标志位官方文档有介绍,要一起配合使用。这里介绍一下如果有多个标志位设置可以通过按位或|操作即可。

原理即是,Flag常量换算成二进制数以后,每位上的1即代表一个Flag标志。所以多个标志位进行按位与|的时候即可将多个标志位合并为一个值。感概下设计的精妙啊。

  • event.getAction() & MotionEvent.ACTION_MASK 进行位运算操作后,就可以处理处理多点触摸的ACTION_POINTER_DOWN和ACTION_POINTER_UP事件。

MainActivity:

public class MainActivity extends AppCompatActivity {
    private PinchZoomTextView mZoomTextView;
    private Button mButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mZoomTextView = (PinchZoomTextView) findViewById(R.id.pztv);
        mButton = (Button) findViewById(R.id.btn);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                boolean isZoomState = !mZoomTextView.isZoomEnabled();
                mZoomTextView.setZoomEnabled(isZoomState);
                mButton.setText(isZoomState ? getString(R.string.zoom_enabled) : getString(R.string.zoom_disabled));
            }
        });
    }
}

以上就是这个自定义View的实现方式,代码难度并不大,但还是学到了一些知识点,希望其他同学也能有所收获。

相关文章

网友评论

      本文标题:PinchZoomTextView的源码解读

      本文链接:https://www.haomeiwen.com/subject/kuclmttx.html