http://blog.csdn.net/xujunfeng000/article/details/36399339?utm_source=tuicool&utm_medium=referral

TextView本身是支持图文混排的,在手机上,通过TextView进行图文混排时,排版可能难以达到PC上浏览器的效果,特别是对于一些支持多种标签的发布系统。

1. 网上很容易找到的使用TextView实现图文混排的例子,大多是类似于下面的形式:

TextView tv_Content;

tv_Content.setText(Html.fromHtml(item.getContent(), GetImageGetter(), null));

  1. private ImageGetter imageGetter = null;
  2. private Map<String, URLDrawable> imageHashMap = null;
  3. private ImageGetter GetImageGetter() {
  4. if(imageHashMap == null) {
  5. imageHashMap = new HashMap<String, URLDrawable>(2);
  6. }
  7. if(imageGetter == null) {
  8. imageGetter = new ImageGetter() {
  9. //通过网络获取图片是一个耗时的操作,最好不要放在主线程中,否则容易引起阻塞。
  10. @Override
  11. public Drawable getDrawable(String source) {
  12. String key = MD5.EncoderByMD5(source);
  13. URLDrawable urlDrawable = imageHashMap.get(key);
  14. if(urlDrawable == null) {
  15. urlDrawable = new URLDrawable();
  16. imageHashMap.put(key, urlDrawable);
  17. // get the actual source
  18. ImageGetterAsyncTask.start(mContext, urlDrawable, source, handler);
  19. }
  20. return urlDrawable;
  21. }
  22. };
  23. }
  24. return imageGetter;
  25. }
  26. private Handler handler = new Handler() {
  27. @Override
  28. public void handleMessage(android.os.Message msg) {
  29. if(msg.what == ImageGetterAsyncTask.OnDrawablePrepared) {
  30. refreshNewsImage(msg);
  31. }
  32. }
  33. };
  34. private void refreshNewsImage(android.os.Message msg) {
  35. notifyDataSetChanged();
  36. }

需要设置要显示图片的尺寸:

drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());

2.
一般在listViewItem中使用都没有问题,但是如果作为scrollView的子视图的话,在有图像时会抛出异常(在公司测试机上如此,其他环境
没有去验证)。建议通过自定义视图的方式来实现,基本思路就是利用SpannableStringBuilder来分割图片及非图片内容,然后逐一创建图
片及非图片视图。对于类似于的新闻呈现且需要高度定制UI的场合非常适用。

2.1 content_textview.xml :用于显示图片之外的内容

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <TextView xmlns:android="http://schemas.android.com/apk/res/android"
  3. style="@style/Style_NewsText_Content"
  4. android:layout_width="fill_parent"
  5. android:layout_height="wrap_content"
  6. android:paddingLeft="10dp"
  7. android:paddingRight="10dp"
  8. android:typeface="normal" >
  9. </TextView>

2.2 content_imageview.xml:用于显示图片及图片说明,如“[图 1]”

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="wrap_content"
  5. android:layout_gravity="center_horizontal"
  6. android:orientation="vertical"
  7. android:paddingTop="5dp">
  8. <ImageView
  9. android:id="@+id/content_imageview_image"
  10. android:layout_width="wrap_content"
  11. android:layout_height="wrap_content"
  12. android:layout_gravity="center_horizontal"
  13. android:adjustViewBounds="true"
  14. android:baselineAlignBottom="true"
  15. android:contentDescription="@string/xxx"
  16. android:minHeight="30dp"
  17. android:minWidth="30dp"
  18. android:paddingBottom="5dp"
  19. android:scaleType="centerInside" >
  20. </ImageView>
  21. <TextView
  22. android:id="@+id/content_imageview_title"
  23. style="@style/Style_NewsText_Content"
  24. android:layout_width="fill_parent"
  25. android:layout_height="wrap_content"
  26. android:gravity="center"
  27. android:paddingBottom="5dp"
  28. android:singleLine="false"
  29. android:textColor="@color/text_b0b0b0"
  30. android:textSize="@dimen/font_small" >
  31. </TextView>
  32. </LinearLayout>

2.3 vertical_linearlayout.xml:根视图,用于插入待显示内容

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="wrap_content"
  5. android:orientation="vertical" >
  6. </LinearLayout>

