本文为 FlowTextView 项目中 FlowTextView 部分
项目地址:FlowTextView,分析的版本:815bdc3
1.功能介绍
FlowTextView 是对 TextView 的包装,可以在文本中插入图片等其他控件,并且支持 Html 格式文本。
1.1 集成指南
compile 'com.github.deano2390:FlowTextView:2.0.4'
1.2 使用指南
XML文件指定子控件,用法同RelativeLayout布局。
<uk.co.deanwild.flowtextview.FlowTextView
android:id="@+id/ftv"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:padding="10dip"
android:src="@drawable/android"/>
</uk.co.deanwild.flowtextview.FlowTextView>
Java代码中指定插入文本
FlowTextView flowTextView = (FlowTextView) findViewById(R.id.ftv);
Spanned html = Html.fromHtml("<html>Your html goes here....");
flowTextView.setText(html);
2.详细设计
2.1 models包
models包下存放一些抽象概念的模型类。其中域权限均是 public,并不需要通过get/set方法操作。
1.Area
Area 定义一段X轴上区域,表示子控件(即obstacle)占据的区间。
float x1;
float x2;
float width;
2.Line
Line 代表文本行在x轴上占据的区域。
float leftBound;
float rightBound;
3.Obstacle
Obstacle 代表布局中子控件对文本构成的障碍区,由子控件左上和右下点坐标构成。
int topLeftx;
int topLefty;
int bottomRightx;
int bottomRighty;
4.HtmlObject
HtmlObject 代表 html 文本。
String content;
int start;
int end;
float xOffset; //代表x轴上的实际偏移,非字符偏移
TextPaint paint;
boolean recycle = false;
5.HtmlLink
HtmlLink 继承 HtmlObject,代表超链接。
float width;
float height;
float yOffset; //代表y轴上的实际偏移,非字符偏移
String url;
2.2 OnLinkClickListener类
此接口定义了超链接被点击后的回调方法,可以自定义实现.
public void onLinkClick(String url);
2.3 helpers包
一些辅助类
1.PaintHelper
PaintHelper负责存储和管理画笔,避免重复创建画笔类
主要域
ArrayList<TextPaint> mPaintHeap //维持一个画笔集合
主要方法
void recyclePaint(TextPaint paint) //存储画笔
TextPaint getPaintFromHeap() //获取画笔
void setColor(int color)
2.SpanParser
SpanParser 负责处理 html 文本。
int mTextLength //html文本对象长度
Spannable mSpannable //html文本对象
PaintHelper mPaintHelper //画笔辅助类
FlowTextView mFlowTextView //主控件
List<HtmlLink> mLinks // html文本中的超链接集合
HashMap<Integer, HtmlObject> sorterMap //html文本集合
主要方法是 parseSpan,负责解析出HtmlObject对象,根据 待解析对象的不同分三种解析方法。
HtmlObject parseSpan(Object span, String content, int start, int end){
if(span instanceof URLSpan)
return getHtmlLink((URLSpan) span, content, start, end, 0);
else if(span instanceof StyleSpan){
return getStyledObject((StyleSpan) span, content, start, end, 0);
else
return getHtmlObject(content, start, end, 0);
}
3.ClickHandler
ClickHandler是OnTouchListener的实现类,主要实现了对超链接点击事件的处理。
SpanParser mSpanParser; //html文本处理类,构造方法传入
OnLinkClickListener mOnLinkClickListener; //get/set方法设置
double distance //两点距离
float x1,y1,x2,y2 //两点
4.CollisionHelper
calculateLineSpaceForGivenYOffset 方法负责根据组件中的障碍物列表和文本行高度 计算得到使得文本与组件不冲突的 line 对象。
Line calculateLineSpaceForGivenYOffset(
float lineYbottom,
int lineHeight,
float viewWidth,
ArrayList<Obstacle>)
其算法步骤如下:
建立Line 对象(0~viewWidth),即默认情况下文本行宽度等于组件宽度;
遍历 obstacles 集合,判断当前 Obstacle 是否与 文本行高度有交叉
-
如果有
创建leftArea对象,内循环遍历 obstacles 集合以确定其边界
创建rightArea对象,内循环遍历 obstacles 集合以确定其边界
在Area集合中寻找范围最大者,最终确定Line对象
这个算法写的极其诡异
2.4 FlowTextView
FlowTextView 继承自 RelativeLayout。
主要域
1.PaintHelper mPaintHelper
2.SpanParser mSpanParser
3.ClickHandler mClickHandler
4.int mColor
5.int pageHeight 页面高度
6.TextPaint mTextPaint 文本画笔
7.TextPaint mLinkPaint html中超链接文本画笔
8.float mTextsize 文本字体大小
9.int mTextColor 文本字体颜色
10.Typeface typeFace 文本字体类型
11.mDesiredHeight 整个控件高度
12.boolean needsMeasure 空间是否需要重新测量
13.ArrayList<Obstacle> obstacles 布局中已存在的控件障碍列表
14.CharSequence mText 显示文本
15.boolean mIsHtml 文本是否是 html 格式
16.float mSpacingMult;
17.float mSpacingAdd;
18.List<HtmlObject> lineObjects
19.HtmlObject htmlLine
核心方法
1.setText 方法将导致控件重绘
public void setText(CharSequence text) {
mText = text;
if (text instanceof Spannable) {
mIsHtml = true;
mSpanParser.setSpannable((Spannable) text);
} else {
mIsHtml = false;
}
this.invalidate();
}
2.onDraw方法 绘制
扼要分析
//1.初始化所有Obstacle,并获得子组件最大高度
int lowestYCoord = findBoxesAndReturnLowestObstacleYCoord();
//2.拆分文本并处理
String[] blocks = mText.toString().split("\n");
for (int block_no = 0; block_no <= blocks.length - 1; block_no++){
String thisBlock = blocks[block_no];
while (thisBlock.length() > 0) {
//3.文本行数+1,并获得绘图起点和文本水平范围
lineIndex++;
yOffset = getPaddingTop() + lineIndex * lineHeight - (getLineHeight() + mTextPaint.getFontMetrics().ascent);
Line thisLine = CollisionHelper.calculateLineSpaceForGivenYOffset(yOffset, lineHeight, mViewWidth, obstacles);
xOffset = thisLine.leftBound;
maxWidth = thisLine.rightBound - thisLine.leftBound;
float actualWidth;
//4.处理 actualWidth 与 maxWidth之间的矛盾
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。