• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

Gloading: 用Adapter模式深度解耦Android App中全局加载中、加载失败及空数据视图。按 ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

Gloading

开源软件地址:

https://gitee.com/luckybilly/Gloading

开源软件介绍:

Gloading

JavaDocs | Demo下载

最新版本: Download

  • 深度解耦Android App中全局加载中、加载失败及空数据视图,为组件化改造过程中的解耦长征助力
  • 分离全局加载状态视图的实现和使用
  • 不需要在每个页面的布局文件中额外添加加载状态视图
  • 可用于Activity、Fragment,也可用于为某个View显示加载状态,还可用于各种列表Item(ListView、RecyclerView等)
  • 轻量级:只有一个java文件,没有任何其它依赖,不到300行,其中注释占100+行,aar仅6K
  • 兼容性好:
    • android系统版本从api 1开始兼容
    • 兼容绝大多数第三方炫酷的LoadingView(在Adapter中将其作为View提供给Gloading)

演示

为Activity添加加载状态

加载成功加载失败
点击重试
加载成功
无数据
个别页面使用特殊的Loading视图

为View添加加载状态

单个View多个View用于GridView用于RecyclerView
并且不显示文字

背景

Loading动画几乎每个Android App中都有。

一般在需要用户等待的场景,显示一个Loading动画可以让用户知道App正在加载数据,而不是程序卡死,从而给用户较好的使用体验。

同样的道理,当加载的数据为空时显示一个数据为空的视图、在数据加载失败时显示加载失败对应的UI并支持点击重试会比白屏的用户体验更好一些。

加载中、加载失败、空数据的UI风格,一般来说在App内的所有页面中需要保持一致,也就是需要做到全局统一。

