Android中TextView富文本适配问题

问题1

​ 先来看下面一段代码:

Spanned spanned = Html.fromHtml(text, mImageGetter, tagHandler);if (spanned instanceof SpannableStringBuilder) {    mBuilder = (SpannableStringBuilder) spanned;} else {    mBuilder = new SpannableStringBuilder(spanned);}ImageSpan[] imgSpans = mBuilder.getSpans(0, mBuilder.length(), ImageSpan.class);......setText(mBuilder);setMovementMethod(LinkMovementMethod.getInstance()); //实现TextView中的局部点击事件交互

​ 我先解释一下:这是在TextView中展示富文本时需要用到的一段代码。简单来说,就是通过Html.fromHtml()方法得到一个Spanned对象,然后再把该对象转换为SpannableStringBuilder的一个对象mBuilder,通过mBuilder的一系列方法去设置各种Span的属性,达到展示富文本的目的。

​ 这段代码在API版本<=23时,是完全没有问题,可以正常运行的;但是,当把该代码布置在API版本>23时的设备上时,出现了TextView中图片点击事件错乱的问题!

​ 后经过调试发现:在API版本>23的设备上,通过mBuilder.getSpans()获取Span数组时,变成了无序的数组,如下图所示:


具体为什么会这样?目前还没有找到具体是什么原因……有知悉该问题的朋友,烦请不吝赐教。多谢!多谢!

​ 既然知道了导致该问题的原因,那么解决起来还是相对比较容易的:只需判断当前系统版本是否大于23,如果是大于API23的系统版本时,则对拿到的数组进行重新排序即可。代码如下:

