美文网首页
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