Customize View FlowLayout

1, Renderings

1. Effect 1: the corresponding usage method 1 (add sub View label effect in the layout):

2. Effect 2: corresponding mode 2 (add sub View label effect in code):

The two effects are the same, just to show that the flow layout adds sub View in the layout and adds sub View in the code. Both implementation methods are easy to use

2, How to use

1. Add child View label to layout file

<com.babycy.flowlayout.FlowLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Android Exploration of development Art"
            android:textAllCaps="false" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Deep understanding Java virtual machine"
            android:textAllCaps="false" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Android Analysis and practice of source code design pattern"
            android:textAllCaps="false" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Java Multithreading core programming technology"
            android:textAllCaps="false" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="C++ Primer"
            android:textAllCaps="false" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Deep understanding Android"
            android:textAllCaps="false" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Zen of design pattern" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Http Authoritative guide"
            android:textAllCaps="false" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Android Scenario analysis of system source code"
            android:textAllCaps="false" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ES6 Standard entry"
            android:textAllCaps="false" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="FFmpeg From entry to mastery"
            android:textAllCaps="false" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Android Application of safety protection and reverse analysis"
            android:textAllCaps="false" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="JavaScript Advanced programming"
            android:textAllCaps="false" />

    </com.babycy.flowlayout.FlowLayout>

2. Add child View label to the code

public class AutoActivity extends AppCompatActivity {

    private FlowLayout mFlowLayout;

    private String[] mBooks = {
            "Android Exploration of development Art",
            "Deep understanding Java virtual machine",
            "Android Analysis and practice of source code design pattern",
            "Java Multithreading core programming technology",
            "C++ Primer",
            "Deep understanding Android",
            "Zen of design pattern",
            "Http Authoritative guide",
            "Android Scenario analysis of system source code",
            "ES6 Standard entry",
            "FFmpeg From entry to mastery",
            "Android Application of safety protection and reverse analysis",
            "JavaScript Advanced programming"
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_auto);

        mFlowLayout = (FlowLayout) findViewById(R.id.fl);

        initShowView();
    }

    private void initShowView() {
        LayoutInflater inflater = LayoutInflater.from(this);
        for (int i = 0; i < mBooks.length; i++) {
            TextView tv = (TextView) inflater.inflate(R.layout.tv_tag, mFlowLayout, false);
            tv.setText(mBooks[i]);
            tv.setAllCaps(false);
            mFlowLayout.addView(tv);
        }
    }
}

Activity auto.xml (FlowLayout layout layout file)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.babycy.flowlayout.FlowLayout
        android:id="@+id/fl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

TV? Tag.xml (FlowLayout subview label layout file)

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="I'm a label"
    android:background="@drawable/tag_bg"
    android:layout_margin="5dp"/>

Tag bg.xml (FlowLayout sub View label background, storage location: res/drawable)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <solid android:color="#e7e7e7"/>
    <corners android:radius="30dp"/>
    <padding android:left="10dp"
        android:top="5dp"
        android:right="10dp"
        android:bottom="5dp"/>

</shape>

3, Implement custom View flow layout

public class FlowLayout extends ViewGroup {

    public FlowLayout(Context context) {
        this(context, null);
    }

    public FlowLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //Flow layout flow layout width mode
        int wm = MeasureSpec.getMode(widthMeasureSpec);
        //Flow layout FlowLayout width size (exact value or match < parent)
        int ws = MeasureSpec.getSize(widthMeasureSpec);
        //Flow layout flow layout height mode
        int hm = MeasureSpec.getMode(heightMeasureSpec);
        //Flow layout FlowLayout height size (exact value or match < parent)
        int hs = MeasureSpec.getSize(heightMeasureSpec);

        //Flow layout flow layout width size (Wrap & content)
        int width = 0;
        //Flow layout flow layout height size (Wrap & content)
        int height = 0;

        //Number of child views
        int count = getChildCount();
        //Row width
        int lineWidth = 0;
        //Row height
        int lineHeight = 0;

        //Traversal subview
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            //Survey subview
            measureChild(child, widthMeasureSpec, heightMeasureSpec);

            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            //Exceed the FlowLayout width after adding the current child View width to the current line width lineWidth
            if (childWidth + lineWidth > ws - getPaddingLeft() - getPaddingRight()) {
                //When breaking a line (Note: only the width and height before line breaking are saved here, so if the current line is the last line, it can be judged that when the current sub View is the last sub View, the line width and line height of the last line are updated to the FlowLayout width and height)
                //Each time a line is wrapped, the FlowLayout width takes the maximum value of the last saved width and the line width of the previous line
                width = Math.max(width, lineWidth);
                //FlowLayout height accumulate row height
                height += lineHeight;

                //Reset row width to current subview width
                lineWidth = childWidth;
                //Reset row height to current child View height
                lineHeight = childHeight;
            } else {
                //When there is no line break
                //Here, only line width and line height are recorded, which will be updated in FlowLayout width and FlowLayout height when breaking lines
                //Row width add child control width
                lineWidth += childWidth;
                //Row height takes the maximum of the last saved row height and the current subview height
                lineHeight = Math.max(childHeight, lineHeight);
            }

            //When the current is the last control, update the line width and line height of the last line to the FlowLayout width and height
            if (i == count - 1) {
                //FlowLayout width takes the maximum of the first and last line width
                width = Math.max(width, lineWidth);
                //FlowLayout height
                height += lineHeight;
            }

            //Set FlowLayout width and height (remember to add padding)
            setMeasuredDimension(wm == MeasureSpec.EXACTLY ? ws : width + getPaddingLeft() + getPaddingRight(), hm == MeasureSpec.EXACTLY ? hs : height + getPaddingTop() + getPaddingBottom());
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //Number of child views
        int count = getChildCount();
        //Row width
        int lineWidth = 0;
        //Row height
        int lineHeight = 0;
        //top of child View
        int top = 0;
        //left of child View
        int left = 0;

        //Traversal subview
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            //Width of the current subview
            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            //Height of current subview
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

            //Line feed
            if (childWidth + lineWidth > getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) {
                //The top of the first subview of a new row accumulates the row height of the previous row
                top += lineHeight;
                //The left of the first child View of the new row is reset to 0
                left = 0;

                //Reset the row width to the width of the current subview
                lineWidth = childWidth;
                //Row height reset to the height of the current subview
                lineHeight = childHeight;
            } else {
                //Row width add the width of subview
                lineWidth += childWidth;
                //Row height takes the maximum of the last saved row height and the current subview width
                lineHeight = Math.max(lineHeight, childHeight);
            }

            //left of child View
            int cl = left + lp.leftMargin + getPaddingLeft();
            //top of child View
            int ct = top + lp.topMargin + getPaddingTop();
            //right of child View
            int cr = cl + child.getMeasuredWidth();
            //bottom of child View
            int cb = ct + child.getMeasuredHeight();
            //Place the current subview according to left, top, right and bottom
            child.layout(cl, ct, cr, cb);
            //The left of the current subview of the current row accumulates the width of the previous subview
            left += childWidth;
        }
    }

    /**
     * Override generateLayoutParams(), generateDefaultLayoutParams()
     *
     * The custom ViewGroup must overload the generateLayoutParams() function if it wants to support the layout? Margin parameter of the child control
     * In this function, a class object derived from ViewGroup.MarginLayoutParams is returned, so that the margin parameter can be used
     *
     * The default generateLayoutParams() function only extracts the values of layout width and layout height
     * Only MarginLayoutParams() can extract margin
     */
    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return new MarginLayoutParams(p);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }
}

Tags: Android xml Java Programming

Posted on Thu, 19 Mar 2020 13:02:10 -0400 by ashok_bam