这个自定义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.
启用文本的子像素定位。
以上两个标志位官方文档有介绍,要一起配合使用。这里介绍一下如果有多个标志位设置可以通过按位或|
操作即可。
- LINEAR_TEXT_FLAG:Paint flag that enables smooth linear scaling 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的实现方式,代码难度并不大,但还是学到了一些知识点,希望其他同学也能有所收获。
网友评论