Android StateLayout状态页

文章目录

  • Android StateLayout状态页
    • 概述
    • 源码
    • 使用
    • 源码下载

Android StateLayout状态页

概述

StateLayout(状态页)包含:加载中页面,错误页面,空页面,内含状态默认页面,支持自定义页面。

在这里插入图片描述

源码

全局配置:

package com.example.tools.state_layout.widgets

import android.view.View
import androidx.annotation.LayoutRes

/**
 * StateLayout全局配置
 */
object StateConfig {

    @LayoutRes
    @JvmStatic
    var emptyLayoutRes = View.NO_ID

    @LayoutRes
    @JvmStatic
    var errorLayoutRes = View.NO_ID

    @LayoutRes
    @JvmStatic
    var loadingLayoutRes = View.NO_ID

    @LayoutRes
    @JvmStatic
    var retryIds: IntArray? = null

    private var mOnStateChangeListener: OnStateChangeListener? = null

    fun setOnStateChangeListener(listener: OnStateChangeListener) {
        mOnStateChangeListener = listener
    }

    fun getOnStateChangeListener(): OnStateChangeListener? {
        return mOnStateChangeListener
    }
}

状态监听:

interface OnStateChangeListener {
    fun showState(status: Int)
}

属性:

<declare-styleable name="StateLayout">
    <attr name="empty_layout" format="reference" />
    <attr name="loading_layout" format="reference" />
    <attr name="error_layout" format="reference" />
</declare-styleable>

代码:

class StateLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

    companion object {
        const val STATE_LOADING = 0xA001
        const val STATE_EMPTY = 0xA002
        const val STATE_ERROR = 0xA003
        const val STATE_CONTENT = 0xA004
    }

    // empty布局资源
    @LayoutRes
    private var emptyLayoutRes = View.NO_ID
        get() = if (field == View.NO_ID) StateConfig.emptyLayoutRes else field

    // loading布局资源
    @LayoutRes
    private var loadingLayoutRes = View.NO_ID
        get() = if (field == View.NO_ID) StateConfig.loadingLayoutRes else field

    // error布局资源
    @LayoutRes
    private var errorLayoutRes = View.NO_ID
        get() = if (field == View.NO_ID) StateConfig.errorLayoutRes else field

    // 保存状态
    private val stateInfoMap = ArrayMap<Int, View>()

    // 当前状态
    private var currentState = STATE_CONTENT

    // 需要设置点击事件的id
    private var retryIds: IntArray? = null
        get() = field ?: StateConfig.retryIds

    private var mOnStateChangeListener: OnStateChangeListener? = null
        get() = field ?: StateConfig.getOnStateChangeListener()

    init {
        val a: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.StateLayout)
        emptyLayoutRes = a.getResourceId(R.styleable.StateLayout_empty_layout, View.NO_ID)
        loadingLayoutRes = a.getResourceId(R.styleable.StateLayout_loading_layout, View.NO_ID)
        errorLayoutRes = a.getResourceId(R.styleable.StateLayout_error_layout, View.NO_ID)
        a.recycle()
    }

    override fun onFinishInflate() {
        super.onFinishInflate()
        if (childCount != 1) {
            throw IllegalStateException("StateLayout必须只能有一个子View")
        }
        if (stateInfoMap.size == 0) {
            val view = getChildAt(0)
            setContentView(view)
        }
    }

    /**
     * 设置内容布局
     */
    private fun setContentView(contentView: View) {
        stateInfoMap[STATE_CONTENT] = contentView
    }

    /**
     * 设置点击事件
     */
    fun setRetryIds(@IdRes vararg ids: Int) {
        retryIds = ids
    }

    /**
     * 设置状态变化监听
     */
    fun setOnStateChangeListener(listener: OnStateChangeListener) {
        mOnStateChangeListener = listener
    }

    /**
     * 显示内容布局
     */
    fun showContent() {
        showState(STATE_CONTENT)
    }

    /**
     * 显示加载布局
     */
    fun showLoading() {
        showState(STATE_LOADING)
    }

    /**
     * 显示失败布局
     */
    fun showError() {
        showState(STATE_ERROR)
    }

    /**
     * 显示空布局
     */
    fun showEmpty() {
        showState(STATE_EMPTY)
    }

    /**
     * 显示视图
     */
    private fun showState(status: Int) {
        if (currentState == status) {
            return
        }
        val stateView = getStateView(status)
        for (i in stateInfoMap) {
            if (i.key != status) {
                val view = i.value
                hideStateView(view)
            }
        }
        showStateView(this, stateView, status)
        mOnStateChangeListener?.showState(status)
        currentState = status
    }

    /**
     * 获取状态视图
     */
    private fun getStateView(status: Int): View {
        val view = stateInfoMap[status]
        if (view != null) {
            return view
        } else {
            val layoutRes = when (status) {
                STATE_EMPTY -> emptyLayoutRes
                STATE_ERROR -> errorLayoutRes
                STATE_LOADING -> loadingLayoutRes
                STATE_CONTENT -> View.NO_ID
                else -> View.NO_ID
            }
            if (layoutRes == View.NO_ID) {
                when (status) {
                    STATE_ERROR -> throw Resources.NotFoundException("请设置errorLayout")
                    STATE_EMPTY -> throw Resources.NotFoundException("请设置emptyLayout")
                    STATE_LOADING -> throw Resources.NotFoundException("请设置loadingLayout")
                    STATE_CONTENT -> throw Resources.NotFoundException("请设置contentView")
                }
            }
            val view = LayoutInflater.from(context).inflate(layoutRes, this, false)
            stateInfoMap[status] = view
            return view
        }
    }

    /**
     * 隐藏视图
     */
    private fun hideStateView(view: View) {
        view.visibility = View.GONE
    }

    /**
     * 显示视图
     */
    private fun showStateView(container: StateLayout, view: View, status: Int) {
        if (container.indexOfChild(view) != -1) {
            view.visibility = View.VISIBLE
        } else {
            if (status == STATE_EMPTY || status == STATE_ERROR) {
                if (retryIds != null) {
                    for (id in retryIds!!) {
                        view.findViewById<View>(id).setOnClickListener {
                            showLoading()
                        }
                    }
                }
            }
            container.addView(view)
        }
    }
}

