项目地址

最近项目上有个需求,需要在一块区域中显示文字,这块区域可以拖动,也可以通过拖拽右下角来改变大小,里面的文字大小要根据区域的大小进行自适应。刚开始觉得这个需求不难,只需要一个TextView就能实现。

后来发现虽然使用TextView可以很容易实现拖动与缩放的功能,但是文字大小不会改变。在求助github的时候发现了AutoFitTextView控件,参考https://github.com/AndroidDeveloperLB/AutoFitTextView,但是使用的时候,每一次需要根据区域的大小重新创建一个TextView。想想这样对性能消耗太大,不如自己编写一个。

程序常量与变量

具体含义见注释

	//事件类型, 0代表移动, 1代表缩放
private static final int MOVEVIEW = 0;
private static final int DRAGVIEW = 1; //背景画笔
private Paint backPaint;
//文字画笔
private TextPaint textPaint;
//背景区域
private Rect backRect;
//文字测量出的区域
private Rect textMeasureRect;
//事件类型
private int eventType;
//手指点击的位置
private int lastX, lastY;
//背景与文字的长宽
private int rectWidth, rectHeight, textWidth, textHeight;
//字体大小
private int textSize; private String text = "这是一个测试程序abcdefg!@#$%^&";

构造函数

因为是将文字绘制在View上,所以在构造函数里生成了一个背景画笔和文字画笔,同时生成背景的矩阵。

public MyDrawView(Context context, AttributeSet attrs) {
super(context, attrs); backPaint = new Paint(); //设置一个笔刷大小是3的黄色的画笔
backPaint.setColor(Color.YELLOW);
backPaint.setStrokeJoin(Paint.Join.ROUND);
backPaint.setStrokeCap(Paint.Cap.ROUND);
backPaint.setStrokeWidth(3); textPaint = new TextPaint();
textPaint.setColor(Color.BLUE);
textPaint.setTextSize(100); backRect = new Rect(100, 100, 400, 200);
textMeasureRect = new Rect();
}

Touch事件

在TouchEvent中,手指按下时,根据手指的点击位置,判断是拖动事件还是缩放事件,并设置eventType。

根据eventType,在手指拖动时,执行移动或者缩放的操作。执行完之后,别忘了invalidate,重新绘制区域。

	@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
Log.d(TAG, "onTouchEvent: " + event.getX() + " " + backRect.right + " " + event.getY() + " " +
backRect.bottom);
if ((Math.abs(event.getX() - backRect.right)) < 100
&& ((Math.abs(event.getY() - backRect.bottom) < 100))) {
eventType = DRAGVIEW;
} else {
eventType = MOVEVIEW;
}
Log.d(TAG, "onTouchEvent: " + eventType);
break;
case MotionEvent.ACTION_MOVE:
switch (eventType) {
case MOVEVIEW:
int dx = (int) (event.getRawX() - lastX);
int dy = (int) (event.getRawY() - lastY);
backRect.offset(dx, dy);
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
break;
case DRAGVIEW:
Log.d(TAG, "onTouchEvent: " + backRect.toShortString());
if (event.getX() > backRect.left + 100 && event.getY() > backRect.top + 100) {
backRect.set(backRect.left, backRect.top, (int) event.getX(), (int) event.getY());
}
break;
}
break;
case MotionEvent.ACTION_UP:
break;
}
invalidate(); //重新绘制区域
return true;
}

onDraw在界面上绘制

在onDraw时间里,需要调用adjustTextSize函数计算文字大小,得到文字大小后,需要进行文字绘制。在绘制的时候,通过StaticLayout对文字进行自动换行操作,将文字绘制在背景的Rect上。

参考Android自定义View使用canvas绘制文字实现居中、自动换行Android使用StaticLayout实现文本绘制自动换行

	@Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(backRect, backPaint); adjustTextSize(); //文字自动换行
StaticLayout layout = new StaticLayout(text, textPaint, backRect.width(), Layout.Alignment.ALIGN_NORMAL, 1.0F,
0.0F, true);
canvas.save();
textPaint.setTextAlign(Paint.Align.LEFT);
//文字的位置
canvas.translate(backRect.left, backRect.top);
layout.draw(canvas);
canvas.restore(); super.onDraw(canvas);
}

计算文字大小

首先获取背景的长度和宽度,最开始将文字大小设为行高的0.9倍,然后通过TextPaint.getTextBounds,获取文字的长宽,如果文字的面积大于背景矩阵面积*0.7(因为行与行之间有间隙,假设文字占据了行高的0.7倍),则减小文字的大小,一直到文字大小稍小于背景面积*0.7为止。如此便可获得合适的文字大小。

	/**
* 确定文字大小
*/
private void adjustTextSize() {
rectWidth = backRect.right - backRect.left;
rectHeight = backRect.bottom - backRect.top;
textSize = (int) (rectHeight * 0.9);
textPaint.setTextSize((float) (textSize));
textPaint.getTextBounds(text, 0, text.length(), textMeasureRect);
textWidth = textMeasureRect.width();
textHeight = textMeasureRect.height();
//0.7为文字占据行高的系数
while (rectWidth * rectHeight * 0.7 < textWidth * textHeight) {
textSize = textSize -3;
textPaint.setTextSize((float) (textSize));
textPaint.getTextBounds(text, 0, text.length(), textMeasureRect);
textWidth = textMeasureRect.width();
textHeight = textMeasureRect.height();
}
textPaint.getTextBounds(text, 0, text.length(), textMeasureRect);
Log.d(TAG, "adjustTextSize: " + textMeasureRect.width() + " " + textMeasureRect.height());
}

后记:经过这个项目,知道了在解决问题前,应该先好好的分析解决思路。第三方控件并不是万能的,只有深刻的理解了Android上控件的机理,才能准确的找到解决问题的思路。

最新文章

  1. ArcGIS10.2.2 Desktop直接连接数据库的具体步骤
  2. *HDU1969 二分
  3. 总结-computer
  4. shell选择语句
  5. asp.net 之 购物车
  6. [C++]项目中的代码注释规范(整理)
  7. zt:如何快速赚取人生第一个100万?
  8. 【HDOJ】4982 Goffi and Squary Partition
  9. 【POJ】3264 Balanced Lineup ——线段树 区间最值
  10. 重新想象 Windows 8 Store Apps (9) - 控件之 ScrollViewer 基础
  11. Exchange Server 2007的即将生命周期,您的计划是?
  12. 侯捷STL学习(一)
  13. kaldi chain模型的序列鉴别性训练代码分析
  14. linux 常见技巧
  15. @RequestBody ajax 415 400
  16. CMake--变量
  17. 2018-2019-2 20175227张雪莹 《Java程序设计》 实验一 Java开发环境的熟悉
  18. tomcat加载web项目报错:bad major version at offset=6
  19. ifstream 和 ofstream 用法。
  20. 面向对象三大特性一一封装(encapsulation)

热门文章

  1. python+selenium自动化软件测试(第3章):unittes
  2. ALGO-22_蓝桥杯_算法训练_装箱问题(DP)
  3. 【java】之类加载机制
  4. Boost--variant (C++中的union)
  5. Hadoop 新增删除节点
  6. spring4.0之一:简介
  7. Ajax的兼容及Ajax的缓存问题
  8. Scrapy学习篇(六)之Selector选择器
  9. [UE4]小地图UI放在哪里创建合适?
  10. [UE4]RetainerBox,控制UI更新频率,把渲染后的UI当成Texture