Android——基于LinearLayout实现的可联动伸缩布局组件

Posted 化作孤岛的瓜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android——基于LinearLayout实现的可联动伸缩布局组件相关的知识,希望对你有一定的参考价值。

 

首先先预览一下实现的效果:灰色区域是设置的分割线,可以支持设置分割线的粗度和颜色属性:

 

<declare-styleable name="ZoomLayout">
        <attr name="IntervalLineWidth" format="reference" />
        <attr name="IntervalLineColor" format="reference" />
    </declare-styleable>

实现思路:

1.首先在线性布局里的child中插入分割线view,根据设置的分割线粗和颜色进行设置。

2.增加分割线view的点击事件setOnTouchListener,比如,当手指向左移动时,缩小当前分割线左边子view的宽度,增大当前分割线右边子view的宽度,以实现分割线的左右移动和左右子view的横轴改变。联动拖动:当左边拖动到最小值(比如左边的子view 设置的minWidth是10dp)时,会判断左边所有的子view宽度是否达到了最小值,如果没有,则同时减少宽度,实现联动拖动的效果。

全部代码如下:

package com.ng.nguilib

import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout

/**
 * 描述:可缩放的layout
 * @author Jzn
 * @date 2020/9/9
 */
class ZoomLayout  constructor(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) 
    //子layout列表
    private var mChildLayoutList: ArrayList<View> = arrayListOf()
    private var mIntervalList: ArrayList<View> = arrayListOf()

    //起始点位置
    private var mStartX = 0f
    private var mStartY = 0f

    //位移
    private var mIntervalX = 0f
    private var mIntervalY = 0f

    //分割线是否添加过
    private var hadAdd = false

    //保存每个子view的宽度
    private var mChildWidthList: ArrayList<Int> = arrayListOf()
    private var mChildHeightList: ArrayList<Int> = arrayListOf()

    //变化中的子view宽度
    private var mRunningXList: ArrayList<Int> = arrayListOf()
    private var mRunningYList: ArrayList<Int> = arrayListOf()


    //params
    private var mIntervalLineWidth = 1
    private var mIntervalLineColor = 1

    init 
        val ta = context.obtainStyledAttributes(attrs, R.styleable.ZoomLayout)
        mIntervalLineWidth = context.resources.getDimensionPixelOffset(ta.getResourceId(R.styleable.ZoomLayout_IntervalLineWidth, R.dimen.dd10))
        mIntervalLineColor = ta.getColor(R.styleable.ZoomLayout_IntervalLineColor, Color.BLACK)
        ta.recycle()
    

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) 
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        refreshChildList()
        addSplit()
        refreshChildSizeList()
        val maxSize = if (measuredHeight > measuredWidth) measuredHeight else measuredWidth
        //修正分割线宽度
        mIntervalList.forEachIndexed  _, child ->
            val lp: ViewGroup.LayoutParams = child.layoutParams
            if (orientation == HORIZONTAL) 
                lp.height = maxSize
             else if (orientation == VERTICAL) 
                lp.width = maxSize
            
            child.layoutParams = lp
        
    

    //刷新子view数组
    private fun refreshChildList() 
        if (mChildLayoutList.size != childCount) 
            mChildLayoutList.clear()
            for (i in 0 until childCount) 
                val childView: View = getChildAt(i)
                mChildLayoutList.add(childView)
            
        
    

    //刷新子view size
    private fun refreshChildSizeList() 
        if (mChildWidthList.size != childCount) 
            mChildWidthList.clear()
            mChildLayoutList.forEachIndexed  _, child ->
                mChildWidthList.add(child.measuredWidth)
            
            mRunningXList = mChildWidthList
        
        if (mChildHeightList.size != childCount) 
            mChildHeightList.clear()
            mChildLayoutList.forEachIndexed  _, child ->
                mChildHeightList.add(child.measuredHeight)
            
            mRunningYList = mChildHeightList
        
    

    //在子view中设置操作分割线
    private fun addSplit() 
        if (mChildLayoutList.size == childCount && !hadAdd) 
            //在子view的间距中添加操作view
            mChildLayoutList.forEachIndexed  index, child ->
                if (index < mChildLayoutList.size - 1) 
                    addIntervalLine(index, child)
                
            
            hadAdd = true
        
    

    //增加垂直分割线
    private fun addIntervalLine(number: Int, child: View) 
        val interValView = View(context)
        interValView.setBackgroundColor(mIntervalLineColor)
        var lp: ViewGroup.LayoutParams = LayoutParams(measuredWidth, mIntervalLineWidth)
        if (orientation == HORIZONTAL) 
            lp = LayoutParams(mIntervalLineWidth, measuredHeight)
         else if (orientation == VERTICAL) 
            lp = LayoutParams(measuredWidth, mIntervalLineWidth)
        
        interValView.layoutParams = lp
        val tarGetLocation = IntArray(2)
        child.getLocationOnScreen(tarGetLocation)
        if (orientation == HORIZONTAL) 
            interValView.x = tarGetLocation[0].toFloat()
         else if (orientation == VERTICAL) 
            interValView.y = tarGetLocation[1].toFloat()
        
        val realIndex = 1 + number * 2
        interValView.setOnTouchListener  view, motionEvent ->
            when (motionEvent.action) 
                MotionEvent.ACTION_DOWN -> 
                    mStartX = motionEvent.x
                    mStartY = motionEvent.y
                
                MotionEvent.ACTION_UP -> 
                    refreshChildSizeList()
                    view.performClick()
                
            
            mIntervalX = mStartX - motionEvent.x
            mIntervalY = mStartY - motionEvent.y
            if (orientation == HORIZONTAL) 
                if (isChildValueLegal(mRunningXList[realIndex - 1] - mIntervalX.toInt(), realIndex - 1) &&
                        isChildValueLegal(mRunningXList[realIndex + 1] + mIntervalX.toInt(), realIndex + 1)
                ) 
                    mRunningXList[realIndex - 1] -= mIntervalX.toInt()
                    mRunningXList[realIndex + 1] += mIntervalX.toInt()
                
                // 联动调整左边
                if (!isChildValueLegal(mRunningXList[realIndex - 1] - mIntervalX.toInt(), realIndex - 1)) 
                    gravity = Gravity.START
                    var fixMulti = 0
                    if (realIndex - 2 > 0) 
                        for (index in 0..realIndex - 2) 
                            //这里要判断是否是分割线
                            if (index % 2 == 0 && isChildValueLegal(mRunningXList[index] - mIntervalX.toInt(), index)) 
                                mRunningXList[index] -= mIntervalX.toInt()
                                fixMulti++
                            
                        
                        mRunningXList[realIndex + 1] += mIntervalX.toInt() * fixMulti
                    
                
                //联动调整右边
                if (!isChildValueLegal(mRunningXList[realIndex + 1] + mIntervalX.toInt(), realIndex + 1)) 
                    gravity = Gravity.END
                    var fixMulti = 0
                    for (index in (realIndex + 2) until childCount) 
                        if (index % 2 == 0 && isChildValueLegal(mRunningXList[index] + mIntervalX.toInt(), index)) 
                            mRunningXList[index] += mIntervalX.toInt()
                            fixMulti++
                        
                    
                    mRunningXList[realIndex - 1] -= mIntervalX.toInt() * fixMulti
                
             else if (orientation == VERTICAL) 
                if (isChildValueLegal(mRunningYList[realIndex - 1] - mIntervalY.toInt(), realIndex - 1) &&
                        isChildValueLegal(mRunningYList[realIndex + 1] + mIntervalY.toInt(), realIndex + 1)) 
                    mRunningYList[realIndex - 1] -= mIntervalY.toInt()
                    mRunningYList[realIndex + 1] += mIntervalY.toInt()
                
                // 联动调整上面
                if (!isChildValueLegal(mRunningYList[realIndex - 1] - mIntervalY.toInt(), realIndex + 1)) 
                    gravity = Gravity.TOP
                    var fixMulti = 0
                    if (realIndex - 2 > 0) 
                        for (index in 0..realIndex - 2) 
                            if (index % 2 == 0 && isChildValueLegal(mRunningYList[index] - mIntervalY.toInt(), index)) 
                                mRunningYList[index] -= mIntervalY.toInt()
                                fixMulti++
                            
                        
                        mRunningYList[realIndex + 1] += mIntervalY.toInt() * fixMulti
                    
                
                // 联动调整下面
                if (!isChildValueLegal(mRunningYList[realIndex + 1] + mIntervalY.toInt(), realIndex + 1)) 
                    gravity = Gravity.BOTTOM
                    var fixMulti = 0
                    for (index in (realIndex + 2) until childCount) 
                        if (index % 2 == 0 && isChildValueLegal(mRunningYList[index] + mIntervalY.toInt(), index)) 
                            mRunningYList[index] += mIntervalY.toInt()
                            fixMulti++
                        
                    
                    mRunningYList[realIndex - 1] -= mIntervalY.toInt() * fixMulti
                
            
            mChildLayoutList.forEachIndexed  index, child ->
                val childLp: LayoutParams = child.layoutParams as LayoutParams
                childLp.weight = 0f
                if (orientation == HORIZONTAL) 
                    childLp.width = mRunningXList[index]

                 else if (orientation == VERTICAL) 
                    childLp.height = mRunningYList[index]
                

                child.layoutParams = childLp
            
            //防止左越界
            if (mChildLayoutList.size != 0) 
                mChildLayoutList[0].x = 0f
            
            true
        
        mIntervalList.add(interValView)
        addView(interValView, realIndex, lp)
    

    private fun isChildValueLegal(value: Int, index: Int): Boolean 
        val minZoom = if (orientation == HORIZONTAL) 
            mChildLayoutList[index].minimumWidth
         else 
            mChildLayoutList[index].minimumHeight
        
        return value > minZoom
    

代码下载和效果预览地址:

https://github.com/jiangzhengnan/NguiLib

求start~求fork~

也欢迎提交~

以上是关于Android——基于LinearLayout实现的可联动伸缩布局组件的主要内容,如果未能解决你的问题,请参考以下文章

Android学习——LinearLayout布局实现居中左对齐右对齐

Android LinearLayout实现下拉刷新

Android布局管理器-使用LinearLayout实现简单的登录窗口布局

Android LinearLayout布局 实现十二生肖的跳转

android 开发: 怎么把一个linearlayout设置为不可点击??

Android 自动换行的LinearLayout