使用WebView实现离线阅读

来源:互联网 发布:北京房山网络职业学院 编辑:程序博客网 时间:2024/06/02 02:41

1.先看效果

加载动画

这里写图片描述

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

这里写图片描述

2.使用

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

package com.zgh.offlinereader;import java.util.List;/** * Created by zhuguohui on 2016/6/7. */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的页面加载时间隙的,于是参考这篇文章如何监听WebView显示事件,我通过getContentHeight()判断内容高度来实现显示完成的监听,结果任然不理想。于是我最终版是这样的

/** *  可以监听显示完成的webview * Created by zhuguohui on 2016/6/24. */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.进度提示

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

/** * Created by zhuguohui on 2016/6/8. */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;    }

更多内容请看我的Demo。

Demo

https://github.com/zhuguohui/OffLineReaderDemo

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 实验室授权签字人考不过怎么办 万和热水器排污口漏水怎么办 万和热水器水箱漏水怎么办 军训鞋大了怎么办妙招 麽稍神经不好受怎么办 绒面高跟鞋太硬怎么办 新买的鞋子太硬怎么办 鞋底太硬脚掌疼怎么办 耐克鞋子走路吱吱响怎么办 两只鞋子有色差怎么办 劳保鞋鞋底太硬怎么办 在学校校服丢了怎么办 高中没进重点班怎么办 孩子不懂学不想学怎么办 孩子小学数学学不懂怎么办 入学籍系统提交了没分班怎么办 被监考老师抓了作弊怎么办 作弊被老师抓到了怎么办? 中考作弊给抓到怎么办 考试作弊证据没得监控怎么办 考试作弊被领导发现了怎么办 黄冈讲课视频看不了怎么办 高中学校不给转学籍怎么办 兴山香溪大酒店欠钱不还怎么办 黑坑青鱼滑口怎么办 不交物业费物业怎么办 车牌刮了一点漆怎么办 电脑光驱线坏了怎么办 做系统不读光驱怎么办 光盘放进电脑没反应怎么办 不服省高院裁定维持原判怎么办 咖啡和酒一起喝怎么办 跟法官联系不上怎么办 四维没有预约到怎么办 钥匙锁在车里怎么办 如果孩子很叛逆骂人打人怎么办 错过了今年规培怎么办 枣木怎么办才能搞直了 高中生和家里闹意见离家出走怎么办 校长信箱实名举报了怎么办 枣子吃多了胀气怎么办