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

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()判断内容高度来实现显示完成的监听,结果任然不理想。于是我最终版是这样的

/**
 *  可以监听显示完成的webview
 */
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 实现离线阅读功能的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android 如何从零开始写一款书籍阅读器的示例

    一款书籍阅读器,需要以下功能才能说的上比较完整: 文字页面展示,即书页: 页面之间的跳转动画,即翻页动作: 能够在每一页上记录阅读进度,即书签: 能够自由选择文字并标注,即笔记: 能够设置一些属性,如屏幕亮度,字体大小,主体颜色等,即个性化设置. 书籍阅读器 这篇文章带来的就是如何打造这么一款阅读器.(由于整体代码量比较大,所以我只能说说我的实现思路再加上部分的核心代码来说明,不会有太多的代码展示.) 翻页动作--搭建整个阅读器的框架 在阅读器上的翻页动作无外乎仿真和平移这两种动画,翻页时需要准

  • Android实现阅读APP平移翻页效果

    自己做的一个APP需要用到翻页阅读,网上看过立体翻页效果,不过bug太多了还不兼容.看了一下多看阅读翻页是采用平移翻页的,于是就仿写了一个平移翻页的控件.效果如下: 在翻页时页面右边缘绘制了阴影,效果还不错.要实现这种平移翻页控件并不难,只需要定义一个布局管理页面就可以了.具体实现上有以下难点: 1.循环翻页,页面的重复利用. 2.在翻页时过滤掉多点触碰. 3.采用setAdapter的方式设置页面布局和数据. 下面就来一一解决这几个难点.首先看循环翻页问题,怎么样能采用较少的页面实现这种翻页呢

  • 在Android环境下WebView中拦截所有请求并替换URL示例详解

    需求背景 接到这样一个需求,需要在 WebView 的所有网络请求中,在请求的url中,加上一个xxx=1的标志位. 例如 http://www.baidu.com 加上标志位就变成了 http://www.baidu.com?xxx=1 寻找解决方案 从 Android API 11 (3.0) 开始,WebView 开始在 WebViewClient 内提供了这样一条 API ,如下: public WebResourceResponse shouldInterceptRequest(Web

  • Android WebView如何判定网页加载的错误

    一 前言 在WebView加载网页的过程中,有时会页面加载不出来的情况,如下: 那么如何知道网页加载错误了呢? 二 解决方法 webview.webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) Log.d(TAG, "onPageFinished: ") } overri

  • Android编程实现小说阅读器滑动效果的方法

    本文实例讲述了Android编程实现小说阅读器滑动效果的方法.分享给大家供大家参考,具体如下: 看过小说都知道小说阅读器翻页有好多种效果,比如仿真翻页,滑动翻页,等等.由于某种原因,突然想写一个简单点的滑动翻页效果.在这里写出来也没有什么意图,希望大家可以根据这个效果举一反三,写出其他的效果.图就不上了. 下面是代码:大家理解onTouch事件即可 package com.example.testscroll.view; import android.content.Context; impor

  • android仿新闻阅读器菜单弹出效果实例(附源码DEMO下载)

    开发中碰到问题之后实现的,觉得可能有的开发者用的到或则希望独立成一个小功能DEMO,所以就放出来这么一个DEMO. 原本觉得是最后完成后发网站客户端的,可是这样体现不出一个功能一个功能的分析实现效果,而且周期时间长,所以就完成一部分,发一部分,敬请谅解. 下面的菜单弹出效果在很多的新闻阅读器上都有,比如今日头条.360新闻等. 其实这个实现起来很简单,看其效果,其实就是一个PopupWindow,之后设定相应postion的按钮点击属性,之后获取按钮的位置,给它设置动画显示消失就可以出现了. 下

  • Android webView字体突然变小的原因及解决

    背景 最近,端内在做 webView 统一的时候,个性签名中的 WebView 替换为 CustomWebView 之后,发现字体突然变小. 一开始不知道是什么原因,通过二分法查找最近的提交,排查之后,发现是 SignatureWebView 的继承关系从 WebView 修改为 CustomWebView.revert 之后就正常了. 于是,我问自己,为什么会这样呢? 原因分析 我们知道,WebViewSetting 里面是可以修改 WebView 的一些默认设置的. 阅读官方文档,发现 se

  • 解决Android webview设置cookie和cookie丢失的问题

    Android页面嵌套了一个h5,H5页面内部有用户登陆页面,发现h5页面的登陆功能无法使用,一直登陆失败.和web那边商量一会,发现js写入的cookie丢失了.所有需要Android这边在重写写入一次. mWebView = view.findViewById(R.id.mall_view); settings = mWebView.getSettings(); settings.setJavaScriptEnabled(true); settings.setLoadsImagesAutom

  • Android实现阅读进度记忆功能

    本文实例为大家分享了Android控件WebView实现保存阅读进度的具体代码,供大家参考,具体内容如下 用户提了一个要求,要求保存他的阅读进度,然后在他下次阅读的时候可以继续阅读,然后动手实现了一下,是这样的. 我用的控件是WebView public class WebViewClientEmb extends WebViewClient { // 在WebView中而不是系统默认浏览器中显示页面 @Override public boolean shouldOverrideUrlLoadi

  • Android 中 WebView 的基本用法详解

    加载 URL (网络或者本地 assets 文件夹下的 html 文件) 加载 html 代码 Native 和 JavaScript 相互调用 加载网络 URL webview.loadUrl(https://www.baidu.com/); 加载 assets 下的 html 文件 webview.loadUrl(file:///android_asset/test.html); 加载 html 代码 // 两个代码差不多 // 偶尔出现乱码 webview.loadData(); // 比

  • Android 解决WebView多进程崩溃的方法

    问题 在android 9.0系统上如果多个进程使用WebView需要使用官方提供的api在子进程中给webview的数据文件夹设置后缀: WebView.setDataDirectorySuffix(suffix); 否则将会报出以下错误: Using WebView from more than one process at once with the same data directory is not supported. https://crbug.com/558377 1 com.a

  • android阅读器长按选择文字功能实现代码

    前言: 有时候我们需要实现长按选择文字功能,比如阅读器一般都有这个功能,有时候某个自定义控件上可能就有这种需求,如何实现呢?正好最近还算闲,想完善一下自己写的那个轻量级的txt文件阅读器(比如这个长按选择文字的功能就想加进去).于是花了两三天时间,实现了这个功能,效果还是不错的. 首先先看看效果图吧: 授人以鱼不如授人以渔,下面具体实现原理的教程. 1.实现原理 原理其实也不难,简单总结就是:绘制文字时把显示的文字的坐标记录下来(记录文字的左上右上左下右下四个点坐标),作用就是为了计算滑动范围.

随机推荐