专栏首页Android干货园【PageLayout】非常简单的一键切换加载-空数据-错误页,支持自定义

【PageLayout】非常简单的一键切换加载-空数据-错误页,支持自定义

项目中我们经常会用到的加载数据,加载完数据后显示内容,如果没有数据显示一个空白页,这是如果网络错误了显示一个网络错误页,自定义一个PageLayout。

绪论

Android中经常使用一个空白页和网络错误页用来提高用户体验,给用户一个较好的感官,如果获取到的数据为空,那么会显示一个空白数据页,如果在获取数据的过程中网络错误了,会显示一个网络异常页,像最近比较火的某东这样,见下图。网上也有一些开源的组件,大部分都是自定义继承某个布局在xml中让其作为跟布局,然后将自己的内容布局添加进去,效果也都不错,但是个人总觉得稍微有些麻烦,不是那么灵活,n多个xml布局都去定义,写的心烦,所以有了今天的主角。

思考

实现的思路实际上是和上面说的一样,只不过换了一种方式,我们手动获取到contentView,将它从DecorView中移除,然后交给PageLayout取管理。当时考虑的时候就是不想在每个xml中去写页面切换的布局,那么我们可不可以用Java代码去控制?带着下面几个问题一起来看一下。

  • 1.自定义一个布局让其作为跟布局
  • 2.提供切换加载loading空白页empty错误页errror内容页content功能
  • 3.怎么让其取管理上边的四个页面?
  • 4.contentView怎么添加?
  • 5.如果我想切换的跟布局不是个Activity或者Fragment怎么办?
  • 6.因为切换页面状态的功能一般都是一个APP统一的,那么可不可以一键配置呢?

实现

1.代码设计

首先我们定义PageLayout继承FrameLayout或者LinearLayou或者其他的布局都可以,然后我们需要提供切换四个布局的功能,当然如果支持自定义就更好了,还有状态布局里面的一些属性,还方便一键配置,所以最后采用了Builder模式来创建,使用方式就和Android里面的AlertDialog一样,通过Builder去构建一个PageLayout。最后的样子是长这样的:

默认样式

PageLayout.Builder(this)
                .initPage(ll_default)
                .setOnRetryListener(object : PageLayout.OnRetryClickListener{
                    override fun onRetry() {
                        loadData()
                    }

                })
                .create()

自定义样式

PageLayout.Builder(this)
                .initPage(ll_demo)
                .setLoading(R.layout.layout_loading_demo)
                .setEmpty(R.layout.layout_empty_demo)
                .setError(R.layout.layout_error_demo,R.id.tv_page_error_demo,object : PageLayout.OnRetryClickListener{
                    override fun onRetry() {
                        loadData()
                    }
                })
                .setEmptyDrawable(R.drawable.pic_empty)
                .setErrorDrawable(R.drawable.pic_error)
                .create()

2.设置PageLayout

考虑好了代码设计方式之后,我们来具体实现功能,首先需要考虑上面的5,6点:

contentView怎么添加?

如果我想切换的跟布局不是个Activity或者Fragment怎么办?

1.Activity

如果我们要切换的跟布局是个Activity时,首先我们需要了解一下Android中的setContentView()方法,很熟悉,是我们新建完Activity后默认会在生命周期方法onCreate()中默认存在的,那么setContentView()做了些什么呢?我们先看一张图:

一个Activity是通过ActivityThread创建出来的,创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联,setContentView()是通过getWindow()调用的,这里的window实际初始化的时候初始化为PhoneWindow,也就是说Activity会调用PhoneWindow的setContentView()将layout布局添加到DecorView上,而此时的DecorView就是那个最底层的View。然后通过LayoutInflater.infalte()方法加载布局生成View对象并通过addView()方法添加到Window上,(一层一层的叠加到Window上)所以,Activity其实不是显示视图,Window才是真正的显示视图。

再来看上面的那张图,可以说DecorView是一个界面的真正跟布局,TitleView我们可以通过设置theme样式显示隐藏的,状态布局切换时我们不考虑TitleView,我们只需要考虑ContentView,而ContentView也就是android.R.id.content,知道了这些我们来看看怎么获取将contenView交给PageLayout管理。

2.Fragment、View

如果我们要切换的跟布局是个Fragment、View时,我们只需要获取到它的parent

3.PageLayout设置跟布局

