自定义图文混排视图MyImageTextView

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.   
  27. private Handler handler = new Handler() {  
  28.     @Override  
  29.     public void handleMessage(android.os.Message msg) {  
  30.         if(msg.what == ImageGetterAsyncTask.OnDrawablePrepared) {  
  31.             refreshNewsImage(msg);  
  32.         }  
  33.     }  
  34. };  
  35.   
  36. private void refreshNewsImage(android.os.Message msg) {  
  37.     notifyDataSetChanged();  
  38. }  


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

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.   
  10. </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.   
  9.     <ImageView  
  10.         android:id="@+id/content_imageview_image"  
  11.         android:layout_width="wrap_content"  
  12.         android:layout_height="wrap_content"  
  13.         android:layout_gravity="center_horizontal"  
  14.         android:adjustViewBounds="true"  
  15.         android:baselineAlignBottom="true"  
  16.         android:contentDescription="@string/xxx"  
  17.         android:minHeight="30dp"  
  18.         android:minWidth="30dp"  
  19.         android:paddingBottom="5dp"  
  20.         android:scaleType="centerInside" >  
  21.   
  22.     </ImageView>  
  23.   
  24.     <TextView  
  25.         android:id="@+id/content_imageview_title"  
  26.         style="@style/Style_NewsText_Content"  
  27.         android:layout_width="fill_parent"  
  28.         android:layout_height="wrap_content"  
  29.         android:gravity="center"  
  30.         android:paddingBottom="5dp"  
  31.         android:singleLine="false"  
  32.         android:textColor="@color/text_b0b0b0"  
  33.         android:textSize="@dimen/font_small" >  
  34.     </TextView>  
  35.   
  36. </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.       
  7.   
  8. </LinearLayout>  



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

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


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

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


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

posted @ 2015-11-18 19:30  misybing  阅读(744)  评论(0编辑  收藏  举报