一.前言
一个项目中有一个需求:很多个TextView按照从左到右,从上到下依次排列,如下图所示:
这种效果叫做流式布局,在网上查了一下流布局,发现基本都是通过继承ViewGroup来实现的,所以想通过自定义View来实现一个流式布局效果的TextView。
二.要点
观察我们的效果图,发现有如下要点:
1. 流布局的实现
2. 添加点击事件
3. 点击文本块后有水波纹效果
三.实现
1.流布局的实现:
自定义一个View,名为FlowTextView,继承自View。首先需要计算View的大小,宽度很简单,这里就不多过描述,麻烦的是高度的计算。看效果图,不难发现每块文本的高度是一样的,所以View的高度应该是:
height = textHeight * row + spacingVertical * (row-1) + paddingTop + paddingBottom
其中:
row : 行数
textHeight:文本高度;
spacingVertical:文本垂直间距;
paddingTop、paddingBottom:View的上下Padding
<1>.计算文字的高度
这里是将画笔paint设置好字体大小和字体后,通过Paint的FontMetrics来计算文字的高度。
private void calculateTextHeightAndBaselineY() {
mPaint.setTextSize(mTxtSize);
if (mTypeface != null)
mPaint.setTypeface(mTypeface);
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
mTxtHeightMax = (float) Math.ceil(fontMetrics.descent - fontMetrics.ascent)
+ mTxtPaddingTop + mTxtPaddingBottom;
}
<2>.计算view的高度、文本块坐标
复写onMeasure方法。获取view的宽度,遍历所有文本,获取文本的宽度,累积相加,超过View的宽度时换行,记录行数和文本坐标。重复以上动作,直到文本遍历完毕,进而计算view高度。需要注意的是当单个文本的长度超过View的宽度时,需要对该个文本做特殊处理。如,截取文本,使文本在文本后添加“…”等。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
calculateTextHeightAndBaselineY();
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int height = 0;
int actualWidth = widthSize - getPaddingRight(); //获取可绘制的最大宽度
if (actualWidth > 0) {
//计算总高度
height = initTextListPoint(actualWidth);
}
setMeasuredDimension(widthSize, height);
}
/**
* 初始化文本坐标区间,计算View高度
*
* @param maxX 最大横坐标
* @return view的高度
*/
private int initTextListPoint(int maxX) {
int x = getPaddingLeft();
int height = 0;
int rows = 1;
int drawMaxWidth = (int) (maxX - getPaddingLeft() - mTxtPaddingLeft - mTxtPaddingRight); //绘制的最大宽度
mRow.append(0, 0);
mTextPoints = new Rect[mTextList.length];
for (int i = 0; i < mTextList.length; i++) {
String text = mTextList[i];
mPaint.getTextBounds(text, 0, text.length(), mBound);
int w = (int) mPaint.measureText(text); //计算该文本的长度