使用

使用全局配置:

<com.example.tools.state_layout.widgets.StateLayout
    android:id="@+id/state_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="加载成功" />
</com.example.tools.state_layout.widgets.StateLayout>
StateConfig.emptyLayoutRes = R.layout.state_empty
StateConfig.errorLayoutRes = R.layout.state_error
StateConfig.loadingLayoutRes = R.layout.state_loading
StateConfig.retryIds = intArrayOf(R.id.state_msg, R.id.state_iv)

StateConfig.setOnStateChangeListener(object : OnStateChangeListener {
    override fun showState(status: Int) {
        when (status) {
            StateLayout.STATE_LOADING -> {
                LogUtils.e("StateLayout", "显示加载页")
                postDelayed({
                    stateLayout.showContent()
                }, 2000L)
            }
            StateLayout.STATE_CONTENT -> {
                LogUtils.e("StateLayout", "显示内容页")
            }
            StateLayout.STATE_ERROR -> {
                LogUtils.e("StateLayout", "显示失败页")
            }
            StateLayout.STATE_EMPTY -> {
                LogUtils.e("StateLayout", "显示空页")
            }
        }
    }
})

使用局部配置:

<com.example.tools.state_layout.widgets.StateLayout
    android:id="@+id/state_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:empty_layout="@layout/state_empty"
    app:error_layout="@layout/state_error"
    app:loading_layout="@layout/state_loading">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="加载成功" />
</com.example.tools.state_layout.widgets.StateLayout>
stateLayout.setRetryIds(R.id.state_msg, R.id.state_iv)
stateLayout.setOnStateChangeListener(object : OnStateChangeListener {
    override fun showState(status: Int) {
        when (status) {
            StateLayout.STATE_LOADING -> {
                LogUtils.e("StateLayout", "显示加载页")
                postDelayed({
                    stateLayout.showContent()
                }, 2000L)
            }
            StateLayout.STATE_CONTENT -> {
                LogUtils.e("StateLayout", "显示内容页")
            }
            StateLayout.STATE_ERROR -> {
                LogUtils.e("StateLayout", "显示失败页")
            }
            StateLayout.STATE_EMPTY -> {
                LogUtils.e("StateLayout", "显示空页")
            }
        }
    }
})

