LoadSir是一个高效易用,低碳环保,扩展性良好的加载反馈页管理框架,在加载网络或其他数据时候,根据需求切换状态页面,可添加自定义状态页面,如加载中,加载失败,无数据,网络超时,占位图,登录失效等常用页面。可配合网络加载框架,结合返回状态码,错误码,数据进行状态页自动切换,封装使用效果更佳。

效果预览

in Activity in View in Fragment



Placeholder Muitl-Fragment ViewPage+Fragment



使用场景

下面为大家常见的加载反馈页面:

loading error timeout



empty custom placeholder



面对这么多状态页面,你是不是还在用include的方式,setVisibility(View.VISIBLE/GONE),这种方式即不方便控制,也造成了视图层级冗余(你要把所有状态布局include进一个视图)。如果有一种工具,能把这些事都做了就好了。恰好,  LoadSir 把这些事做了,接下来我们就来了解一下它。

LoadSir的功能及特点

  • 支持Activity,Fragment,Fragment(v4),View状态回调

  • 适配多个Fragment切换,及Fragment+ViewPager切换,不会状态叠加或者状态错乱

  • 利用泛型转换输入信号和输出状态,可根据网络返回体的状态码或者数据返回自动适配状态页,实现全局自动状态切换

  • 只加载唯一一个状态视图,不会预加载全部视图

  • 可保留标题栏(Toolbar,titile view等)

  • 可设置重新加载点击事件(OnReloadListener)

  • 可自定义状态页(继承Callback类)

  • 可在子线程直接切换状态

  • 可设置初始状态页(常用进度页作为初始状态)

  • 不需要设置枚举或者常量状态值,直接用状态页类类型(xxx.class)作为状态码

  • 可扩展状态页面,在配置中添加自定义状态页

  • 可对单个状态页单独设置点击事件,根据返回boolean值覆盖或者结合OnReloadListener使用,如网络错误可跳转设置页

  • 可全局单例配置,也可以单独配置

  • 无预设页面,低耦合,开发者随心配置

开始使用LoadSir

LoadSir的使用只需要简单的三步,三步上篮的三步。

添加依赖

compile 'com.kingja.loadsir:loadsir:1.3.6'

第一步: 配置

全局配置方式

全局配置方式,使用的是单例模式,即获取的配置都是一样的。可在Application中配置,添加状态页,设置初始化状态页,建议使用这种配置方式。

