文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android使用WebView实现离线阅读功能

2024-04-02 19:55

关注

1、先看效果图,加载动画:

加载完成,注意当前为飞行模式!

2、使用

1)、让你的javabean实现OffLineLevelItem接口,因为我的这个离线阅读支持多级下载,比如Demo中的每个频道下面的第一页item都可以缓存。


package com.zgh.offlinereader;

import java.util.List;

public interface OffLineLevelItem  {
    //是否有下一级
    boolean haveNextLevel();
    //内容url
    String getWebUrl();
    //下一级的url
    String getNextLevelListUrl();
    //生成下一级
    List<OffLineLevelItem> getNextLevelList(String jsonStr);
}

public class Channel implements OffLineLevelItem {
    String title;
    String url;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public boolean haveNextLevel() {
        return true;
    }

    @Override
    public String getWebUrl() {
        return null;
    }

    @Override
    public String getNextLevelListUrl() {
        return url;
    }

    @Override
    public List<OffLineLevelItem> getNextLevelList(String jsonStr) {
        List<OffLineLevelItem> items = GsonUtil.jsonToBeanList(jsonStr, NewsItem.class);
        return items;
    }
}

2)、初始化


OfflineReaderServer.init(this, getCacheDir(), new MyFirstLevel(),new WaterWaveProgressUI(this));

3)、启动


 @Override
    public void onClick(View v) {
        Intent intent=new Intent(this, OfflineReaderServer.class);
        startService(intent);
    }

4)、记得在你的webview使用前调用


    //设置缓存目录
    WebViewHelper.setWebViewConfig(webView);

就这么简单!

实现

首先我们为什么要使用webview实现离线阅读,因为简单。webview自带的缓存机制可以实现图片,js,css的缓存。不然你自己得实现数据库,html下载,js下载,css保存,html的拼装。下面我将讲解一些webview设置缓存,实现多级下载,webview遍历url,webview显示完成监听。

1.WebView设置缓存

这一部分比较简单,主要是缓存目录的设置,然后设置缓存模式为:


WebSettings.LOAD_CACHE_ELSE_NETWORK

这种模式下webview会优先加载本地缓存,如果没有缓存的话再加载网络。


        mWebView.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
        // 建议缓存策略为,判断是否有网络,有的话,使用LOAD_DEFAULT,无网络时,使用LOAD_CACHE_ELSE_NETWORK

        mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); // 设置缓存模式
        // 开启DOM storage API 功能
        mWebView.getSettings().setDomStorageEnabled(true);
        // 开启database storage API功能
        mWebView.getSettings().setDatabaseEnabled(true);
        // String cacheDirPath = getFilesDir().getAbsolutePath()
        //         + APP_CACHE_DIRNAME;
        String cacheDirPath = ConfigUtil.getCacheDir()
                + APP_CACHE_DIRNAME;
        Log.i(TAG, "cachePath=" + cacheDirPath);
        // 设置数据库缓存路径
        mWebView.getSettings().setDatabasePath(cacheDirPath); // API 19 deprecated
        // 设置Application caches缓存目录
        mWebView.getSettings().setAppCachePath(cacheDirPath);
        // 开启Application Cache功能
        mWebView.getSettings().setAppCacheEnabled(true);
        mWebView.getSettings().setAppCacheMaxSize(MAX_SIZE);

2.多级缓存

我的项目中需要将每个频道的首页中的每个item都缓存下来,所以涉及到多级缓存于是我设计了一个接口在离线阅读的时候最重要的是拿到叶子节点也就是每个item的url地址,如果是每叶子节点也就是haveNextLevel()返回true的时候就调用getNextLevelListUrl获取下一级的url,一般都是Jason字符串,再把json字符串传入getNextLevelList()方法获取下一级,如果到达叶子节点,则调用getWebUrl()获取url地址保存在一个集合中,当所有的url都获取以后,就开始用webview遍历url实现缓存。


public interface OffLineLevelItem  {
    //是否有下一级
    boolean haveNextLevel();
    //内容url
    String getWebUrl();
    //下一级的url
    String getNextLevelListUrl();
    //生成下一级
    List<OffLineLevelItem> getNextLevelList(String jsonStr);
}

频道的javabean


public class Channel implements OffLineLevelItem {
    String title;
    String url;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public boolean haveNextLevel() {
        return true;
    }

    @Override
    public String getWebUrl() {
        return null;
    }

    @Override
    public String getNextLevelListUrl() {
        return url;
    }

    @Override
    public List<OffLineLevelItem> getNextLevelList(String jsonStr) {
        List<OffLineLevelItem> items = GsonUtil.jsonToBeanList(jsonStr, NewsItem.class);
        return items;
    }
}