传统的做法

  1. 定义一个(或多个)显示不同加载状态的控件或者xml布局文件(例如:LoadingView
  2. 每个页面的布局中都写上这个view
  3. BaseActivity/BaseFragment中封装LoadingView的初始化逻辑,并封装加载状态切换时的UI显示逻辑,暴露给子类以下方法:
    • void showLoading(); //调用此方法显示加载中的动画
    • void showLoadFailed(); //调用此方法显示加载失败界面
    • void showEmpty(); //调用此方法显示空页面
    • void onClickRetry(); //子类中实现,点击重试的回调方法
  4. BaseActivity/BaseFragment的子类中可通过上一步的封装比较方便地使用加载状态显示功能

这种使用方式耦合度太高,每个页面的布局文件中都需要添加LoadingView,使用起来不方便而且维护成本较高,一旦UI设计师需要更改布局,修改起来成本较高。

好一点的封装方法

  1. 定义一个(或多个)显示不同加载状态的控件或者xml布局文件(例如:LoadingView
  2. 定义一个工具类(LoadingUtil)来管理LoadingView,不同状态显示不同的UI(或者在多个View之间切换显示)
  3. BaseActivity/BaseFragment中对LoadingUtil的使用进行封装,暴露给子类以下方法:
    • void showLoading(); //调用此方法显示加载中的动画
    • void showLoadFailed(); //调用此方法显示加载失败界面
    • void showEmpty(); //调用此方法显示空页面
    • void onClickRetry(); //子类中实现,点击重试的回调方法
    • abstract int getContainerId(); //子类中实现,LoadingUtil动态创建LoadingView并添加到该方法返回id对应的控件中
  4. BaseActivity/BaseFragment的子类中可通过上一步的封装比较方便地使用加载状态显示功能

这种封装的好处是通过封装动态地创建LoadingView并添加到指定的父容器中,让具体页面无需关注LoadingView的实现,只需要指定在哪个容器中显示即可,很大程度地进行了解耦。如果公司只在一个App中使用,这基本上就够了。

但是,这种封装方式还是存在耦合:页面与它所使用的LoadingView仍然存在绑定关系。如果需要复用到其它App中,因为每个App的UI风格可能不同,对应的LoadingView布局也可能会不一样,要想复用必须先将页面与LoadingView解耦。

如何解耦?

梳理一下我们需要实现的效果

  • 页面的LoadingView可切换,且不需要改动页面代码
  • 页面中可指定LoadingView的显示区域(例如导航栏Title不希望被LoadingView覆盖)
  • 支持在Fragment中使用
  • 支持加载失败页面中点击重试
  • 兼容不同页面显示的UI有细微差别(例如提示文字可能不同)

2. 确定思路

说到View的解耦,很容易联想到Android系统中的AdapterView(我们常用的GridView和ListView都是它的子类)及support包里提供的ViewPager、RecyclerView等,它们都是通过Adapter来解耦的,将自身的逻辑与需要动态变化的子View进行分离。我们也可以按照这个思路来解耦LoadingView

使用Gloading来解耦

Gloading是一个基于Adapter思路实现的深度解耦App中全局LoadingView的轻量级工具(只有一个java文件,不到300行,其中注释占100+行,aar仅6K)

1、 依赖Gloading

compile 'com.billy.android:gloading:1.0.0'

2、 创建Adapter,在getView方法中实现创建各种状态视图(加载中、加载失败、空数据等)的逻辑

Gloading不侵入UI布局,完全由用户自定义

public class GlobalAdapter implements Gloading.Adapter {    @Override    public View getView(Gloading.Holder holder, View convertView, int status) {        GlobalLoadingStatusView loadingStatusView = null;        //convertView为可重用的布局        //Holder中缓存了各状态下对应的View        //	如果status对应的View为null,则convertView为上一个状态的View        //	如果上一个状态的View也为null,则convertView为null        if (convertView != null && convertView instanceof GlobalLoadingStatusView) {            loadingStatusView = (GlobalLoadingStatusView) convertView;        }        if (loadingStatusView == null) {            loadingStatusView = new GlobalLoadingStatusView(holder.getContext(), holder.getRetryTask());        }        loadingStatusView.setStatus(status);        return loadingStatusView;    }        class GlobalLoadingStatusView extends RelativeLayout {        public GlobalLoadingStatusView(Context context, Runnable retryTask) {            super(context);            //初始化LoadingView            //如果需要支持点击重试,在适当的时机给对应的控件添加点击事件        }                public void setStatus(int status) {            //设置当前的加载状态:加载中、加载失败、空数据等            //其中,加载失败可判断当前是否联网,可现实无网络的状态            //		属于加载失败状态下的一个分支,可自行决定是否实现        }    }}

3、 初始化Gloading的默认Adapter

Gloading.initDefault(new GlobalAdapter());

注:可以用AutoRegister在Gloading类装载进虚拟机时自动完成初始化注册,无需在app层执行注册,耦合度更低

4、在需要使用LoadingView的地方获取Holder

//在Activity中显示, 父容器为: android.R.id.contentGloading.Holder holder = Gloading.getDefault().wrap(activity);//需要支持加载失败后点击重试Gloading.Holder holder = Gloading.getDefault().wrap(activity).withRetry(retryTask);

or

//为某个View显示加载状态//Gloading会自动创建一个FrameLayout,将view包裹起来,LoadingView也显示在其中Gloading.Holder holder = Gloading.getDefault().wrap(view);//需要支持加载失败后点击重试Gloading.Holder holder = Gloading.getDefault().wrap(view).withRetry(retryTask);

5、 使用Holder来显示各种加载状态

//显示加载中的状态,通常是显示一个加载动画holder.showLoading() //显示加载成功状态(一般是隐藏LoadingView)holder.showLoadSuccess()//显示加载失败状态holder.showFailed()//数据加载完成,但数据为空holder.showEmpty()//如果以上默认提供的状态不能满足使用,可使用此方法调用其它状态holder.showLoadingStatus(status)

更多API详情请查看 Gloading JavaDocs

6、 兼容多App场景下的页面、View的复用

每个App的LoadingView可能会不同,只需为每个App提供不同的Adapter,不同App调用不同的Gloading.initDefault(new GlobalAdapter());,具体页面中的使用代码无需改动。

注:如果使用AutoRegister,则只需在不同App中创建各自的 Adapter实现类即可,无需手动注册。只需改动2处gradle文件即可:

  • 修改根目录build.gradle,添加对AutoRegister的依赖
buildscript {  //...  dependencies {    //...    classpath 'com.billy.android:autoregister:使用最新版'  }}
  • 修改主application module下的build.gradle,添加如下代码即可实现Adapter的自动注册
apply plugin: 'auto-register'autoregister {  registerInfo = [     [         'scanInterface'             : 'com.billy.android.loading.Gloading$Adapter'         , 'codeInsertToClassName'   : 'com.billy.android.loading.Gloading'         , 'registerMethodName'      : 'initDefault'     ]  ]}

开启/关闭Debug模式

//设置为true时Logcat会输出日志Gloading.debug(trueOrFalse);

鸣谢

Demo中的卡通图片均来自: https://www.thiswaifudoesnotexist.net/

Demo中使用的全局LoadingView图片均取自:https://www.iconfont.cn/

Demo中的特殊Loading动画来自:https://github.com/ldoublem/LoadingView/


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap