美文网首页
自定义FlowLayout

自定义FlowLayout

作者: 被虐的小鸡 | 来源:发表于2020-08-25 18:53 被阅读0次

    先将代码附上,抽空再细讲内部实现

    主要实现了流式布局,并且考虑了子View的margin和gravity,自定义了LayoutParams。

    自定义FlowLayout主要代码

    package net.test.kotlintest.flow
    
    import android.content.Context
    import android.util.AttributeSet
    import android.util.Log
    import android.view.Gravity
    import android.view.View
    import android.view.ViewGroup
    import androidx.annotation.InspectableProperty
    import net.test.kotlintest.R
    import java.util.ArrayList
    import kotlin.math.max
    
    class FlowLayout(context:Context) : ViewGroup(context) {
        private val lines = ArrayList<Line>()
    
        constructor(context: Context, attrs: AttributeSet?): this(context)
    
        constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): this(context)
    
        override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
            var left=paddingLeft
            var top=paddingTop
            var childTop=0
            for(line in lines){
                val views = line.getViews()
                for(view in views){
                    val layoutParams:LayoutParams= view.layoutParams as LayoutParams
    
                    Log.e("man",layoutParams.rightMargin.toString())
    
                    var gravity = layoutParams.gravity
                    if (gravity == -1) {
                        gravity = DEFAULT_CHILD_GRAVITY
                    }
                    Log.e("man", "before gravity$gravity")
                    val verticalGravity = gravity?.and(Gravity.VERTICAL_GRAVITY_MASK)
    
                    childTop = when (verticalGravity) {
                        Gravity.TOP -> top+layoutParams.topMargin
                        Gravity.CENTER_VERTICAL -> (line.getHeight()-view.measuredHeight)/2+top+layoutParams.topMargin
                        Gravity.BOTTOM -> line.getHeight()-view.measuredHeight+top+layoutParams.topMargin
                        else -> top+layoutParams.topMargin
                    }
    
                    Log.e("man", "gravity$verticalGravity")
    
                    view.layout(left+layoutParams.leftMargin,childTop,left+view.measuredWidth+layoutParams.leftMargin,view.measuredHeight+childTop)
    
                    left+=view.measuredWidth+ HORIZONTAL_SIZE+layoutParams.leftMargin+layoutParams.rightMargin
                }
    
                left=paddingLeft
                top+=line.getHeight()+ VERTICAL_SIZE
            }
    
        }
    
    
        override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
            lines.clear()
            val childCount:Int=childCount
            val leftPadding:Int=paddingLeft
            val rightPadding:Int=paddingRight
            val topPadding:Int=paddingTop
            val bottomPadding:Int=paddingBottom
    
            var useWidth=0
            val selfHeight=MeasureSpec.getSize(heightMeasureSpec)
            val selfWidth=MeasureSpec.getSize(widthMeasureSpec);
            var line= Line()
    
            var totalHeight=0
            var totalWidth=0
    
            for (i in 0 until childCount){
                val view:View=getChildAt(i)
    
                if (view.visibility==View.GONE){
                    continue
                }
                val layoutParams:LayoutParams = view.layoutParams as LayoutParams
    
                val childWidthMeasureSpec = getChildMeasureSpec(
                    widthMeasureSpec,
                    leftPadding + rightPadding,
                    layoutParams.width
                )
    
                val childHeightMeasureSpec = getChildMeasureSpec(
                    heightMeasureSpec,
                    topPadding + bottomPadding,
                    layoutParams.height
                )
    
                view.measure(childWidthMeasureSpec,childHeightMeasureSpec)
    
                if (useWidth+view.measuredWidth+ Companion.HORIZONTAL_SIZE+layoutParams.leftMargin+layoutParams.rightMargin + leftPadding + rightPadding >selfWidth){
                    totalWidth= max(totalWidth,useWidth)
                    totalHeight+=line.getHeight()+ Companion.VERTICAL_SIZE
                    lines.add(line)
                    line= Line()
                    useWidth=0
                }
                line.setHeight(max(line.getHeight(),view.measuredHeight+layoutParams.topMargin+layoutParams.bottomMargin))
                line.addView(view)
                useWidth += view.measuredWidth + Companion.HORIZONTAL_SIZE+layoutParams.leftMargin+layoutParams.rightMargin
    
    
                if (i==childCount-1){
                    totalWidth= max(totalWidth,useWidth)
                    totalHeight+=line.getHeight()+ Companion.VERTICAL_SIZE
                    lines.add(line)
                }
            }
    
    
            val height=if (MeasureSpec.getMode(heightMeasureSpec)==MeasureSpec.EXACTLY) selfHeight     else totalHeight+topPadding + bottomPadding
            val width=if (MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.EXACTLY) selfWidth else totalWidth+leftPadding + rightPadding
    
    
            setMeasuredDimension(width,height)
    
    
        }
    
        override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
            return LayoutParams(context,attrs)
        }
    
    
        override fun generateLayoutParams(p: ViewGroup.LayoutParams?): ViewGroup.LayoutParams {
            return super.generateLayoutParams(p)
        }
    
        internal class Line{
            private var lineHeight:Int=0
    
            private val views = ArrayList<View>()
    
    
            fun addView(view: View){
                views.add(view)
            }
    
            fun getViews(): ArrayList<View> {
                return views
            }
    
            fun setHeight(height:Int){
                this.lineHeight=height
            }
    
            fun getHeight():Int{
                return lineHeight
            }
        }
    
        companion object {
            private const val VERTICAL_SIZE:Int=10
            private const val HORIZONTAL_SIZE:Int=10
            private const val DEFAULT_CHILD_GRAVITY = Gravity.TOP or Gravity.START
        }
    
        class LayoutParams : MarginLayoutParams {
    
    //        @InspectableProperty(
    //            name = "layout_gravity",
    //            valueType = InspectableProperty.ValueType.GRAVITY)
            var gravity:Int? = UNSPECIFIED_GRAVITY
    
            constructor(c: Context?, attrs: AttributeSet?) : super(c, attrs){
                val a = c?.obtainStyledAttributes(attrs, R.styleable.FlowLayout_Layout)
                gravity = a?.getInt(R.styleable.FlowLayout_Layout_layout_gravity,
                    Companion.UNSPECIFIED_GRAVITY
                )
                a?.recycle()
            }
    
            constructor(width: Int, height: Int) : super(width, height)
            constructor(source: MarginLayoutParams?) : super(source)
            constructor(source: ViewGroup.LayoutParams?) : super(source)
    
            constructor(source: LayoutParams): super(source) {
                this.gravity = source.gravity
            }
    
            constructor(width: Int, height: Int, gravity: Int): this(width, height) {
                this.gravity = gravity
            }
            companion object {
                const val UNSPECIFIED_GRAVITY = -1
            }
    
    
        }
    
    }
    

    自定义layout_gravity属性

    <declare-styleable name="FlowLayout_Layout">
            <attr name="layout_gravity">
                <enum name="center_vertical" value="16"></enum>
                <enum name="top" value="48"></enum>
                <enum name="bottom" value="80"></enum>
            </attr>
        </declare-styleable>
    

    布局中使用

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:padding="20dp"
        android:layout_height="match_parent">
    
        <net.test.kotlintest.flow.FlowLayout
            android:layout_width="match_parent"
            android:layout_gravity="bottom"
            android:layout_height="wrap_content">
            <TextView
                android:padding="10dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="a"
                android:visibility="gone"
                android:layout_marginRight="10dp"
                android:background="#ff0000"/>
            <TextView
                android:padding="15dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="qqqq"
                android:layout_marginTop="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginBottom="10dp"
                android:background="#ff0000"/>
            <TextView
                android:padding="10dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="aaaaaaa"
                app:layout_gravity="center_vertical"
                android:background="#ff0000"/>
            <TextView
                android:padding="10dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_gravity="bottom"
                android:text="aaaaaaaaaa"
                android:background="#ff0000"/>
            <TextView
                android:padding="10dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="eeeeeeee"
                android:background="#ff0000"/>
            <TextView
                android:padding="10dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tttttttttttttttt"
                android:background="#ff0000"/>
            <TextView
                android:padding="10dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="yyyy"
                android:background="#ff0000"/>
            <TextView
                android:padding="10dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="uuu"
                android:background="#ff0000"/>
            <TextView
                android:padding="10dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="i"
                android:background="#ff0000"/>
    
            <TextView
                android:padding="10dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvbbbbbbbbbbbbbbbbb"
                android:background="#ff0000"/>
        </net.test.kotlintest.flow.FlowLayout>
    </LinearLayout>
    

    最终结果

    运行结果.png

    相关文章

      网友评论

          本文标题:自定义FlowLayout

          本文链接:https://www.haomeiwen.com/subject/dewzjktx.html