item的javabean


public class NewsItem implements OffLineLevelItem{
    String title;
    String url;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public String toString() {
        return title;
    }

    @Override
    public boolean haveNextLevel() {
        return false;
    }

    @Override
    public String getWebUrl() {
        return url;
    }

    @Override
    public String getNextLevelListUrl() {
        return null;
    }

    @Override
    public List<OffLineLevelItem> getNextLevelList(String jsonStr) {
        return null;
    }
}

当然为了获取到频道列表需要一个第一级的目录,而这个目录在初始化的时候就设置进去了。


public class MyFirstLevel implements OffLineLevelItem {
    @Override
    public boolean haveNextLevel() {
        return true;
    }

    @Override
    public String getWebUrl() {
        return null;
    }

    @Override
    public String getNextLevelListUrl() {
        return "raw://news_list";
    }

    @Override
    public List<OffLineLevelItem> getNextLevelList(String jsonStr) {
        List<OffLineLevelItem> items = GsonUtil.jsonToBeanList(jsonStr, Channel.class);
        return items;
    }
}

3.使用WebView遍历URL,我原来的思路是给webview设置WebViewClient然后重写onPageFinished方法,在这个方法中获取下一个需要换成的url,然后再调用webview.loadurl()结果是很多页面加载出来是空的。而且在Android4.4以上onPageFinished会调用两次

于是乎,我重写了WebView的OnDraw()方法,在OnDraw()方法里设置了一个监听回调,但是由于我的WebView是在Service中创建的所以ondraw方法根本不会调用,但是这难得的我吗?,呵呵,于是我在service的onCreat方法中使用WindowManger将webview添加到屏幕,长宽都是一个像素


   @Override
    public void onCreate() {
        super.onCreate();
        if (!haveInit) {
            throw new RuntimeException("请先调用init()方法,初始化OfflineReaderServer");
        }
        windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        params=new WindowManager.LayoutParams();
        params.type = WindowManager.LayoutParams.TYPE_TOAST;
        params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        params.gravity = Gravity.LEFT | Gravity.TOP;
        params.width = 1;
        params.height = 1;
        initWebView();
        windowManager.addView(mWebView,params);
    }

结果还是很明显的大部分的页面都能缓存下来,但是任然有部分页面是空白的,后来发现webview的OnDraw()方法会多次持续,webview的页面加载时间隙的,我通过getContentHeight()判断内容高度来实现显示完成的监听,结果任然不理想。于是我最终版是这样的



public class LoadWebView extends WebView {
    private boolean isRendered = false;
    private static final int MSG_FINISH=1;
    private static final int MIN_CONTENT_HEIGHT=1000;
    public LoadWebView(Context context) {
        this(context, null);
    }
    public LoadWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    private int contentHeight=MIN_CONTENT_HEIGHT;
    Handler handler=new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==MSG_FINISH) {
                if (finishListenter != null) {
                    finishListenter.onFinish();
                    contentHeight=MIN_CONTENT_HEIGHT;
                }
            }
        }
    };
    @Override
    protected void onDraw(Canvas canvas) {
        //与上一次的contentHeight比较,如果比上一次大,说明还在加载
        if(getContentHeight()>=contentHeight){
            //更新contentHeight
            contentHeight=getContentHeight();
            //取消消息
            handler.removeMessages(MSG_FINISH);
            //延迟200ms发送,如果在200ms内webview又加载了则这条消息会被取消,知道webview加载完成,
            //这条消息会被发送,所以每离线一个页面有200ms的延迟,但是与功能相比这点是可以接受的。
            handler.sendEmptyMessageDelayed(MSG_FINISH,200);
        }
    }

    public interface OnLoadFinishListenter{
        void onFinish();
    }

    private OnLoadFinishListenter finishListenter;
    public void setFinishListenter(OnLoadFinishListenter listenter){
        finishListenter=listenter;
    }
}

3、进度提示

为了让用户知道离线的进度我抽取出了一个接口


public interface OffLineProgressUI {
    void showProgress();

    void closeProgress();

    void updateProgress(int progress);
}

并默认实现了一个水波纹的进度球

设置进度提示有两种方式,一种是在初始化的时候设置


OfflineReaderServer.init(this, getCacheDir(), new MyFirstLevel(),new WaterWaveProgressUI(this));

还有一种是调用OfflineReaderServer的setProgressUI方法


   public static void setProgressUI(@NonNull OffLineProgressUI progressUI) {
        sProgressUI = progressUI;
    }

源码地址:

https://github.com/zhuguohui/OffLineReaderDem

以上就是Android使用WebView实现离线阅读功能的详细内容,更多关于Android 实现离线阅读功能的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