获取到了contentView跟布局后,我们要移除自己的显示内容的布局,并把这个布局交给PageLayout,下面看一下代码,注释的很详细了

 /**
         * set target view for root
         */
        fun initPage(targetView: Any): Builder {
            var content: ViewGroup? = null
            when (targetView) {
                is Activity -> {    //如果是Activity,获取到android.R.content
                    mContext = targetView
                    content = (mContext as Activity).findViewById(android.R.id.content)
                }
                is Fragment -> {    //如果是Fragment获取到parent
                    mContext = targetView.activity!!
                    content = (targetView.view)?.parent as ViewGroup
                }
                is View -> {        //如果是View,也取到parent
                    mContext = targetView.context
                    try {
                        content = (targetView.parent) as ViewGroup
                    } catch (e: TypeCastException) {
                    }
                }
            }
            val childCount = content?.childCount
            var index = 0
            val oldContent: View
            if (targetView is View) {   //如果是某个线性布局或者相对布局时,遍历它的孩子,找到对应的索引,记录下来
                oldContent = targetView
                childCount?.let {
                    for (i in 0 until childCount) {
                        if (content!!.getChildAt(i) === oldContent) {
                            index = i
                            break
                        }
                    }
                }

            } else {    //如果是Activity或者Fragment时,取到索引为第一个的View
                oldContent = content!!.getChildAt(0)
            }
            mPageLayout.mContent = oldContent   //给PageLayout设置contentView
            mPageLayout.removeAllViews()    
            content?.removeView(oldContent)     //将本身content移除,并且把PageLayout添加到DecorView中去
            val lp = oldContent.layoutParams
            content?.addView(mPageLayout, index, lp)
            mPageLayout.addView(oldContent)
            initDefault()   //设置默认状态布局
            return this
        }

这样我们就解决了上面的5,6的问题。

4.其他

  • 因为错误布局中一般都包括一个点击重试的功能,如果你需要自定义布局,你可以在配置PageLayout之前,设置好错误布局和点击事件,然后setError进去,同时也提供了一个默认方式的方法
fun setError(errorView: Int, errorClickId: Int, onRetryClickListener: OnRetryClickListener)
  • 考虑到此功能的APP统一性,所以并没有提供过多的自定义功能,如果你需要的话,你都可以提前设置好View,然后进行set
  • 之前和同事讨论,xml形式和代码形式哪个更方便更灵活,这些都属于个人喜好吧,如果你更喜欢在xml里写的话,你可以进行改造,也挺简单,目前没提供xml方式,PageLayout的初衷就是模仿AlertDialog方式,随时随地使用状态布局切换
  • 你也可以在BaseActivity和BaseFragment中进行PageLayout的初始化,Demo中未使用,自行解决

效果图

代码已经上传到Githubhttps://github.com/Hankkin/PageLayoutDemo

Reading:一款不错的Material Desgin风格的Kotlin版本的开源APP https://github.com/Hankkin/Reading

欢迎大家Follow、star、fork,谢谢 如果有不合适的地方,请提issues讨论指正

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • popwindow 被魅族虚拟键挡住

    版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/45...

    Hankkin
  • Android高仿微信照片选择器+预览+显示照片

    版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/49...

    Hankkin
  • Android 自定义上面圆角下面直角的ImageView

    版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/48...

    Hankkin
  • ceph分布式存储-集群客户端连接

    Lucien168
  • 亚马逊云平台孵化器:跟创业小伙伴一起玩

    [在对亚马逊AWS在中国业务发展的评估中,除了市场的业绩表现外,有多少人尝试使用公有云服务,是否在持续地使用是其中一个重要的考核指标] “听风平安卫士”是一家集...

    静一
  • HomePwn:一款专用于物联网设备渗透测试的“瑞士军刀”

    HomePwn是一款功能强大的物联网渗透测试框架,它可谓是该领域的一把“瑞士军刀”。HomePwn可以提供设备安全审计和渗透测试功能,企业员工可以使用HomeP...

    FB客服
  • 加上一个奇虎,去掉一个中搜

                                搜索引擎的战火才刚拉开序幕,而普通网民将会在这场无休止的战役中取得实惠。

    田春峰-JCJC错别字检测
  • Web Workers 介绍

    有时候,我们写的脚本需要执行一些耗时的大计算量的操作。在脚本执行过程中,浏览器会出现冻结用户界面的情况(用户对页面进行操作,但浏览器没有响应)。

    Joel
  • 高通发布神经处理引擎SDK,开发骁龙处理器的深度学习潜能

    陈桦 编译整理 量子位 报道 | 公众号 QbitAI ? 为了在各类设备上实现基于深度学习的软件开发,高通为骁龙系列移动处理器开发了“神经处理引擎”(NPE)...

    量子位
  • 第1章 程序设计和C语言

    程序,就是一组计算机能识别和执行的指令。每一条指令使计算机执行特定的操作。只要让计算机执行这个程序,计算机就会“自动地”执行各条指令,有条不紊地进行工作。

    诸葛青云

扫码关注云+社区

领取腾讯云代金券