Android自定义ViewGroup——ExpansionPanel

本文介绍了如何在Android中实现类似Flutter ExpansionPanel的伸缩效果。作者通过自定义ViewGroup,继承自LinearLayout,内部使用ConstraintLayout作为根布局。详细阐述了思路、开始实现的步骤,包括添加子控件、默认显示收缩状态、增加动画,并展示了最终效果。
摘要由CSDN通过智能技术生成

前言

最近在开发中需要实现一个伸缩效果,就像Flutter中的ExpansionPanel控件一样,效果如下图,发现Android中竟然没有类似的控件,网上也有较多的实现,效果不太理想,因此决定自己实现一个。

思路

为了方便可伸缩内容的拓展以及达到图中的效果,我选择直接继承于CardView来实现,这样比较方便,不需要自己实现测量等方法。在CardView内部填充一个ConstraintLayout(减少布局嵌套层数)作为根布局,使其他控件在根布局的特点位置进行摆放,最后利用动画效果实现伸缩。从效果图中可以得知,该控件分为标题(也就是处于收缩状态下显示的控件)、伸缩按钮以及展开后需要显示的控件。知道思路之后那就直接开干。

开始

  • 新建类ExpansionPanel并继承于CardView,定义根布局root
class NewExpansionPanel(
    context: Context,
    attributeSet: AttributeSet
) : CardView(context,attributeSet){
   

	private var root = ConstraintLayout(context)    // 根布局
    private var toggle = ImageButton(context)       // 伸缩按钮
    
}

这里直接创建ConstraintLayout是为了减少解析XML的时间,如果闲麻烦的话可以直接在构造方法中加载一个XML布局,然后得到根布局。

  • 添加ViewNewExpansionPanel
    在解析XML时,系统会自动调用ViewGroup中的addView方法将解析得到的子控件添加到其中,因此,我们想要得到XML中的子控件,我才用重写addView方法实现。
	private val views = mutableListOf<View>()       // 存放子控件
	private var initial = false		// 是否初始化
// 重写addView获取子控件
    override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
   
        views.add(child!!)		// 保存子控件
        child.layoutParams = params
        if (views.size > 2){
   
            throw IllegalArgumentException("childCount must less or equals two. Your childCount is ${
     views.size} !")
        }
        if (!initial){
   
        // 由于可能有多个子控件,保证以下代码只执行一次,减少不必要的测量
            removeAllViews()
            // 添加根布局root
            super.addView(root, index, params)
            initial = true
        }
    }

这样既得到了子控件也让根布局成功的添加到了NewExpansionPanel中。

  • 默认显示收缩状态
    定义变量isExpand作为是否伸缩的标志,定义变量expansionHeight存放整个控件的高度,重写onMeasure方法,控制控件高度。
	private var isExpand = false
	private var expansionHeight = 0
	
	override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
   
        addAllViews()	// 将XML中的子控件添加到root
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)	// 测量各控件的高宽
        getExpansionHeight()	// 获取整个控件的高度
        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),expansionHeight)		// 设置控件的高宽
    }

在第一次测量之前,把XML中的子控件添加到根布局,并为这些控件添加约束,完成后再进行测量,测量后获取各个控件的高度以及设置根布局的高度。

	
	private var isAdd = false   // 是否添加布局

	private fun addAllViews(){
   
        if (!isAdd){
   
            // 遍历子控件并添加到root
            views.forEach {
   
                if (it.id == View.NO_ID){
   
                    it.id = View.generateViewId()
                }
                root.addView(it)
            }
        }
        initConstraint()    // 为各个控件添加约束
    }

定义变量topHeightcontentHeight分别存放顶部最大高度(顶部View与伸缩按钮的高度最大值)以及底部内容的高度。

	private fun getExpansionHeight(){
   
	        if (!isAdd){
   
	        	// 得到控件高度
	            expansi
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值