最近利用業餘時間,開發了一款基於懂球帝接口數據的足球資訊app,整體的UI也是仿照懂球帝設計的。這是一個比較綜合的項目,用到了不少以前沒用過的組件和api,而且產生了很多新的開發思路,有些實現方式也是自己琢磨的,所以值得做一些記錄,可能還存在瑕疵和可以優化的地方,也希望大家給我多指正。
先來看看實現前後的對比圖:
再來看一看接口返回的數據(數據結構比較長,這裏只截取了部分用到的數據):
可以看到,懂球帝這裏是通過file_name去跟文本中對應的標記匹配來實現圖文混排的,而不是通過html格式去做的,因此我這裏想到的是通過在spannableString中插入圖片的方式去實現混排。
思路整理
1、通過glide將圖片下載下來;
Glide.with(context).load(a.getUrl()).into(new SimpleTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
}
});
2、利用String的indexOf函數,通過匹配file_name,找到圖片在文本中的位置;
int startIndex = circle.getContent().indexOf(a.getFile_name());
int endIndex = startIndex+a.getFile_name().length();
3、利用spannableString的setSpan函數,將圖片插入到對應位置;
spannableString.setSpan(imageSpan, startIndex,endIndex, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
遇到的問題
1、下載圖片是需要通過異步方式去實現的,那麼我就沒法控制 當所有圖片都下載完成後再通知TextView更新文本了。
解決辦法:後來發現spannableString的setSpan()並不會覆蓋上一次的樣式,而是類似於addSpan的效果,因此我的解決方法就是每加載完一張圖片,就setSpan()一次,更新一次樣式;
spannableString.setSpan(imageSpan, startIndex,endIndex, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
handler.sendEmptyMessage(i);
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
tvContent.setText(spannableString);
}
};
2、setSpan之後,文本的{{p1}}、{{p2}}等處確實不顯示了,但圖片未加載到對應位置。
解決辦法:出現這種問題的原因是沒有對圖片的寬高進行設置,需要調用drawable的setBounds()設置圖片的大小。
//設置圖片顯示的寬高
resource.setBounds(0,0,(int)picWidth,(int)picHeight);
3、圖片與文字之間的行高與文件間的行高不一致;
解決辦法:首先去除文本中多餘的空行
public static String deleteCRLF(String input) {
return input.replaceAll("((\r\n)|\n)[\\s\t ]*(\\1)+", "$1").replaceAll("^((\r\n)|\n)", "");
}
然後對ImageSpan做一些處理(這個是我直接上網百度的,原理有待研究)
public class MyImageSpan extends ImageSpan {
public MyImageSpan( Drawable b) {
super(b);
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end,
float x, int top, int y, int bottom, Paint paint) {
Drawable b = getDrawable();
canvas.save();
int transY;
//要顯示的文本高度-圖片高度除2等居中位置+top(換行情況)
transY = ((bottom - top) - b.getBounds().bottom) / 2 + top;
//偏移畫布後開始繪製
canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
}
}
最終實現
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
tvContent.setText(spannableString);
}
};
for(CircleNews.Attachment a: circle.getAttachments()){
Glide.with(context).load(a.getUrl()).into(new SimpleTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
MyImageSpan imageSpan = new MyImageSpan(resource);
//ImageSpan imageSpan = new ImageSpan(resource);
double picWidth = UIUtils.getScreenWidth(context);
double ratio = picWidth/(double)resource.getIntrinsicWidth();
double picHeight = ratio*resource.getIntrinsicHeight();
//設置圖片顯示的寬高
resource.setBounds(0,0,(int)picWidth,(int)picHeight);
int i = circle.getAttachments().indexOf(a);
int startIndex = circle.getContent().indexOf(a.getFile_name());
int endIndex = startIndex+a.getFile_name().length();
spannableString.setSpan(imageSpan, startIndex,
endIndex, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
handler.sendEmptyMessage(i);
}
});
}
總結
之前處理富文本通常是通過html樣式的去實現的;通過研究懂球帝的這個api接口,又獲得了一種新的思路:可以通過SpannableString+文本標記的方式去實現富文本,使用SpannableString的好處是可以實現高度的自定義,比如說插入一個自定義表情,使用SpannableString只需在文本中增加一個標記識別即可,而使用html樣式的話,就相對複雜了。實現這種圖文混排的方式應該還有很多,如果你耐心看到這裏,不妨留下你的一些想法吧,我們可以一起交流,共同進步!