源码下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/472017.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

提高WhatsApp营销效果:如何在WhatsApp上增加打开率和提高转化率?

提高WhatsApp营销效果&#xff1a;如何在WhatsApp上增加打开率和提高转化率&#xff1f; WhatsApp是一款广受欢迎的社交应用程序&#xff0c;不仅可以用于个人通讯&#xff0c;也可以用于企业营销。许多企业已经开始在WhatsApp上进行营销活动&#xff0c;但是如何提高营销效果…

极佳文件备份工具支持独占文件备份

1 指定备份的文件夹或者分区盘符 或者子文件夹dir1;dir2;dir3; 2 指定备份到哪里 例如disk1 E分区内的backup文件夹 3 指定备份周期 每日几点几分备份&#xff0c;每周几 几点几分备份 或者 间隔几时几分备份 4 备份模式 单向 将数据源文件夹 完整复制到 目标文件夹, 包括正…

JavaScript高级(十八)---进程和线程,宏任务和微任务

进程和线程 进程&#xff08;process&#xff09;&#xff1a;计算机已经运行的程序&#xff0c;是操作系统管理程序的一种方式&#xff0c;我们可以认为&#xff0c;启动一个应用程序&#xff0c;就会默认启动一个进程&#xff08;也可能是多个进程&#xff09;。 线程&…

【现代C++】移动语义和右值引用

现代C中的移动语义和右值引用是C11引入的重要特性&#xff0c;旨在优化资源管理和提升性能。这些特性允许资源的转移而非复制&#xff0c;特别是对于临时对象和大型对象。 1. 右值引用 右值引用是对临时对象的引用&#xff0c;它允许你修改临时对象。使用&&来声明右值…

阿里云效flow流水线构建时内存不足

云效flow流水线构建时内存不足 报错截图 咨询阿里云后建议自己搭建私有化集群 https://help.aliyun.com/document_detail/201868.html?spm5176.2020520104.0.0.481b3f1bw2Mr1v 搭建地址 打开流水线&#xff0c;只有管理员和拥有者才能看到这个菜单 注意&#xff1a;构建集群…

【数组、字符串】算法例题

每个题的【方法1】是自己的思路&#xff0c;【其他方法】是力扣上更优的解题思路 目录 一、数组、字符串 1. 合并两个有序数组 ① 2. 移除元素 ① 3. 删除有序数组中的重复项 ① 4. 删除有序数组中的重复项 II ② 5. 多数元素 ① 6. 轮转数组 ② 7. 买卖股票的最佳时机…

[leetcode] 45. 跳跃游戏 II

文章目录 题目描述解题方法贪心java代码复杂度分析 题目描述 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0…

【二分查找】算法例题

目录 十八、二分查找 114. 搜索插入位置 ① √- 115. 搜索二维矩阵 ② 116. 寻找峰值 ② √- 117. 搜索旋转排序数组 ② 118. 在排序数组中查找元素的第一个和最后一个位置 ② √ 119. 寻找寻钻排序数组中的最小值 ② 120. 寻找两个正序数组的中位数 ③ 136. 直线上最多…

MTLAB 批量下载 脑医学图像数据集BrainWeb: Simulated Brain Database

MTLAB 批量下载 脑医学图像数据集BrainWeb: Simulated Brain Database BrainWeb数据集的网址&#xff1a;https://brainweb.bic.mni.mcgill.ca/brainweb/ 1. 了解 BrainWeb: Simulated Brain Database 这是一个模拟大脑数据的数据库&#xff08;SBD&#xff0c;Simulated Br…

京师心智心理健康测评系统 MyReport.ashx 信息泄露漏洞复现

0x01 产品简介 京师心智心理健康测评系统由心理学专家指导研发&#xff0c;使用先进的测试方法&#xff0c;通过一系列的数据处理将人的心理特征数量化&#xff0c;以此来衡量个体心理因素水平以及心理差异&#xff0c;帮助开展标准化心理健康工作。 0x02 漏洞概述 京师心智…

Java基础---反射

什么是反射&#xff1f; 反射允许对成员变量&#xff0c;成员方法和构造方法的信息进行编程访问。 这么说可能比较抽象&#xff0c;可以简单理解为&#xff1a;反射就是一个人&#xff0c;可以把类里面的成员变量&#xff0c;成员方法&#xff0c;构造方法都获取出来。 并且可…

论文阅读——EarthPT

EarthPT: a time series foundation model for Earth Observation 一个Earth Observation (EO)预训练的Transformer。EarthPT是一个7亿参数解码Transformer基础模型&#xff0c;以自回归自监督方式进行训练&#xff0c;并专门针对EO用例进行开发。我们证明了EarthPT是一个有效的…

ArkTs :5 分钟学会鸿蒙开发语言关键知识

ArkTS 是 HarmonyOS 首选的开发语言&#xff0c;它在 TypeScript 的基础上做了一些优化和扩展&#xff0c;继承了 TypeScript 的优点&#xff0c;屏蔽了 TypeScript 的缺点&#xff0c;和 ArkUI 框架一起构建了鸿蒙应用&#xff0c;这篇文章我们主要介绍 ArkTS。 ArkTS 设计初…

【机器学习】TinyML的介绍以及在运动健康领域的应用

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向的学习指导…

大华 DSS 数字监控系统 attachment_clearTempFile.action SQL注入漏洞复现

0x01 产品简介 大华 DSS 数字监控系统是大华开发的一款安防视频监控系统,拥有实时监视、云台操作、录像回放、报警处理、设备管理等功能。 0x02 漏洞概述 大华 DSS存在SQL注入漏洞,攻击者 向 attachment_clearTempFile.action 路由发送特殊构造的数据包,利用报错注入获取…

peft模型微调_IA3

IA3(论文:Few-Shot Parameter-Efficient Fine-Tuning is Better and Cheaper than In-Context Learning),通过学习向量来对激活层加权进行缩放,从而获得更强的性能,同时仅引入相对少量的新参数,如下图左边所示,它的诞生背景是为了改进 LoRA。 为了使微调更有效,IA3(通…

QQ聊天的快捷回复怎么设置?

在快节奏的商务环境中&#xff0c;每一秒都至关重要。对于在线客服而言&#xff0c;快速而准确地回应客户咨询可以显著提升服务质量和客户满意度。如果你在使用QQ进行客户服务工作&#xff0c;需要更快捷回复客户信息&#xff0c;可以设置快捷回复&#xff0c;那我们就来看看怎…

演讲恐惧症?别怕!这3招帮你克服舞台紧张

演讲恐惧症&#xff1f;别怕&#xff01;这3招帮你克服舞台紧张 在人生的舞台上&#xff0c;演讲往往被视为展现个人才华、传递思想观点的重要平台。然而&#xff0c;对于许多人来说&#xff0c;站在舞台上却成为了一种难以克服的恐惧。演讲恐惧症&#xff0c;这个看似微不足道…

【动态规划】算法例题

目录 一维动态规划&#xff1a; 137. 爬楼梯 ① 138. 打家劫舍 ② 139. 单词拆分 ② 140. 零钱兑换 ② 141. 最长递增子序列 ② 多维动态规划&#xff1a; 142. 三角形最小路径和 ② 143. 最小路径和 ② 144. 不同路径 II ② 145. 最长回文子串 ② 146. 交错字符串…

7.安全性基础知识

主要议题&#xff1a; 安全防护体系&#xff1a;7层次&#xff0c;要记7层次的名称以及这些层次与哪些方面相关&#xff1b; 安全保护等级&#xff1a;5等级&#xff0c;要记5等级安全性的高低排序&#xff0c;掌握每个等级的特点&#xff1b; 用户认证机制&#xff1a;用户认…