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布局 实现十二生肖的跳转