2.4 MyImageTextView.jva:实现图文混排的类

  1. public class MyImageTextView extends FrameLayout {
  2. //对应的view
  3. private LinearLayout mContentView = null;
  4. //对应的数据
  5. private CharSequence mData = null;
  6. private String[] mImageUrl = null;
  7. private ImageView[] mImage = null;
  8. private int mImageBaseIndex = 1; //从[图 1]开始
  9. //是否支持超链接点击
  10. private Boolean supportMovementMethod = false;
  11. //是否显示图索引
  12. private Boolean showImageIndex = false;
  13. public MyImageTextView(Context context) {
  14. this(context, null);
  15. }
  16. public MyImageTextView(Context context, AttributeSet attrs) {
  17. this(context, attrs, 0);
  18. }
  19. public MyImageTextView(Context context, AttributeSet attrs, int defStyle) {
  20. super(context, attrs, defStyle);
  21. init();
  22. }
  23. private void init() {
  24. setDrawingCacheEnabled(false);
  25. setClipChildren(false);
  26. mContentView = (LinearLayout) LayoutInflater.from(getContext()).inflate(
  27. R.layout.vertical_linearlayout, null);
  28. addView(mContentView);
  29. }
  30. /**
  31. * 设置待显示内容
  32. * @param content
  33. */
  34. public void setText(CharSequence content) {
  35. try {
  36. if(TextUtils.isEmpty(content)) { return; }
  37. if(content.equals(mData)) { return; }
  38. mData = content;
  39. mContentView.removeAllViews(); // 首先清理之前加入的子视图
  40. int viewIndex = 0;
  41. int len = content.length();
  42. SpannableStringBuilder style = new SpannableStringBuilder(content);
  43. ImageSpan[] imgAry = style.getSpans(0, len, ImageSpan.class);
  44. if(imgAry == null || imgAry.length <= 0) {
  45. addTextView(content, viewIndex);
  46. return;
  47. }
  48. int pos = 0;
  49. int start = 0;
  50. int end = 0;
  51. ImageSpan img = null;
  52. mImageUrl = new String[imgAry.length];
  53. mImage = new ImageView[imgAry.length];
  54. for(int i = 0; i < imgAry.length; i++) {
  55. img = imgAry[i];
  56. mImageUrl[i] = img.getSource();
  57. start = style.getSpanStart(img);
  58. if(pos < start) {
  59. addTextView(style.subSequence(pos, start), viewIndex++);
  60. }
  61. end = style.getSpanEnd(img);
  62. addImageView(i, viewIndex++);
  63. pos = end + 1;
  64. }
  65. if(pos > 0 && pos < len) {
  66. addTextView(style.subSequence(pos, len), viewIndex);
  67. }
  68. requestLayout();
  69. invalidate(); //on a UI thread
  70. } catch(Exception ex) {
  71. }
  72. }
  73. private void addTextView(CharSequence text, int viewIndex) {
  74. TextView tv = (TextView) LayoutInflater.from(getContext()).inflate(
  75. R.layout.content_textview, null);
  76. mContentView.addView(tv, viewIndex);
  77. tv.setText(text);
  78. if(supportMovementMethod) {
  79. changeLink(tv);
  80. }
  81. }
  82. private void addImageView(int index, int viewIndex) {
  83. View parent = LayoutInflater.from(getContext()).inflate(
  84. R.layout.content_imageview, null);
  85. mImage[index] = (ImageView) parent.findViewById(R.id.content_imageview_image);
  86. TextView tvTitle = (TextView)parent.findViewById(R.id.content_imageview_title);
  87. if(showImageIndex) {
  88. //这里的图片标题,也可以通过<img>标签的title/alt等属性分析出来
  89. tvTitle.setText("[图 " + Integer.toString(mImageBaseIndex + index) + "]");
  90. tvTitle.setVisibility(View.VISIBLE);
  91. } else {
  92. tvTitle.setVisibility(View.GONE);
  93. }
  94. mContentView.addView(parent, viewIndex);
  95. setImage(parent, mImage[index], mImageUrl[index]);
  96. }
  97. private void setImage(View parent, ImageView iv, String picUrl){
  98. if(picUrl != null && picUrl.trim().length() > 0) {
  99. parent.setVisibility(View.VISIBLE);
  100. iv.setImageResource(R.drawable.weibo_pic_loading);
  101. Size size = setPic(iv, picUrl);
  102. if(size.getHeight() > 0 && size.getWidth() > 0) {
  103. parent.requestLayout();
  104. }
  105. }
  106. else{
  107. parent.setVisibility(View.GONE);
  108. }
  109. }
  110. private Size setPic(ImageView logoView, String logoUrl) { //异步加载图片代码略
  111. return XXXFileManager.getInstance().setImageBitmapWithMemoryCache(
  112. getContext(), logoView, logoUrl, XXXFileManager.getImagetLrucache(),
  113. getContext().getClass().getName(), false);
  114. }
  115. /**
  116. * 供图片下载完毕时调用
  117. * @param fileURL
  118. */
  119. public void setPic(String fileURL) {
  120. if(mImage != null && mImageUrl != null && !TextUtils.isEmpty(fileURL)) {
  121. String source = null;
  122. for(int i = 0; i < mImageUrl.length && i < mImage.length; i++) {
  123. source = mImageUrl[i];
  124. if(!TextUtils.isEmpty(source)) {
  125. if(fileURL.equals(source)) {
  126. setPic(mImage[i], source);
  127. mImage[i].getParent().requestLayout();
  128. break;
  129. }
  130. }
  131. }
  132. }
  133. }
  134. /**
  135. * 设置是否支持超链接点击
  136. */
  137. public void setSupportMovementMethod(Boolean supportMovementMethod) {
  138. this.supportMovementMethod = supportMovementMethod;
  139. }
  140. /**
  141. * 设置是否显示图索引
  142. * @param showImageIndex
  143. */
  144. public void setShowImageIndex(Boolean showImageIndex) {
  145. this.showImageIndex = showImageIndex;
  146. }
  147. /**
  148. * 设置TextView超链接跳转
  149. * @param tv
  150. */
  151. private void changeLink(TextView tv){
  152. tv.setMovementMethod(LinkMovementMethod.getInstance());
  153. CharSequence text = tv.getText();
  154. if (text instanceof Spannable) {
  155. int end = text.length();
  156. Spannable sp = (Spannable) tv.getText();
  157. URLSpan[] urls = sp.getSpans(0, end, URLSpan.class);
  158. if(urls == null || urls.length <= 0) { return; }
  159. SpannableStringBuilder style = new SpannableStringBuilder(text);
  160. URLSpan[] urlsn = style.getSpans(0, end, URLSpan.class);
  161. if(urlsn == null || urls.length != urlsn.length) { return; }
  162. //循环把链接发过去
  163. URLSpan url = null;
  164. for(int i = 0; i < urls.length && i < urlsn.length; i++) {
  165. url = urls[i];
  166. MyURLSpan myURLSpan = new MyURLSpan(getContext(), url.getURL());
  167. style.removeSpan(urlsn[i]);
  168. style.setSpan(myURLSpan, sp.getSpanStart(url),
  169. sp.getSpanEnd(url), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
  170. }
  171. tv.setText(style);
  172. }
  173. }
  174. public int getImageCount() {
  175. int cnt = mImageBaseIndex;
  176. if(mImage != null && mImageUrl != null) {
  177. cnt += mImage.length;
  178. }
  179. return cnt;
  180. }
  181. public void setmImageBaseIndex(int baseIndex) {
  182. this.mImageBaseIndex = baseIndex;
  183. }
  184. public CharSequence getmData() {
  185. return mData;
  186. }
  187. }

2.5 MyURLSpan.java:定义一个可点击的Span,点击超链接时通过浏览器打开改网页/文件。

  1. public class MyURLSpan extends ClickableSpan {
  2. private Context context = null;
  3. private String mUrl     = null;;
  4. public MyURLSpan(Context context,String url) {
  5. this.context = context;
  6. this.mUrl = url;
  7. }
  8. @Override
  9. public void onClick(View widget) {
  10. if (URLUtil.isNetworkUrl(mUrl)) {
  11. XXXUtils.openMyWebBrowser(this.context,
  12. this.context.getResources().getString(R.string.newstext_hyperlink),
  13. this.mUrl);
  14. }
  15. }
  16. }

3 使用简单,可以在xml文件中引用,也可以动态创建视图。

3.1 在xml中引用

  1. <ScrollView
  2. android:id="@+id/XXX_ScrollView"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent" >
  5. <LinearLayout
  6. android:id="@+id/XXX_Parent"
  7. android:layout_width="fill_parent"
  8. android:layout_height="wrap_content"
  9. android:orientation="vertical"
  10. android:visibility="gone" >
  11. ……
  12. <LinearLayout
  13. android:layout_width="fill_parent"
  14. android:layout_height="wrap_content"
  15. android:orientation="vertical"
  16. android:paddingBottom="5dp"
  17. android:paddingTop="10dp" >
  18. <XXX.textview.MyImageTextView
  19. android:id="@+id/XXX_Content"
  20. android:layout_width="fill_parent"
  21. android:layout_height="wrap_content"
  22. android:paddingLeft="10dp"
  23. android:paddingRight="10dp" >
  24. </XXX.textview.MyImageTextView>
  25. <RelativeLayout
  26. android:id="@+id/XXX_PayViewParent"
  27. android:layout_width="fill_parent"
  28. android:layout_height="wrap_content"
  29. android:paddingBottom="10dp"
  30. android:paddingTop="5dp"
  31. android:visibility="gone" >
  32. <XXX.textview.MyImageTextView
  33. android:id="@+id/XXX_PayContent"
  34. android:layout_width="fill_parent"
  35. android:layout_height="wrap_content"
  36. android:paddingLeft="10dp"
  37. android:paddingRight="10dp"
  38. android:visibility="gone" >
  39. </XXX.textview.MyImageTextView>
  40. <RelativeLayout
  41. android:id="@+id/XXX_PayLock"
  42. android:layout_width="fill_parent"
  43. android:layout_height="wrap_content"
  44. android:background="@drawable/xxx_paylock_bg"
  45. android:gravity="center_horizontal" >
  46. <ImageView
  47. android:id="@+id/xxx_Lock"
  48. android:layout_width="wrap_content"
  49. android:layout_height="wrap_content"
  50. android:layout_centerVertical="true"
  51. android:layout_marginRight="5dp"
  52. android:contentDescription="@string/xxx"
  53. android:padding="5dp"
  54. android:src="@drawable/xxx_paylock_icon" >
  55. </ImageView>
  56. ……
  57. </RelativeLayout>
  58. </RelativeLayout>
  59. </LinearLayout>
  60. </LinearLayout>
  61. </ScrollView>

3.2 java代码,设置显示内容

  1. itvFreeContent = (MyImageTextView) this.findViewById(R.id.XXX_Content);
  2. itvFreeContent.setSupportMovementMethod(true);
  3. //itvFreeContent.setShowImageIndex(true);
  4. itvFreeContent.setText(Html.fromHtml(formatContent(content)));

当然这里还需要加入图片异步下载完成后的代码,如:

  1. private void initHandler() {
  2. this.mHandler = new Handler() {
  3. @Override
  4. public void handleMessage(Message msg) {
  5. switch (msg.what) {
  6. case KLoadImageOver:
  7. itvFreeContent.setPic(msg.getData().getString("fileURL"));
  8. break;
  9. default:
  10. break;
  11. }
  12. }
  13. };
  14. }

实际效果图(截取部分):

4 第二种方式需要扩展的是,如果显示的内容有超链接,且超链接时中的显示对象是图片,那么需要给图片增加点击事件,点击的跳转参照MyURLSpan.onClick。

关于图片的下载,这里推荐一个第三方库Android-Universal-Image-Loader

自定义图文混排视图MyImageTextView的更多相关文章

  1. android开发 自定义图文混排控件

    功能:图文混排,可自动缩放字体,如图: 单点触控使用的代码来自:http://blog.csdn.net/xiaanming/article/details/42833893  谢谢博主! 在该dem ...

  2. iOS中 图文混排/自定义图文混排 作者:韩俊强

    指示根视图:(准备几张图片,把label加载在window上) CustomLable *label = [[CustomLable alloc]initWithFrame:CGRectMake(0, ...

  3. IOS实现UIButton图文混排、自定义按钮按下和正常状态下不同的背景颜色、根据文字长度自定义UIButton长度

    在一些项目中,我们需要自定义自己的UIButton,使Button上面同时具有图片和文字描述,实现自定义UIButton的图文混排. 首先我们需要定义一个继承自UIButton的类,同时实现自己的in ...

  4. XMPP键盘订制实现图文混排

    在现阶段的通信服务中,各种标准都有,因此会出现无法实现相互连通,而XMPP(Extensible Message and presence Protocol)协议的出现,实现了整个及时通信服务协议的互 ...

  5. iOS 图文混排 链接 可点击

    对于这个话题 我想到 1 第一个解决方法就是使用 webView 比较经典 把所有复杂工作都交给控件本身去处理了,  但是好像好多需要自定义的地方 没法从 webView获得响应回调 :(估计也可以实 ...

  6. [Swift通天遁地]八、媒体与动画-(13)CoreText框架实现图文混排

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  7. iOS火焰动画效果、图文混排框架、StackView效果、偏好设置、底部手势等源码

    iOS精选源码 高性能图文混排框架,构架顺滑的iOS应用. 使用OpenGLE覆盖阿尔法通道视频动画播放器视图. 可选最大日期截至当日日期的日期轮选器ChooseDatePicker 简单轻量的图片浏 ...

  8. 【iOS】使用CoreText实现图文混排

    iOS没有现成的支持图文混排的控件,而要用多个基础控件组合拼成图文混排这样复杂的排版,是件很苦逼的事情.对此的解决方案有使用CoreText进行绘制,或者使用TextKit.本文主要讲解对于CoreT ...

  9. 高性能图文混排框架,构架顺滑的iOS应用-b

    About GallopGallop是一个功能强大.性能优秀的图文混排框架. Features主要用于解决以下需求: 滚动列表的性能优化.Gallop使用异步绘制.视图层级合并.观察mainRunlo ...

随机推荐

  1. 浅谈JS DDoS攻击原理与防御

    分布式拒绝服务攻击(DDoS)攻击是一种针对网站发起的最古老最普遍的攻击.Nick Sullivan是网站加速和安全服务提供商CloudFlare的一名系统工程师.近日,他撰文介绍了攻击者如何利用恶意 ...

  2. Django Sqlite3 数据库向MySQL迁移

    整合了两个URL而来.. 1,http://www.phodal.com/blog/django-mezzanine-sqlite3-migrate-mysql/ 2,http://www.ziqia ...

  3. Android SwitchCompat 自定义颜色及使用

    在Android 5.0 中 Switch 更新了样式 变得 比较好用了 但是在5.0 以下的版本 还是老样子 不实用 因此 就有了 SwitchCompat 来兼容 它是v7 包中的 因此可兼容到 ...

  4. jinfo命令(Java Configuration Info)

    jinfo可以输出并修改运行时的java 进程的opts.用处比较简单,用于输出JAVA系统参数及命令行参数.用法是jinfo -opt  pid 如:查看2788的MaxPerm大小可以用  jin ...

  5. Android清除本地数据缓存代码

    /*  * 文 件 名:  DataCleanManager.java  * 描    述:  主要功能有清除内/外缓存,清除数据库,清除sharedPreference,清除files和清除自定义目 ...

  6. 清除nginx静态资源缓存

    之前写过一篇如何配置nginx缓存及手动清除缓存的文章: http://www.cnblogs.com/Eivll0m/p/4921829.html 但如果有大量缓存需要清理,手动一条条清理就比较慢了 ...

  7. mkimage使用详解

    uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件. mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字 ...

  8. 数据结构(启发式合并):HNOI 2009 梦幻布丁

    Description N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. Input 第 ...

  9. Android项目开发全程(二)--Afinal用法简单介绍

    本篇博文接上篇的<Android项目开发全程(一)--创建工程>,主要介绍一下在本项目中用到的一个很重要的框架-Afinal,由于本系列博文重点是项目开发全程,所以在这里就先介绍一下本项目 ...

  10. 使用Codis搭建redis集群服务

    转(http://www.jianshu.com/p/f8e968e57863) 一. 应用场景 redis 作为数据结构存储引擎,有着很多优点 高性能单机引擎可以达到5-10W qps 数据结构全面 ...