自定义图文混排视图MyImageTextView

来源:互联网 发布:淘宝网跳蚤街 编辑:程序博客网 时间:2024/05/29 05:54

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


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

TextView tv_Content;

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


private ImageGetter imageGetter = null;private Map<String, URLDrawable> imageHashMap = null;private ImageGetter GetImageGetter() {if(imageHashMap == null) {imageHashMap = new HashMap<String, URLDrawable>(2);}if(imageGetter == null) {imageGetter = new ImageGetter() {//通过网络获取图片是一个耗时的操作,最好不要放在主线程中,否则容易引起阻塞。@Overridepublic Drawable getDrawable(String source) {String key = MD5.EncoderByMD5(source);URLDrawable urlDrawable = imageHashMap.get(key);if(urlDrawable == null) {urlDrawable = new URLDrawable();imageHashMap.put(key, urlDrawable);// get the actual sourceImageGetterAsyncTask.start(mContext, urlDrawable, source, handler);}        return urlDrawable;}};}return imageGetter;}private Handler handler = new Handler() {@Overridepublic void handleMessage(android.os.Message msg) {if(msg.what == ImageGetterAsyncTask.OnDrawablePrepared) {refreshNewsImage(msg);}}};private void refreshNewsImage(android.os.Message msg) {notifyDataSetChanged();}

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

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


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


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

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


2.2 content_imageview.xml:用于显示图片及图片说明,如“[图 1]”
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:layout_gravity="center_horizontal"    android:orientation="vertical"     android:paddingTop="5dp">    <ImageView        android:id="@+id/content_imageview_image"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center_horizontal"        android:adjustViewBounds="true"        android:baselineAlignBottom="true"        android:contentDescription="@string/xxx"        android:minHeight="30dp"        android:minWidth="30dp"        android:paddingBottom="5dp"        android:scaleType="centerInside" >    </ImageView>    <TextView        android:id="@+id/content_imageview_title"        style="@style/Style_NewsText_Content"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:gravity="center"        android:paddingBottom="5dp"        android:singleLine="false"        android:textColor="@color/text_b0b0b0"        android:textSize="@dimen/font_small" >    </TextView></LinearLayout>


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

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


2.4 MyImageTextView.jva:实现图文混排的类
public class MyImageTextView extends FrameLayout {//对应的viewprivate LinearLayout mContentView = null;//对应的数据private CharSequence mData = null;private String[] mImageUrl = null;private ImageView[] mImage = null;private int mImageBaseIndex = 1; //从[图 1]开始//是否支持超链接点击private Boolean supportMovementMethod = false;//是否显示图索引private Boolean showImageIndex = false;public MyImageTextView(Context context) {this(context, null);}public MyImageTextView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyImageTextView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}private void init() {setDrawingCacheEnabled(false);setClipChildren(false);mContentView = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.vertical_linearlayout, null);addView(mContentView);}/** * 设置待显示内容 * @param content */public void setText(CharSequence content) {try {if(TextUtils.isEmpty(content)) { return; }if(content.equals(mData)) { return; }mData = content;mContentView.removeAllViews(); // 首先清理之前加入的子视图int viewIndex = 0;int len = content.length();SpannableStringBuilder style = new SpannableStringBuilder(content);ImageSpan[] imgAry = style.getSpans(0, len, ImageSpan.class);if(imgAry == null || imgAry.length <= 0) {addTextView(content, viewIndex);return; }int pos = 0;int start = 0;         int end = 0;        ImageSpan img = null;        mImageUrl = new String[imgAry.length];        mImage = new ImageView[imgAry.length];        for(int i = 0; i < imgAry.length; i++) {        img = imgAry[i];        mImageUrl[i] = img.getSource();        start = style.getSpanStart(img);        if(pos < start) {        addTextView(style.subSequence(pos, start), viewIndex++);        }    end = style.getSpanEnd(img);    addImageView(i, viewIndex++);        pos = end + 1;        }                if(pos > 0 && pos < len) {        addTextView(style.subSequence(pos, len), viewIndex);        }                requestLayout();        invalidate(); //on a UI thread} catch(Exception ex) {}}private void addTextView(CharSequence text, int viewIndex) {TextView tv = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.content_textview, null);mContentView.addView(tv, viewIndex);tv.setText(text);if(supportMovementMethod) {changeLink(tv);}}private void addImageView(int index, int viewIndex) {View parent = LayoutInflater.from(getContext()).inflate(R.layout.content_imageview, null);mImage[index] = (ImageView) parent.findViewById(R.id.content_imageview_image);TextView tvTitle = (TextView)parent.findViewById(R.id.content_imageview_title);if(showImageIndex) {//这里的图片标题,也可以通过<img>标签的title/alt等属性分析出来tvTitle.setText("[图 " + Integer.toString(mImageBaseIndex + index) + "]");tvTitle.setVisibility(View.VISIBLE);} else {tvTitle.setVisibility(View.GONE);}mContentView.addView(parent, viewIndex);setImage(parent, mImage[index], mImageUrl[index]);}private void setImage(View parent, ImageView iv, String picUrl){if(picUrl != null && picUrl.trim().length() > 0) {parent.setVisibility(View.VISIBLE);iv.setImageResource(R.drawable.weibo_pic_loading);Size size = setPic(iv, picUrl);if(size.getHeight() > 0 && size.getWidth() > 0) {parent.requestLayout();}}else{parent.setVisibility(View.GONE);}}private Size setPic(ImageView logoView, String logoUrl) { //异步加载图片代码略    return XXXFileManager.getInstance().setImageBitmapWithMemoryCache(    getContext(), logoView, logoUrl, XXXFileManager.getImagetLrucache(),    getContext().getClass().getName(), false);}/** * 供图片下载完毕时调用 * @param fileURL */public void setPic(String fileURL) {if(mImage != null && mImageUrl != null && !TextUtils.isEmpty(fileURL)) {String source = null;for(int i = 0; i < mImageUrl.length && i < mImage.length; i++) {source = mImageUrl[i];if(!TextUtils.isEmpty(source)) {if(fileURL.equals(source)) {setPic(mImage[i], source);mImage[i].getParent().requestLayout();break;}}}}}/** * 设置是否支持超链接点击 */public void setSupportMovementMethod(Boolean supportMovementMethod) {this.supportMovementMethod = supportMovementMethod;}/** * 设置是否显示图索引 * @param showImageIndex */public void setShowImageIndex(Boolean showImageIndex) {this.showImageIndex = showImageIndex;}/** * 设置TextView超链接跳转 * @param tv */private void changeLink(TextView tv){tv.setMovementMethod(LinkMovementMethod.getInstance());CharSequence text = tv.getText();           if (text instanceof Spannable) {               int end = text.length();               Spannable sp = (Spannable) tv.getText();               URLSpan[] urls = sp.getSpans(0, end, URLSpan.class);            if(urls == null || urls.length <= 0) { return; }            SpannableStringBuilder style = new SpannableStringBuilder(text);            URLSpan[] urlsn = style.getSpans(0, end, URLSpan.class);            if(urlsn == null || urls.length != urlsn.length) { return; }                        //循环把链接发过去            URLSpan url = null;            for(int i = 0; i < urls.length && i < urlsn.length; i++) {            url = urls[i];                 MyURLSpan myURLSpan = new MyURLSpan(getContext(), url.getURL());                style.removeSpan(urlsn[i]);                style.setSpan(myURLSpan, sp.getSpanStart(url),                           sp.getSpanEnd(url), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);               }            tv.setText(style);        }}public int getImageCount() {int cnt = mImageBaseIndex;if(mImage != null && mImageUrl != null) {cnt += mImage.length;}return cnt;}public void setmImageBaseIndex(int baseIndex) {this.mImageBaseIndex = baseIndex;}public CharSequence getmData() {return mData;}}


2.5 MyURLSpan.java:定义一个可点击的Span,点击超链接时通过浏览器打开改网页/文件。
public class MyURLSpan extends ClickableSpan {private Context context = null;private String mUrl     = null;;public MyURLSpan(Context context,String url) {this.context = context;this.mUrl = url;}@Overridepublic void onClick(View widget) {if (URLUtil.isNetworkUrl(mUrl)) {XXXUtils.openMyWebBrowser(this.context,this.context.getResources().getString(R.string.newstext_hyperlink),this.mUrl);}}}


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

3.1 在xml中引用

        <ScrollView            android:id="@+id/XXX_ScrollView"            android:layout_width="fill_parent"            android:layout_height="fill_parent" >            <LinearLayout                android:id="@+id/XXX_Parent"                android:layout_width="fill_parent"                android:layout_height="wrap_content"                android:orientation="vertical"                android:visibility="gone" >                ……                <LinearLayout                    android:layout_width="fill_parent"                    android:layout_height="wrap_content"                    android:orientation="vertical"                    android:paddingBottom="5dp"                    android:paddingTop="10dp" >                    <XXX.textview.MyImageTextView                        android:id="@+id/XXX_Content"                        android:layout_width="fill_parent"                        android:layout_height="wrap_content"                        android:paddingLeft="10dp"                        android:paddingRight="10dp" >                    </XXX.textview.MyImageTextView>                    <RelativeLayout                        android:id="@+id/XXX_PayViewParent"                        android:layout_width="fill_parent"                        android:layout_height="wrap_content"                        android:paddingBottom="10dp"                        android:paddingTop="5dp"                        android:visibility="gone" >                        <XXX.textview.MyImageTextView                            android:id="@+id/XXX_PayContent"                            android:layout_width="fill_parent"                            android:layout_height="wrap_content"                            android:paddingLeft="10dp"                            android:paddingRight="10dp"                            android:visibility="gone" >                        </XXX.textview.MyImageTextView>                        <RelativeLayout                            android:id="@+id/XXX_PayLock"                            android:layout_width="fill_parent"                            android:layout_height="wrap_content"                            android:background="@drawable/xxx_paylock_bg"                            android:gravity="center_horizontal" >                            <ImageView                                android:id="@+id/xxx_Lock"                                android:layout_width="wrap_content"                                android:layout_height="wrap_content"                                android:layout_centerVertical="true"                                android:layout_marginRight="5dp"                                android:contentDescription="@string/xxx"                                android:padding="5dp"                                android:src="@drawable/xxx_paylock_icon" >                            </ImageView>                            ……                        </RelativeLayout>                    </RelativeLayout>                </LinearLayout>            </LinearLayout>        </ScrollView>


3.2 java代码,设置显示内容

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

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

private void initHandler() {this.mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case KLoadImageOver:itvFreeContent.setPic(msg.getData().getString("fileURL"));break;default:break;}}};}

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



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

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


0 0
原创粉丝点击