不使用WebView加载HTML代码,用TextView加载HTML代码,加载网络图片,更改字体大小,更改字体颜色

最近接到一个需求:Android上不使用WebView 来加载  带标签式的网页,并且,要能加载和 文字样式

就像这样:


    
        title
    
    
        

paragraph

大概思路就是 找个组件来解析,然后用个容器加载出来。

所以,问题就是:

1.找到解析的组件

2.选择加载的容器

先看第一个问题,解析的组件

通过度娘了解到有个方法 fromHtml 可以解析网页代码

先看看fromHtml的源码吧:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package android.text;

import android.graphics.drawable.Drawable;
import org.xml.sax.XMLReader;

public class Html {

    ······

    /** @deprecated */
    @Deprecated
    public static Spanned fromHtml(String source) {
        throw new RuntimeException("Stub!");
    }

    public static Spanned fromHtml(String source, int flags) {
        throw new RuntimeException("Stub!");
    }

    /** @deprecated */
    @Deprecated
    public static Spanned fromHtml(String source, Html.ImageGetter imageGetter, Html.TagHandler tagHandler) {
        throw new RuntimeException("Stub!");
    }

    public static Spanned fromHtml(String source, int flags, Html.ImageGetter imageGetter, Html.TagHandler tagHandler) {
        throw new RuntimeException("Stub!");
    }


    public interface TagHandler {
        void handleTag(boolean var1, String var2, Editable var3, XMLReader var4);
    }

    public interface ImageGetter {
        Drawable getDrawable(String var1);
    }
    
    ······

}

然后Spanned这个类型是继承CharSequence的。CharSequence又是什么东西...我也是这样想的。如果对CharSequence不熟悉,那对String、StringBuffer、StringBuilder应该比较熟悉吧。其实CharSequence是个接口,String、StringBuffer、StringBuilder都是继承CharSequence接口的。

他们的关系,大概是这样:

不使用WebView加载HTML代码,用TextView加载HTML代码,加载网络图片,更改字体大小,更改字体颜色_第1张图片

那大概了解Spanned之后,来看看fromHtml的参数:String source, int flags, Html.ImageGetter imageGetter, Html.TagHandler tagHandler.

source即要解析的网页代码

flags即html块元素之间换行符分隔数量:(API>24)

  • FROM_HTML_MODE_COMPACT:html块元素之间使用一个换行符分隔
  • FROM_HTML_MODE_LEGACY:html块元素之间使用两个换行符分隔

imageGetter即解析图片

tagHandler即解析其他标签,甚至可以是自定义的标签

因为需要解析网络图片,且API>24的局限,所以 我们用fromHtml(String source, Html.ImageGetter imageGetter, Html.TagHandler tagHandler)这个方法就行。

第二个问题选择加载的容器,选择的是TextView,TextView既能显示文本,又能显示图片。

好的,现在试一下fromHtml这个方法,先加入第一个参数String source:

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Handler mHandler = new Handler();
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findView();
        init();
    }

    public void findView() {
        textView = findViewById(R.id.tv);
    }

    public void init() {

        mHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message message) {
                if (message.what == 0x101) {
                    textView.setText((CharSequence) message.obj);
                }
                return false;
            }
        });
        final String html = "文字
" + ""; Message msg = Message.obtain(); CharSequence charSequence = Html.fromHtml(html, null, null); msg.what = 0x101; msg.obj = charSequence; mHandler.sendMessage(msg); } }

再看看效果图

不使用WebView加载HTML代码,用TextView加载HTML代码,加载网络图片,更改字体大小,更改字体颜色_第2张图片

已经可以解析HTML代码了,继续加入第二个参数 Html.ImageGetter imageGetter:

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findView();
        init();
    }

    public void findView() {
        textView = findViewById(R.id.tv);
        
    }

    public void init() {
        //设置handler 回调
        mHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message message) {
                if (message.what == 0x101) {//当message.what 一样的时候,说明message已传入
                    textView.setText((CharSequence) message.obj);
                }
                return false;
            }
        });
        final String html = "文字
" + ""; new Thread(new Runnable() {//图片是网络请求,所以新建一个线程来执行 Message msg = Message.obtain(); @Override public void run() { //设置图片长宽 Html.ImageGetter imageGetter = new Html.ImageGetter() { @Override public Drawable getDrawable(String s) { Drawable drawable = getImageFromNetwork(s); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); return drawable; } }; CharSequence charSequence = Html.fromHtml(html, imageGetter, null); msg.what = 0x101; msg.obj = charSequence; mHandler.sendMessage(msg); } }).start(); } //获取网络图片的方法,返回drawable public Drawable getImageFromNetwork(String imageUrl) { URL myFileUrl = null; Drawable drawable = null; try { myFileUrl = new URL(imageUrl); } catch (MalformedURLException e) { e.printStackTrace(); } try { HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection(); conn.setDoInput(true); conn.connect(); InputStream is = conn.getInputStream(); drawable = Drawable.createFromStream(is, null); is.close(); } catch (IOException e) { e.printStackTrace(); } return drawable; } }

