Android自定义控件之《自定义TextView(支持显示图片)》

有很多情况下,变态的需求,不得不让我们另辟蹊径,比如说,在一段文字里,愣是要让几处显示成别的颜色,或者字号要比其它文字大,或者小,其实这种情况下实现起来还算简单,只需要用到Html类的fromHtml()方法,加上html标签就可以实现,例如,把“AbnerMing是一个集才华与帅气于一身的帅哥”这句话中的AbnerMing设置成红色,字号稍大些,那么就可以这样做,Html.fromHtml(“AbnerMing是一个集才华与帅气于一身的帅哥”),如果font不能实现可以改用,把它赋值给TextView上,就能显示了;显然这样处理一些文字上的需求是可以的,但是图片如何显示呢?


对于一个简单的文字处理,用以上的方式就能实现,但是,如果服务端返回的带img标签,那么用以上的方式图片就会显示不出来了,就比如下面的这段标签:


 size='26px' color='#ff0000'>AbnerMing是一个集才华与帅气于一身的帅哥,请关注我的微信公众号吧……
‘<span微信公众号’ src='https://img-blog.csdn.net/20161230150153884?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWluZ18xNDc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast' style='width: 500px; height: 400px;'/>



如果想要显示出以上的效果,那么就需要自定义TextView了。

打开Html这个类,我们会看到一个方法:


/**
 * Returns displayable styled text from the provided HTML string with the legacy flags
 * {
@link #FROM_HTML_MODE_LEGACY}.
 *
 * 
@deprecated use {@link #fromHtml(String, int, ImageGetter, TagHandler)} instead.
 */
@Deprecated
public static Spanned fromHtml(String sourceImageGetter imageGetterTagHandler tagHandler) {
    
return fromHtml(sourceFROM_HTML_MODE_LEGACYimageGettertagHandler);
}


虽然说这个方法已经过时,但是并不耽误我们进行使用,很显然这个方法的用意是返回html标签的应有的字符串,那么显而可见是支持返回img标签的。


我们首先自定义一个TextView类,继承TextView,实现构造方法,写一个方法,用来设置内容:


/**
 * Parses String containing HTML to Android's Spannable format and displays
 * it in this TextView.
 *
 * 
@param 
html String containing HTML, for example: "Hello world!
"
 */
public void setHtmlFromString(String html) {
    Html.ImageGetter imgGetter = new UrlImageGetter(this, getContext());
    
setText(Html.fromHtml(htmlimgGetter, new HtmlTagHandler()));
    
setMovementMethod(LinkMovementMethod.getInstance());
}


这个方法就是我们用来设置内容的,和setText()方法的作用一样,html就是传入的文本,setText是为自己设置内容, setMovementMethod(LinkMovementMethod.getInstance()),是设置文本支持超链接作用,最重要的就是,Html.fromHtml(html, imgGetter, new HtmlTagHandler())html是设置的文本信息,imgGetter,我们继续追溯Html这个类,发现它是一个可用于实现的接口,并提供了一个返回Drawable的方法。


/**
 * Retrieves images for HTML 
<imgtags.
 */
public static interface ImageGetter {
    
/**
     * This method is called when the HTML parser encounters an
     * 
<imgtag.  The source 
argument is the
     * string from the "src" attribute; the return value should be
     * a Drawable representation of the image or 
null
     * for a generic replacement image.  Make sure you call
     * setBounds() on your Drawable if it doesn't already have
     * its bounds set.
     */
    
public Drawable getDrawable(String source);
}


这样就比较简单了,我们不妨继续写一个类,来实现Html.ImageGetter:


@Override
public Drawable getDrawable(String source) {
    final UrlDrawable urlDrawable = new UrlDrawable();
    
ImageLoader.getInstance().loadImage(source, new SimpleImageLoadingListener() {
        @Override
        
public void onLoadingComplete(String imageUriView viewBitmap loadedImage) {
            int imageWidth = loadedImage.getWidth();
            int 
imageHeight = loadedImage.getHeight();
            
// 计算缩放比例
            // 
取得想要缩放的matrix参数
            
Matrix matrix = new Matrix();
            
matrix.postScale(1.8f2.5f);
            
loadedImage = Bitmap.createBitmap(loadedImage00loadedImage.getWidth()loadedImage.getHeight()matrix,
                    true
);
            
urlDrawable.bitmap = loadedImage;
            
urlDrawable.setBounds(00loadedImage.getWidth()loadedImage.getHeight());
            
container.invalidate();
            
container.setText(container.getText())// 解决图文重叠
        
}
    });
    return 
urlDrawable;
}


值得注意的是,这里我用到了ImageLoader,这个方法就是取得标签的scr路径,进行填充,返回一个Drawable,在这里我们可以设置,图片的大小,比例等。


new HtmlTagHandler()是自己定义的一个类,实现了Html类的TgHandler接口:


/**
 * Is notified when HTML tags are encountered that the parser does
 * not know how to interpret.
 */
public static interface TagHandler {
    
/**
     * This method will be called whenn the HTML parser encounters
     * a tag that it does not know how to interpret.
     */
    
public void handleTag(boolean openingString tag,
                             
Editable outputXMLReader xmlReader);
}


定义这个类,主要是用来解析Html标签的,也是标签过滤器:


/**
 * Created by xiaoming.li on 2017/1/2.
 */
public class HtmlTagHandler implements Html.TagHandler {

    private int mListItemCount 0;
    private final 
Vector mListParents new Vector();

    private static class 
Code {
    }

    private static class Center {
    }

    @Override
    
public void handleTag(final boolean opening, final String tagEditable output,
                          final 
XMLReader xmlReader) {
        if (opening) {
            // opening tag
            
if (tag.equalsIgnoreCase("ul") || tag.equalsIgnoreCase("ol")
                    || tag.equalsIgnoreCase("dd")) {
                mListParents.add(tag);
                
mListItemCount 0;
            
else if (tag.equalsIgnoreCase("code")) {
                start(output, new Code());
            
else if (tag.equalsIgnoreCase("center")) {
                start(output, new Center());
            
}
        } else {
            // closing tag
            
if (tag.equalsIgnoreCase("ul") || tag.equalsIgnoreCase("ol")
                    || tag.equalsIgnoreCase("dd")) {
                mListParents.remove(tag);
                
mListItemCount 0;
            
else if (tag.equalsIgnoreCase("li")) {
                handleListTag(output);
            
else if (tag.equalsIgnoreCase("code")) {
                end(outputCode.class, new TypefaceSpan("monospace"), false);
            
else if (tag.equalsIgnoreCase("center")) {
                end(outputCenter.class,
                        new 
AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), true);
            
}
        }
    }

    /**
     * Mark the opening tag by using private classes
     *
     * 
@param 
output
     
@param mark
     
*/
    
private void start(Editable outputObject mark) {
        int len = output.length();
        
output.setSpan(marklenlenSpannable.SPAN_MARK_MARK);
    
}

    private void end(Editable outputClass kindObject repl, boolean paragraphStyle) {
        Object obj = getLast(outputkind);
        
// start of the tag
        
int where = output.getSpanStart(obj);
        
// end of the tag
        
int len = output.length();

        
output.removeSpan(obj);

        if 
(where != len) {
            // paragraph styles like AlignmentSpan need to end with a new line!
            
if (paragraphStyle) {
                output.append("\n");
                
len++;
            
}
            output.setSpan(replwherelenSpannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        
}
    }

    /**
     * Get last marked position of a specific tag kind (private class)
     *
     * 
@param 
text
     
@param kind
     
@return
     
*/
    
private Object getLast(Editable textClass kind) {
        Object[] objs = text.getSpans(0text.length()kind);
        if 
(objs.length == 0) {
            return null;
        
else {
            for (int i = objs.lengthi > 0i--) {
                if (text.getSpanFlags(objs[i - 1]) == Spannable.SPAN_MARK_MARK) {
                    return objs[i - 1];
                
}
            }
            return null;
        
}
    }

    private void handleListTag(Editable output) {
        if (mListParents.lastElement().equals("ul")) {
            output.append("\n");
            
String[] split = output.toString().split("\n");

            int 
lastIndex = split.length 1;
            int 
start = output.length() - split[lastIndex].length() - 1;
            
output.setSpan(new BulletSpan(15 mListParents.size())startoutput.length()0);
        
else if (mListParents.lastElement().equals("ol")) {
            mListItemCount++;

            
output.append("\n");
            
String[] split = output.toString().split("\n");

            int 
lastIndex = split.length 1;
            int 
start = output.length() - split[lastIndex].length() - 1;
            
output.insert(startmListItemCount ". ");
            
output.setSpan(new LeadingMarginSpan.Standard(15 mListParents.size())start,
                    
output.length()0);
        
}
    }



通过以上的代码,那么对于刚开始的那段文字,我们就可以展示出来了,具体显示会如下:


Android自定义控件之《自定义TextView(支持显示图片)》_第1张图片



你可能感兴趣的:(Android,Android自定义View)