网站建设合同报价单 模板,wordpress配置又拍云cdn打不开,厦门seo蜘蛛屯,东风地区网站建设背景Loading动画几乎每个Android App中都有。一般在需要用户等待的场景#xff0c;显示一个Loading动画可以让用户知道App正在加载数据#xff0c;而不是程序卡死#xff0c;从而给用户较好的使用体验。同样的道理#xff0c;当加载的数据为空时显示一个数据为空的视图、在…背景Loading动画几乎每个Android App中都有。一般在需要用户等待的场景显示一个Loading动画可以让用户知道App正在加载数据而不是程序卡死从而给用户较好的使用体验。同样的道理当加载的数据为空时显示一个数据为空的视图、在数据加载失败时显示加载失败对应的UI并支持点击重试会比白屏的用户体验更好一些。加载中、加载失败、空数据的UI风格一般来说在App内的所有页面中需要保持一致也就是需要做到全局统一。1. 传统的做法定义一个(或多个)显示不同加载状态的控件或者xml布局文件例如LoadingView每个页面的布局中都写上这个view在BaseActivity/BaseFragment中封装LoadingView的初始化逻辑并封装加载状态切换时的UI显示逻辑暴露给子类以下方法void showLoading(); //调用此方法显示加载中的动画void showLoadFailed(); //调用此方法显示加载失败界面void showEmpty(); //调用此方法显示空页面void onClickRetry(); //子类中实现点击重试的回调方法在BaseActivity/BaseFragment的子类中可通过上一步的封装比较方便地使用加载状态显示功能这种使用方式耦合度太高每个页面的布局文件中都需要添加LoadingView使用起来不方便而且维护成本较高一旦UI设计师需要更改布局修改起来成本较高。2. 好一点的封装方法定义一个(或多个)显示不同加载状态的控件或者xml布局文件例如LoadingView定义一个工具类(LoadingUtil)来管理LoadingView不同状态显示不同的UI或者在多个View之间切换显示在BaseActivity/BaseFragment中对LoadingUtil的使用进行封装暴露给子类以下方法void showLoading(); //调用此方法显示加载中的动画void showLoadFailed(); //调用此方法显示加载失败界面void showEmpty(); //调用此方法显示空页面void onClickRetry(); //子类中实现点击重试的回调方法abstract int getContainerId(); //子类中实现LoadingUtil动态创建LoadingView并添加到该方法返回id对应的控件中在BaseActivity/BaseFragment的子类中可通过上一步的封装比较方便地使用加载状态显示功能这种封装的好处是通过封装动态地创建LoadingView并添加到指定的父容器中让具体页面无需关注LoadingView的实现只需要指定在哪个容器中显示即可很大程度地进行了解耦。如果公司只在一个App中使用这基本上就够了。但是这种封装方式还是存在耦合页面与它所使用的LoadingView仍然存在绑定关系。如果需要复用到其它App中因为每个App的UI风格可能不同对应的LoadingView布局也可能会不一样要想复用必须先将页面与LoadingView解耦。如何解耦1. 梳理一下我们需要实现的效果页面的LoadingView可切换且不需要改动页面代码页面中可指定LoadingView的显示区域例如导航栏Title不希望被LoadingView覆盖支持在Fragment中使用支持加载失败页面中点击重试兼容不同页面显示的UI有细微差别例如提示文字可能不同2. 确定思路说到View的解耦很容易联想到Android系统中的AdapterView我们常用的GridView和ListView都是它的子类及support包里提供的ViewPager、RecyclerView等它们都是通过Adapter来解耦的将自身的逻辑与需要动态变化的子View进行分离。我们也可以按照这个思路来解耦LoadingView:创建一个工具类用于管理LoadingView各个状态的UI展示创建一个Adapter接口外部提供实现类通过getView方法创建具体的LoadingView每个App提供一个Adapter的实现并注册到工具类中工具类从Adapter.getView获取具体的LoadingView所以页面中使用的代码无需改动已实现页面的LoadingView可切换且不需要改动页面代码由于每个页面或View的加载状态互相之间无关联关系需要创建一个用于管理具体某个LoadingView的状态持有类Holder指定LoadingView所需覆盖的View时动态新建一个FrameLayout布局将原View从ParentView中移除并用它的LayoutParams将FrameLayout添加到ParentView中替代原View在ParentView中的位置再将原View添加到FrameLayout中在Fragment.onCreateView/RecyclerView.Adapter.onCreateViewHolder等方法中创建的View时由于View尚未添加到任何容器中并无getParent()返回null此时需要用动态生成的FrameLayout代替原View作为方法的返回值返回上代码更容易理解public Holder wrap(View view) {FrameLayout wrapper new FrameLayout(view.getContext());ViewGroup.LayoutParams lp view.getLayoutParams();if (lp ! null) {wrapper.setLayoutParams(lp);}if (view.getParent() ! null) {ViewGroup parent (ViewGroup) view.getParent();int index parent.indexOfChild(view);parent.removeView(view);parent.addView(wrapper, index);}LayoutParams newLp new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);wrapper.addView(view, newLp);return new Holder(mAdapter, view.getContext(), wrapper);
}
已实现页面中可指定LoadingView的显示区域
已实现支持在Fragment中使用
另外还顺带支持在RecyclerView、ListView、GridView、ViewPager等情况下的使用为了不侵入UI将加载失败点击重试的点击功能放在Adapter.getView中实现与Android系统中的Adapter不同的是我们的Adapter是全局使用的而失败重试所需执行逻辑每个页面都不一样因为Holder可以持有每个具体的LoadingView可以将retryTask通过Holder传递给Adapter只需要在Adapter.getView时将Holder作为参数传入即可在创建LoadingView时获取该retryTask对象并在点击重试按钮时执行retryTask同理可以通过Holder传递一些附加参数给Adapter以兼容在不同页面上布局的细微差异已实现支持加载失败页面中点击重试
已实现兼容不同页面显示的UI有细微差别例如提示文字可能不同使用Gloading来轻松实现低耦合的全局LoadingViewGloading是一个基于Adapter思路实现的深度解耦App中全局LoadingView的轻量级工具(只有一个java文件不到300行其中注释占100行aar仅6K)1、 依赖Gloadingcompile com.billy.android:gloading:1.0.02、 创建Adapter在getView方法中实现创建各种状态视图加载中、加载失败、空数据等的逻辑Gloading不侵入UI布局完全由用户自定义。示例如下public class GlobalAdapter implements Gloading.Adapter {Overridepublic View getView(Gloading.Holder holder, View convertView, int status) {GlobalLoadingStatusView loadingStatusView null;//convertView为可重用的布局//Holder中缓存了各状态下对应的View// 如果status对应的View为null则convertView为上一个状态的View// 如果上一个状态的View也为null则convertView为nullif (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的默认AdapterGloading.initDefault(new GlobalAdapter());注可以用AutoRegister在Gloading类装载进虚拟机时自动完成初始化注册无需在app层执行注册耦合度更低4、在需要使用LoadingView的地方获取Holder//在Activity中显示, 父容器为: android.R.id.content
Gloading.Holder holder Gloading.getDefault().wrap(activity);//传递点击重试需要执行的task该task在Adapter中用holder.getRetryTask()获取
Gloading.Holder holder Gloading.getDefault().wrap(activity).withRetry(retryTask);//传递点击重试需要执行的task和一个任意类型的扩展参数该参数在Adapter中用holder.getData()获取
Gloading.Holder holder Gloading.getDefault().wrap(activity).withRetry(retryTask).withData(obj);or//为某个View显示加载状态
//Gloading会自动创建一个FrameLayout将view包裹起来LoadingView也显示在其中
Gloading.Holder holder Gloading.getDefault().wrap(view);//传递点击重试需要执行的task该task在Adapter中用holder.getRetryTask()获取
Gloading.Holder holder Gloading.getDefault().wrap(view).withRetry(retryTask);//传递点击重试需要执行的task和一个任意类型的扩展参数该参数在Adapter中用holder.getData()获取
Gloading.Holder holder Gloading.getDefault().wrap(view).withRetry(retryTask).withData(obj);5、 使用Holder来显示各种加载状态//显示加载中的状态通常是显示一个加载动画
holder.showLoading() //显示加载成功状态一般是隐藏LoadingView
holder.showLoadSuccess()//显示加载失败状态
holder.showFailed()//数据加载完成但数据为空
holder.showEmpty()//如果以上默认提供的状态不能满足使用可使用此方法调用其它状态
holder.showLoadingStatus(status)更多API详情请查看 Gloading JavaDocs更多Demo示例代码请查看 Gloading Demo 也可下载Demo apk体验6、封装到BaseActivity/BaseFragment中让BaseActivity和BaseFragment的子类中使用LoadingView更方便子类中使用LoadingView的业务逻辑与实现分离如果原来就是封装到BaseActivity/BaseFragment中的那么可以无缝切换到Gloading如果以后需要将Gloading移除替换成其它实现也无需修改业务代码示例代码如下public abstract class BaseActivity extends Activity {protected Gloading.Holder mHolder;/*** make a Gloading.Holder wrap with current activity by default* override this method in subclass to do special initialization* see SpecialActivity*/protected void initLoadingStatusViewIfNeed() {if (mHolder null) {//bind status view to activity root view by defaultmHolder Gloading.getDefault().wrap(this).withRetry(new Runnable() {Overridepublic void run() {onLoadRetry();}});}}protected void onLoadRetry() {// override this method in subclass to do retry task}public void showLoading() {initLoadingStatusViewIfNeed();mHolder.showLoading();}public void showLoadSuccess() {initLoadingStatusViewIfNeed();mHolder.showLoadSuccess();}public void showLoadFailed() {initLoadingStatusViewIfNeed();mHolder.showLoadFailed();}public void showEmpty() {initLoadingStatusViewIfNeed();mHolder.showEmpty();}}7、 兼容多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]]
}演示为View添加加载状态总结本文介绍了全局LoadingView在实际使用过程中可能存在的一些耦合情况并指出了由此会影响多个App的LoadingView的UI风格不一致导致页面难以复用的问题同时给出了解决思路。另外本文着重介绍了如何使用Gloading来轻松实现低耦合的全局LoadingView喜欢的同学请顺手甩个star支持一下 :)