当然,AndroidMainfest.xml里的里要加上网络权限,不然会报错

来看看效果图:

 

不使用WebView加载HTML代码,用TextView加载HTML代码,加载网络图片,更改字体大小,更改字体颜色_第3张图片 

什么鬼...图片怎么这么小...那我要怎么看清妹纸 

在imageGetter里,不是有个setBounds吗?这个应该就是图片的宽高了

修改一下imageGetter

Html.ImageGetter imageGetter = new Html.ImageGetter() {
                    @Override
                    public Drawable getDrawable(String s) {
                        Drawable drawable = getImageFromNetwork(s);
                        //获取屏幕的宽高
                        int screenWidth = getWindowManager().getDefaultDisplay().getWidth();
                        int screenHeight = getWindowManager().getDefaultDisplay().getHeight();
                        //设置图片的宽 为屏幕宽的一半
                        int w = screenWidth / 2;
                        //根据图片比例,设置图片高度,如果不进行类型转换,被除数为零 报错
                        int h = (int) (w / ((float) drawable.getIntrinsicWidth() / drawable.getIntrinsicHeight()));
                        
                        drawable.setBounds(0, 0, w, h);

                        return drawable;
                    }
                };

来看看现在的效果

不使用WebView加载HTML代码,用TextView加载HTML代码,加载网络图片,更改字体大小,更改字体颜色_第4张图片

现在处理,基本OK了

接下来就是文字的处理的

经过尝试,不能处理CSS样式。也就是说style样式都不适用

如果要更改文字颜色,使用example就可以了

测试了一下,不是所有标签都能解析,我把能解析的标签贴在下方


换行符

段落标签
分区标签
强调文本
粗体文本
斜体显示
斜体显示
斜体显示
斜体显示
大号字体
小号字体
字体标签
标签定义块引用
字体显示为等宽字体
超链接
下划线
上标
下标

~

标题标签
图片标签

然后我在试标签的时候,发现color和face两个属性有效,而size无效

如果直接使用textView.setTextSize(size);又显得太笨重,太蠢了。而且 容器设置字体大小 和网页字体大小 是两回事。

这时候,我们可以看看fromHtml的最后一个参数,Html.TagHandler tagHandler.

//设置自定义标签
Html.TagHandler tagHandler = new Html.TagHandler() {
    @Override
    public void handleTag(boolean b, String tag, Editable editable, XMLReader xmlReader) {
        Log.d(TAG, "handleTag: " + tag);
        if (b) {
            if (tag.equalsIgnoreCase("size")) {
                Log.d(TAG, "handleTag: start");
            }
        } else {
            if (tag.equalsIgnoreCase("size")) {
                Log.d(TAG, "handleTag: end");
            }
        }

     }
};

然后把taghandler放到方法内

CharSequence charSequence = Html.fromHtml(html_1, imageGetter, tagHandler);

运行发现:

不使用WebView加载HTML代码,用TextView加载HTML代码,加载网络图片,更改字体大小,更改字体颜色_第5张图片

能捕捉到(其他标签也打印了,是因为if上方还有一个logd)

既然能捕捉到自定义的

那接下来就好办了,只需要放大字体就好

//自定义标签
    public void handlerStartSIZE(Editable output, XMLReader xmlReader) {
        if (startIndex == null) {
            startIndex = new Stack<>();
        }
        startIndex.push(output.length());
        if (propertyValue == null) {
            propertyValue = new Stack<>();
        }

        propertyValue.push(getProperty(xmlReader, "value"));//value可自己设置

    }
