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

    也欢迎提交~

  • 相关阅读:
    句子反转
    python中计时模块timeit的使用方法
    python入门(一)
    将小程序的API封装成支持Promise的API
    微信小程序实现导航功能的操作步骤
    微信小程序朋友转发和朋友圈分享
    js原生上传图片
    FormData
    原生 websocket
    判断手机终端是pc还是移动端,并自动跳转
  • 原文地址:https://www.cnblogs.com/huilixieqi/p/13754778.html
Copyright © 2020-2023  润新知