public class App extends Application {    @Override    public void onCreate() {        super.onCreate();        LoadSir.beginBuilder()    .addCallback(new ErrorCallback())//'添加各种状态页    .addCallback(new EmptyCallback())    .addCallback(new LoadingCallback())    .addCallback(new TimeoutCallback())    .addCallback(new CustomCallback())    .setDefaultCallback(LoadingCallback.class)//设置默认状态页    .commit();    }}

单独配置方式

如果你即想保留全局配置,又想在某个特殊页面加点不同的配置,可采用该方式。

LoadSir loadSir = new LoadSir.Builder()    .addCallback(new LoadingCallback())    .addCallback(new EmptyCallback())    .addCallback(new ErrorCallback())    .build();        loadService = loadSir.register(this, new Callback.OnReloadListener() {@Overridepublic void onReload(View v) {    // 重新加载逻辑}        });

第二步: 注册

在Activity中使用

@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_content);    // Your can change the callback on sub thread directly.    LoadService loadService = LoadSir.getDefault().register(this, new Callback.OnReloadListener() {        @Override        public void onReload(View v) {// 重新加载逻辑        }    });}}

在View 中使用

ImageView imageView = (ImageView) findViewById(R.id.iv_img);LoadSir loadSir = new LoadSir.Builder()        .addCallback(new TimeoutCallback())        .setDefaultCallback(LoadingCallback.class)        .build();loadService = loadSir.register(imageView, new Callback.OnReloadListener() {    @Override    public void onReload(View v) {        loadService.showCallback(LoadingCallback.class);        // 重新加载逻辑    }});

在Fragment 中使用

由于Fragment添加到Activitiy方式多样,比较特别,所以在Fragment中注册方式不同于上面两种,大家先看模板代码:

@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle        savedInstanceState) {    //第一步:获取布局View    rootView = View.inflate(getActivity(), R.layout.fragment_a_content, null);    //第二步:注册布局View    LoadService loadService = LoadSir.getDefault().register(rootView, new Callback.OnReloadListener() {        @Override        public void onReload(View v) {// 重新加载逻辑        }    });    //第三步:返回LoadSir生成的LoadLayout    return loadService.getLoadLayout();}

第三步: 回调

直接回调

protected void loadNet() {        // 进行网络访问...        // 进行回调        loadService.showSuccess();//成功回调        loadService.showCallback(EmptyCallback.class);//其他回调    }

转换器回调 (推荐使用)

如果你不想再每次回调都要手动进行的话,可以选择注册的时候加入转换器,可根据返回的数据,适配对应的回调。

LoadService loadService = LoadSir.getDefault().register(this, new Callback.OnReloadListener() {    @Override    public void onReload(View v) {// 重新加载逻辑    }}, new Convertor() {@Overridepublic Class extends Callback> map(HttpResult httpResult) {        Class extends Callback> resultCode = SuccessCallback.class;switch (httpResult.getResultCode()) {case SUCCESS_CODE://成功回调if (httpResult.getData().size() == 0) {        resultCode = EmptyCallback.class;    }else{        resultCode = SuccessCallback.class;    }break;case ERROR_CODE:    resultCode = ErrorCallback.class;break;        }return resultCode;    }});

回调的时候直接传入转换器指定的数据类型。

loadService.showWithConvertor(httpResult);

自定义回调页

LoadSir为了完全解耦,没有预设任何状态页,开发者根据需求自定义自己的回调页面,比如加载中,没数据,错误,超时等常用页面,
设置布局及自定义点击逻辑

public class CustomCallback extends Callback {    @Override    protected int onCreateView() {        return R.layout.layout_custom;    }

    @Override    protected boolean onRetry(final Context context, View view) {        //布局点击事件        Toast.makeText(context.getApplicationContext(), "Hello mother fuck! :p", Toast.LENGTH_SHORT).show();        //子控件事件        (view.findViewById(R.id.iv_gift)).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {    Toast.makeText(context.getApplicationContext(), "It's your gift! :p", Toast.LENGTH_SHORT).show();}        });        return true;//返回true则覆盖了register时传入的重试点击事件,返回false则两个都执行    }

    //是否在显示Callback视图的时候显示原始图(SuccessView),返回true显示,false隐藏    @Override    public boolean getSuccessVisible() {        return super.getSuccessVisible();    }

    //将Callback添加到当前视图时的回调,View为当前Callback的布局View    @Override    public void onAttach(Context context, View view) {        super.onAttach(context, view);    }

    //将Callback从当前视图删除时的回调,View为当前Callback的布局View    @Override    public void onDetach() {        super.onDetach(context, view);    }}

动态修改Callback

loadService = LoadSir.getDefault().register(...);loadService.setCallBack(EmptyCallback.class, new Transport() {   @Override   public void order(Context context, View view) {       TextView mTvEmpty = (TextView) view.findViewById(R.id.tv_empty);       mTvEmpty.setText("fine, no data. You must fill it!");   }});

代码混淆

-dontwarn com.kingja.loadsir.**-keep class com.kingja.loadsir.** {*;}

占位图布局效果

placeholder效果状态页类似ShimmerRecyclerView的效果. LoadSir只用了一个自定义状态页PlaceHolderCallback就完成类似的效果,是不是很棒 :p

看到这,想必各位使用LoadSir应该没问题了,如果还想再进一步了解它的内部结构,可以继续往下看。

原理解析

流程图


关键类

  • LoadSir:提供单例模式获取全局唯一实例,内部保存配置信息,根据配置创建LoadService。

  • LoadService:具体操作服务类,提供showSuccess,showCallback,showWithCoverator等方法来进行状态页回调。

  • LoadLayout:最终显示在用户面前的视图View,替换了原布局,是LoadService直接操作对象,要显示的状态页的视图会被添加到LoadLayout上。

  • Callback:状态页抽象类,抽象自定义布局和自定义点击事件两个方法留给子类实现。

  • Coverator:转换接口,可将网络返回实体转换成对应的状态页,达到自动适配状态页的目的。

我们直接观察在Activity中普通加载和使用LoadSir加载视图的区别

>>>没使用LoadSir


>>>使用LoadSir


大家可以看到,LoadSir用LoadLayout把原来的布局给替代掉了,原来的布局加在了LoadLayout上,其它自定义的状态页也同样会被加到这个LoadLayout上(显示的时候),而且LoadLayout的子View只有一个,就是当前要显示的状态页布局,并没有把当前不显示的比如加载中布局,错误布局,无数据布局加载进来,这也是LoadSir的优点之一,按需加载,并且只加载一个状态布局。

>>>替换逻辑

public static TargetContext getTargetContext(Object target) {        ViewGroup contentParent;        Context context;        if (target instanceof Activity) {Activity activity = (Activity) target;context = activity;contentParent = (ViewGroup) activity.findViewById(android.R.id.content);        } else if (target instanceof View) {View view = (View) target;contentParent = (ViewGroup) (view.getParent());context = view.getContext();        } else {throw new IllegalArgumentException("The target must be within Activity, Fragment, View.");        }       ...        if (contentParent != null) {contentParent.removeView(oldContent);        }        return new TargetContext(context, contentParent, oldContent, childIndex);    }

大家可以看到,在Activity和View中的情况都比较简单,直接获取target的父控件,然后在父控件中替换掉该布局即可。在Fragment中,由于可能多个Fragment的布局View并存在一个父控件里,所以不能简单地使用父控件删除子View方式替换,也有可能父控件是ViewPager,不能通过addView()的方式添加LoadLayout。因此Fragment的注册方式是直接返回了LoadLayout到Activity上。这样也达到了一样的目的。

下面是ViewPager+Fragment场景中使用LoadSir的视图,两个Fragment用各自的LoadLayout进行视图分离,避免了状态页叠加或错位。


看到这的童鞋应该也大概知道LoadSir是怎么回事了,如果想明白LoadSir的代码实现,请继续往下看。

源码解析

我们按上面三步上篮的步骤来稍微分析下源码

>>>第一步:配置

单例模式获取LoadSir,在LoadSir构造的时候创建默认配置

public static LoadSir getDefault() {        if (loadSir == null) {synchronized (LoadSir.class) {    if (loadSir == null) {        loadSir = new LoadSir();    }}        }        return loadSir;    }

    private LoadSir() {        this.builder = new Builder();    }

Builder主要提供添加状态页,和设置默认状态页的方法

public static class Builder {        private List callbacks = new ArrayList<>();private Class extends Callback> defaultCallback;public Builder addCallback(Callback callback) {callbacks.add(callback);return this;        }public Builder setDefaultCallback(Class extends Callback> defaultCallback) {this.defaultCallback = defaultCallback;return this;        }      ...public LoadSir build() {return new LoadSir(this);        }    }

LoadSir提供beginBuilder()…commit()来设置全局配置。

public class LoadSir  {   ...    public static Builder beginBuilder() {        return new Builder();    }

    public static class Builder {

        public void commit() {getDefault().setBuilder(this);        }      ...    }}

>>>第二步:注册

LoadSir注册后返回的是LoadService,一看名字大家就明白这是服务类,就是我们所说的Service层。

public LoadService register(Object target, Callback.OnReloadListener onReloadListener) {        return register(target, onReloadListener, null);    }

    public  LoadService register(Object target, Callback.OnReloadListener onReloadListener, Convertorconvertor) {        TargetContext targetContext = LoadSirUtil.getTargetContext(target);return new LoadService<>(convertor, targetContext, onReloadListener, builder);    }

在LoadService的构造方法中根据target等信息创建Success视图,并且生成LoadLayout,相当于LoadSir每次注册都会创建一个LoadLayout。

LoadService(Convertor convertor, TargetContext targetContext, Callback.OnReloadListener onReloadListener, LoadSir.Builder builder) {this.convertor = convertor;        Context context = targetContext.getContext();        View oldContent = targetContext.getOldContent();        loadLayout = new LoadLayout(context, onReloadListener);        loadLayout.addCallback(new SuccessCallback(oldContent, context,    onReloadListener));if (targetContext.getParentView() != null) {targetContext.getParentView().addView(loadLayout, targetContext.getChildIndex(), oldContent        .getLayoutParams());        }        initCallback(builder);    }

>>>第三步:回调

LoadService的三个回调方法最终调用的都是loadLayout.showCallback(callback);

public void showSuccess() {        loadLayout.showCallback(SuccessCallback.class);    }

    public void showCallback(Class extends Callback> callback) {        loadLayout.showCallback(callback);    }

    public void showWithConvertor(T t) {        if (convertor == null) {throw new IllegalArgumentException("You haven't set the Convertor.");        }        loadLayout.showCallback(convertor.map(t));    }

我们直接看LoadLayout的showCallback方法,先做Callback是否配置判断,然后进行线程安全操作。重点还是showCallbackView(callback);

public void showCallback(final Class extends Callback> callback) {        if (!callbacks.containsKey(callback)) {throw new IllegalArgumentException(String.format("The Callback (%s) is nonexistent.", callback        .getSimpleName()));        }        if (LoadSirUtil.isMainThread()) {showCallbackView(callback);        } else {postToMainThread(callback);        }    }

这个方法可以说是最后的执行者,就做两件事,删除LoadLayout所有子View(重置),添加指定的布局页View(回调)。

private void showCallbackView(Class extends Callback> status) {        if (getChildCount() > 0) {removeAllViews();        }        for (Class key : callbacks.keySet()) {if (key == status) {    addView(callbacks.get(key).getRootView());}        }    }

自此,LoadSir一个完整的配置,注册,回调的过程完成了。不知道你们明白了没,反正我是有点口渴了。

总结

建议在Application中全局配置,在BaseActivity,BaseFragment或者MVP中封装使用,能极大的减少代码量,让你的代码更加优雅,生活更加愉快。时间和个人能力有限,如果大家发现需要改进的地方,欢迎提交issue。
如果这个库对你有用的话,也请点个star。

Github传送门:https://github.com/KingJA/LoadSir

大家都在看

Android仿抖音的音乐旋转效果

安卓技术架构演进及未来

Android应用安全提升攻略

Android性能优化指南 文末附花絮视频

欢迎前往安卓巴士博客区投稿,技术成长于分享

期待巴友留言,共同探讨学习

activity中fragment 返回键不退出_优雅地处理加载中(loading),重试(retry)和无数据(empty)等...相关推荐

  1. activity中fragment 返回键不退出_分享一波阿里Android客户端面经,我竟连这都答不上来?...

    今天整理了电脑,发现了几个月前总结了一半的阿里面经,想起来了去年秋招不愉快的阿里一面,今天分享给大家.希望大家前事不忘后事之师,吸取经验教训. 一面(挂) 1.点击图标,应用打开,点击home键,重新 ...

  2. python祝福祖国代码_国庆节踩空间留言代码_国庆节祝福正在加载中

    [fts=6][ffg,#OOOFFF,#FFFFFF]★.`[/ft][/ft][ftc=000000][/ft] [B][ftc=#f49bc1]▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂[ftc=#ff ...

  3. Dialog显示加载中

    移动端的的网络情况有的时候是很不稳定的(在地下室额,气死人)或是使用某些运营商的SIM卡,有的时候进出地铁就会没信号,所以造成数据加载很慢,假如我们没有对这块做处理,App的使用者就会很烦躁(一般不超 ...

  4. JQuery插件:遮罩+数据加载中。。。(特点:遮你想遮,罩你想罩)

    在很多项目中都会涉及到数据加载.数据加载有时可能会是2-3秒,为了给一个友好的提示,一般都会给一个[数据加载中...]的提示.今天就做了一个这样的提示框. 先去jQuery官网看看怎么写jQuery插 ...

  5. Android-完美解决在Activity中触摸返回键onBackPressed不能触发问题

    场景:在一个只有WebView组件的Activity中如何正确处理页面的返回? 之前思路是覆写Activity的onBackPressed方法,因为用户触摸"返回键"会触发该方法: ...

  6. 如何添加Android返回键的退出功能

    环境:Cocos2dx v3.5 经网上搜索资料知道有两种方法可以实现Android返回键的退出功能,下面分别说明. 1.修改java代码 首先,在Cocos2dx的源文件中找到Cocos2dxGLS ...

  7. 解决Hbuilder打包的app返回键直接退出

    解决Hbuilder打包的app返回键直接退出 参考 https://blog.csdn.net/qq_25252769/article/details/76913083 解决打包成App后,单击 手 ...

  8. 关于移动web手机端返回键直接退出的问题

    问题描述:Hbuilder打包的app如果点击手机返回键,app会直接退出,返回不了上一页. 1.处理子页面点击返回键直接退出,无法返回,代码如下: //这个不是写在首页,写在子页面(子页面才能返回, ...

  9. 静态原型设计 加载中_见解1:原型设计有助于填补静态设计留下的空白。

    静态原型设计 加载中 In April 2015, I joined the Disney Parks creative team to design mobile experiences for t ...

最新文章

  1. 题目1095:2的幂次方
  2. 采购订单模板_电子信息制造业解决方案,电子工业采购监管、管理、降本可控化...
  3. leetcode107. 二叉树的层次遍历 II
  4. 前端学习(2353):button按钮组件的使用
  5. 从文件中读取数据,排序之后输出到另一个文件中
  6. 音频压缩编码技术(四)—AAC编解码器
  7. python画散点图-python绘制散点图
  8. 深入理解Nginx~网络连接的设置
  9. 4k电视色彩表现测试软件,选高端4K电视 4K测试图帮你轻松分辨
  10. c语言编程创意表白,C语言和图形界面编程打造——浪漫的表白程序
  11. 全国计算机等级考试二级Python(2021年9月)备考笔记 第九天
  12. 安装vum、npm、cnpm、vue/cli
  13. 阿里云短信服务(解决个人无法申请问题)
  14. Android Material Component:工具栏与DisplayCutout
  15. 帝国时代2战役php文件,帝国时代各种类型文件使用说明
  16. matlab数字信号处理(1)——正弦信号生成与时域分析
  17. 遍历一个文件夹下的所有文件和子文件夹
  18. 网络视频监控系统的现状和发展
  19. 面向考研的数据结构板子
  20. Oracle的基础知识点汇总

热门文章

  1. STM32 基础系列教程 50 – MbedTls
  2. u-boot分析之命令实现(四)
  3. 那些很骚很酷很有内涵的话
  4. 1、HTML 初步认识
  5. UVA 220 Othello
  6. 我理解的javascript事件循环(一)
  7. mac远程桌面Microsoft Remote Desktop for Mac的安装与使用
  8. Hillstone 防火墙备份脚本
  9. 终于有人讲透了芯片是什么(电子行业人士必读)
  10. CSP认证201503-2 数字排序[C++题解]:哈希表、排序、结构体