//自定义标签
    public void handlerEndSIZE(Editable output) {
        if (!isEmpty(propertyValue)) {
            try {
                int value = Integer.parseInt(propertyValue.pop());
                output.setSpan(new AbsoluteSizeSpan(sp2px(MyApplication.getContext(), value)), startIndex.pop(), output.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

效果图:

不使用WebView加载HTML代码,用TextView加载HTML代码,加载网络图片,更改字体大小,更改字体颜色_第6张图片

这样,就就可以改变文字的大小颜色了

最后附上关键代码的源码

package com.example.user.textviewloadimage;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.Html;
import android.text.Spanned;
import android.text.style.AbsoluteSizeSpan;
import android.util.Log;
import android.util.TypedValue;
import android.widget.TextView;

import org.xml.sax.XMLReader;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.Stack;

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Handler mHandler = new Handler();
    private String TAG = "TAG";
    private Stack startIndex;
    private Stack propertyValue;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findView();
        init();
    }

    public void findView() {
        textView = findViewById(R.id.tv);

    }

    public void init() {
        //设置handler 回调
        mHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message message) {
                if (message.what == 0x101) {//当message.what 一样的时候,说明message已传入
                    textView.setText((CharSequence) message.obj);
                }
                return false;
            }
        });
        final String html = "文字
" + ""; final String html_1 = "文字
" + "文字
" + ""; new Thread(new Runnable() {//图片是网络请求,所以新建一个线程来执行 Message msg = Message.obtain(); @Override public void run() { //设置图片长宽 Html.ImageGetter imageGetter = new Html.ImageGetter() { @Override public Drawable getDrawable(String s) { Drawable drawable = getImageFromNetwork(s); //获取屏幕的宽高 int screenWidth = getWindowManager().getDefaultDisplay().getWidth(); int screenHeight = getWindowManager().getDefaultDisplay().getHeight(); //设置图片的宽 为屏幕宽的一半 int w = screenWidth / 2; //根据图片比例,设置图片高度 int h = (int) (w / ((float) drawable.getIntrinsicWidth() / drawable.getIntrinsicHeight())); drawable.setBounds(0, 0, w, h); return drawable; } }; //设置自定义标签 Html.TagHandler tagHandler = new Html.TagHandler() { @Override public void handleTag(boolean b, String tag, Editable editable, XMLReader xmlReader) { Log.d(TAG, "handleTag: " + tag); if (b) { if (tag.equalsIgnoreCase("size")) { handlerStartSIZE(editable, xmlReader); } } else { if (tag.equalsIgnoreCase("size")) { handlerEndSIZE(editable); } } } }; CharSequence charSequence = Html.fromHtml(html_1, imageGetter, tagHandler); msg.what = 0x101; msg.obj = charSequence; mHandler.sendMessage(msg); } }).start(); } //自定义标签----开始---- public void handlerStartSIZE(Editable output, XMLReader xmlReader) { if (startIndex == null) { startIndex = new Stack<>(); } startIndex.push(output.length()); if (propertyValue == null) { propertyValue = new Stack<>(); } propertyValue.push(getProperty(xmlReader, "value")); } //自定义标签====结束==== public void handlerEndSIZE(Editable output) { if (!isEmpty(propertyValue)) { try { int value = Integer.parseInt(propertyValue.pop()); output.setSpan(new AbsoluteSizeSpan(sp2px(MyApplication.getContext(), value)), startIndex.pop(), output.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } catch (Exception e) { e.printStackTrace(); } } } //获取html标签的 value private String getProperty(XMLReader xmlReader, String property) { try { Field elementField = xmlReader.getClass().getDeclaredField("theNewElement"); elementField.setAccessible(true); Object element = elementField.get(xmlReader); Field attsField = element.getClass().getDeclaredField("theAtts"); attsField.setAccessible(true); Object atts = attsField.get(element); Field dataField = atts.getClass().getDeclaredField("data"); dataField.setAccessible(true); String[] data = (String[]) dataField.get(atts); Field lengthField = atts.getClass().getDeclaredField("length"); lengthField.setAccessible(true); int len = (Integer) lengthField.get(atts); for (int i = 0; i < len; i++) { if (property.equals(data[i * 5 + 1])) { return data[i * 5 + 4]; } } } catch (Exception e) { e.printStackTrace(); } return null; } //判断是否为空 public static boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } public static int sp2px(Context context, float spValue) { return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.getResources().getDisplayMetrics()) + 0.5f); } public Drawable getImageFromNetwork(String imageUrl) { URL myFileUrl = null; Drawable drawable = null; try { myFileUrl = new URL(imageUrl); } catch (MalformedURLException e) { e.printStackTrace(); } try { HttpURLConnection conn = (HttpURLConnection) myFileUrl.openConnection(); conn.setDoInput(true); conn.connect(); InputStream is = conn.getInputStream(); drawable = Drawable.createFromStream(is, null); is.close(); } catch (IOException e) { e.printStackTrace(); } return drawable; } }

参考资料:

1.Android TextView(同时显示图片+文字) https://www.cnblogs.com/zhou-guobao/p/4651192.html

2.Android 在TextView 中显示图片的4种方式 https://blog.csdn.net/u012724237/article/details/79010741

3.Android 详解实现TextView加载带图片标签的Html并按比例缩放 https://blog.csdn.net/shaohx0518/article/details/50129511

4.Html.fromHtml()中Html.TagHandler()的使用 https://blog.csdn.net/baidu_34012226/article/details/53301047

 

你可能感兴趣的:(学习之路,html,fromHtml,imageGetter,tagHandler)