if (imgSpans.length > 0 && Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {    Arrays.sort(imgSpans, new Comparator() {        @Override        public int compare(ImageSpan lhs, ImageSpan rhs) {            // 升序排列            return mBuilder.getSpanStart(lhs) - mBuilder.getSpanStart(rhs);        }    });}

问题2

​ 我首先来说一下该问题在项目中的具体需求:在一段纯文本内容的文字中,当点击某一个特定区域时,改变该区域文本的颜色,实现交互效果;如下图所示:

​ 为了实现该需求,我需要自定义一个SectionClickableSpan继承自系统的ClickableSpan用来实现点击事件。代码如下:

public class SectionClickableSpan extends ClickableSpan {    /**     * 用以保存每个点击区域的位置信息holder     */    public static class SectionClickHolder {        /**         * 当前位置是否可点击;为false时,不可点击         */        public boolean isCanChange;        /**         * 当前位置索引         */        public int sectionIndex = -1;        /**         * 用以区分当前位置状态信息         */        public int sectionStatus = -1;        /**         * 可点击区域的开始索引         */        public int start;        /**         * 可点击区域的结束索引         */        public int end;        /**         * 颜色值         */        public int color;    }    public interface ISectionClickListener {        /**         * 当点击时回调该方法         *         * @param widget          当前被点击的view         * @param clickableHolder 当前被点击的holder信息         */        void onSectionClick(View widget, SectionClickHolder clickableHolder);    }    private SectionClickHolder mSectionClickHolder;    private ISectionClickListener mSectionClickListener;    public SectionClickableSpan(SectionClickHolder sectionClickHolder) {        mSectionClickHolder = sectionClickHolder;    }    @Override    public void onClick(View widget) {        if (mSectionClickListener != null) {            mSectionClickListener.onSectionClick(widget, mSectionClickHolder);        }    }    @Override    public void updateDrawState(TextPaint ds) {        super.updateDrawState(ds);        ds.setColor(Color.parseColor("#666666"));        ds.setUnderlineText(false);        ds.clearShadowLayer();    }    public void setSectionClickListener(ISectionClickListener sectionClickListener) {        mSectionClickListener = sectionClickListener;    }    public SectionClickHolder getSectionClickHolder() {        return mSectionClickHolder;    }}

​ 然后在点击之后,执行如下操作,实现指定区域颜色的更新。代码如下:

......int color = -1;if (clickHolder.sectionStatus == QTConstants.SectionImgStatus.SECTION_CHECKED) {    color = QTConstants.PenColor.BLUE;} else if (clickHolder.sectionStatus == QTConstants.SectionImgStatus.SECTION_DEF) {    color = QTConstants.PenColor.GRAY;}if (color != -1) {    // 说明有需要更新的SectionClickableSpan,更新当前位置span颜色值    ForegroundColorSpan fcs = new ForegroundColorSpan(color);    mBuilder.setSpan(fcs, clickHolder.start, clickHolder.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);    setText(mBuilder);}

​ 这两段代码在API版本<=23的设备上运行时,没有任何问题;但是在适配到API版本>23的设备时,碰到了如下问题:

  • ​ 一. TextView中ClickableSpan响应点击事件混乱;

  • ​ 二. TextView中富文本初始化时,ClickableSpan颜色信息混乱;

  • ​ 三. 每次点击ClickableSpan时,颜色信息更新混乱;

    第一个问题和上面的问题1其实是同一个问题,解决起来相对比较简单,也是同样的套路。代码如下:

SectionClickableSpan[] sectionClickableSpans = mBuilder.getSpans(0, mBuilder.length(), SectionClickableSpan.class);if (sectionClickableSpans.length > 0 && Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {    Arrays.sort(sectionClickableSpans, new Comparator() {        @Override        public int compare(SectionClickableSpan lhs, SectionClickableSpan rhs) {            // 升序排列            return lhs.getSectionClickHolder().start - rhs.getSectionClickHolder().start;        }    });}

​ 至于第二个问题,着实纠结了很久,网上也没有找到类似问题的相关描述。后经过仔细分析之后发现,可以从SectionClickableSpan中的updateDrawState()方法着手,最终,解决了初始化颜色混乱的问题。代码如下:

@Overridepublic void updateDrawState(TextPaint ds) {    super.updateDrawState(ds);    /*        在api23版本之上时,TextView中span的颜色会出现错乱问题;具体原因不明。        该mSectionClickHolder.color用以解决该问题。    */    if (mSectionClickHolder != null && mSectionClickHolder.color != 0) {        ds.setColor(mSectionClickHolder.color);    } else {        // error color        ds.setColor(Color.parseColor("#666666"));    }    ds.setUnderlineText(false);    ds.clearShadowLayer();}

​ 至于为什么会造成第二个问题的产生,一直也没有找到具体的原因;要想彻底搞明白,就得去对比一下两个版本的源码啦,这个还是暂时先放一边吧。如果有朋友搞明白了这个问题,烦请不吝赐教!在此,万分感谢!

​ 接下来就集中精力解决第三个问题。经过debug不断调试,发现如下现象:当API版本>23时,在TextView中的同一个位置重复设置ForegroundColorSpan颜色时,需要先把之前存在的ForegroundColorSpan清除掉才可以。修改后的代码如下:

int color = -1;if (clickHolder.sectionStatus == QTConstants.SectionImgStatus.SECTION_CHECKED) {    color = QTConstants.PenColor.BLUE;} else if (clickHolder.sectionStatus == QTConstants.SectionImgStatus.SECTION_DEF) {    color = QTConstants.PenColor.GRAY;}// 每次点击更新颜色值clickHolder.color = color;if (color != -1) {    /*        在api23以上的版本中,重复在同一位置设置背景span样式时,会导致颜色显示错乱。具体原因不明        每次设置背景时,移除该位置之前所有的背景span    */    ForegroundColorSpan[] foregroundColorSpans = mBuilder.getSpans(clickHolder.start,                                    clickHolder.end, ForegroundColorSpan.class);    if (foregroundColorSpans != null && foregroundColorSpans.length > 0) {        for (ForegroundColorSpan foregroundColorSpan : foregroundColorSpans) {            mBuilder.removeSpan(foregroundColorSpan);        }    }    ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(color);    mBuilder.setSpan(foregroundColorSpan, clickHolder.start, clickHolder.end,                   Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);    setText(mBuilder);}

​ 至此,在API版本>23时,TextView中的富文本适配问题已经算是解决了。至于该问题背后的深层次原因,留待后续再来深究各版本源码的差异!

​ 以此记录,方便查阅!

更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. CreateProcess error = 2,系统找不到指定的文件
  3. Android(安卓)文件管理器 Android文件管理器源代码
  4. Android(安卓)Menu小例子
  5. android 5.0修改插电显示电池百分比,拔掉不显示问题
  6. Android查看源码
  7. Android设备:外接扫码枪与系统软键盘问题
  8. Android发展历程
  9. Android环境搭建

随机推荐

  1. android4.3应用程序隐藏状态栏和标题栏
  2. Android(安卓)颜色渲染(十) ComposeShade
  3. 基础 Android(安卓)开发规范整理
  4. Android(安卓)代码执行Linux Shell小记
  5. Android属性之android:priority
  6. 2011.08.29——— android dip px解析及
  7. Android快速入门相关(一)
  8. 卡片式UI的总结 android
  9. Android(安卓)Map API key 申请
  10. Google Android软件架构