用户登录
用户注册

分享至

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

  • 作者: 姑孰秋雨
  • 来源: 51数据库
  • 2021-09-05

首先先预览一下实现的效果:

红色区域是设置的分割线,可以支持设置分割线的粗度和颜色属性:

<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~

也欢迎提交~

软件
前端设计
程序设计
Java相关