欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-03-03 22:14:37
首先先预览一下实现的效果:红色区域是设置的分割线,可以支持设置分割线的粗度和颜色属性:

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

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~

也欢迎提交~

本文地址:https://blog.csdn.net/qq_22770457/article/details/108560992