Android custom View implements multi-line RadioGroup (MultiLineRadioGroup) and android custom view

Source: Internet
Author: User

Android custom View implements multi-line RadioGroup (MultiLineRadioGroup) and android custom view

I. Project Overview

We all know that RadioGroup can implement a selection box, but it has a limitation. Because it inherits from LinearLayout, it can only have one direction, either horizontally or vertically; however, sometimes only one row of RadioGroup cannot meet the actual needs. For example, if you cannot display all the options under the width of one row, you cannot slide between the left and right sides in design, at this time, RadioGroup cannot meet such functional design. Based on this, I wrote this MultiLineRadioGroup and made it open-source;

1. Program Interface

 

 

2. functional interfaces

The functions that can be used in Api development and what I can think of have been added. The details are as follows:

  • Add or delete the child Option
  • Select the child option. deselect the child option.
  • Child alignment (left | Middle | right)
  • Child row spacing, left and right spacing
  • Set the selection mode (single choice | multiple choice)
  • Get selected options
  • Check the status of the listener selected by the child

3. Demo URL

Https://github.com/a284628487/MultiLineRadioGroup

 

2. Project Analysis

1. Based on the above functional design, some custom attributes are added for ease of design;

    <declare-styleable name="MultiLineRadioGroup">        <attr name="child_margin_horizontal" format="dimension" />        <attr name="child_margin_vertical" format="dimension" />        <attr name="child_layout" format="integer" />        <attr name="child_count" format="integer" />        <attr name="child_values" format="integer" />        <attr name="single_choice" format="boolean" />        <attr name="gravity" format="integer" />    </declare-styleable>

The preceding custom attributes represent

  • Child Horizontal spacing
  • Child spacing
  • Layout file corresponding to child (this attribute must be configured later)
  • Number of initial elements
  • Initial element value list
  • Select mode (single choice | multiple choice)
  • Child alignment

2. Use MultiLineRadioGroup in layout

(1) define an xml file containing MultiLineRadioGroup

    <org.ccflying.MultiLineRadioGroup        android:id="@+id/content"        android:layout_width="match_parent"        android:layout_height="wrap_content"        app:child_layout="@layout/child"        app:child_margin_horizontal="6.0dip"        app:child_margin_vertical="2.0dip"        app:child_values="@array/childvalues"        app:single_choice="true" >    </org.ccflying.MultiLineRadioGroup>

(2) define a layout file whose root node is CheckBox and set the file id to the child_layout attribute of MultiLineRadioGroup (Note: This attribute must be set)

<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:background="@drawable/bg"    android:button="@null"    android:padding="8.0dip"    android:textColor="@color/text_color" ></CheckBox>

In MultiLineRadiaGroup, the child element of MultiLineRadiaGroup is CheckBox. Therefore, you must specify the child_layout for layout as CheckBox. This CheckBox can set the style in different States according to your needs;

 

3. Core MultiLineRadioGroup Method Analysis

(1) onMeasure

    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        childCount = getChildCount();        int flagX = 0, flagY = 0, sheight = 0;        if (childCount > 0) {            for (int i = 0; i < childCount; i++) {                View v = getChildAt(i);                measureChild(v, widthMeasureSpec, heightMeasureSpec);                int w = v.getMeasuredWidth() + childMarginHorizontal * 2                        + flagX + getPaddingLeft() + getPaddingRight();                if (w > getMeasuredWidth()) {                    flagY++;                    flagX = 0;                }                sheight = v.getMeasuredHeight();                flagX += v.getMeasuredWidth() + childMarginHorizontal * 2;            }            rowNumber = flagY;        }        int height = (flagY + 1) * (sheight + childMarginVertical)                + childMarginVertical + getPaddingBottom() + getPaddingTop();        setMeasuredDimension(getMeasuredWidth(), height);    }

Traverse all the children and call measureChild to measure the child's width and height. Then, compare the accumulated width with the value of getWidth to determine whether a line break is required, record the number of rows to be used;

(2) onLayout

    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        if (!changed && !forceLayout) {            Log.d("tag", "onLayout:unChanged");            return;        }        childCount = getChildCount();        int[] sX = new int[rowNumber + 1];        if (childCount > 0) {            if (gravity != LEFT) {                for (int i = 0; i < childCount; i++) {                    View v = getChildAt(i);                    int w = v.getMeasuredWidth() + childMarginHorizontal * 2                            + mX + getPaddingLeft() + getPaddingRight();                    if (w > getWidth()) {                        if (gravity == CENTER) {                            sX[mY] = (getWidth() - mX) / 2;                        } else { // right                            sX[mY] = (getWidth() - mX);                        }                        mY++;                        mX = 0;                    }                    mX += v.getMeasuredWidth() + childMarginHorizontal * 2;                    if (i == childCount - 1) {                        if (gravity == CENTER) {                            sX[mY] = (getWidth() - mX) / 2;                        } else { // right                            sX[mY] = (getWidth() - mX);                        }                    }                }                mX = mY = 0;            }            for (int i = 0; i < childCount; i++) {                View v = getChildAt(i);                int w = v.getMeasuredWidth() + childMarginHorizontal * 2 + mX                        + getPaddingLeft() + getPaddingRight();                if (w > getWidth()) {                    mY++;                    mX = 0;                }                int startX = mX + childMarginHorizontal + getPaddingLeft()                        + sX[mY];                int startY = mY * v.getMeasuredHeight() + (mY + 1)                        * childMarginVertical;                v.layout(startX, startY, startX + v.getMeasuredWidth(), startY                        + v.getMeasuredHeight());                mX += v.getMeasuredWidth() + childMarginHorizontal * 2;            }        }        mX = mY = 0;        forceLayout = false;    }

Like onMeasure, The onLayout method also needs to traverse the child. However, the traversal here is not a measurement, but a placement of the child, during placement, the width and height attributes of the Child Elements measured in the onMeasure method must be used;

Traversal may be performed twice. If the child alignment is not Left, the first traversal calculates the gaps in each row, then, the left offset distance of the first child in each row is calculated based on alignment. During the second traversal, the child is layout based on the previously calculated offset distance;

(3) Other methods

  • Append (String str) attaches a child;
  • Insert (int position, String str) insert child to the specified position;
  • GetCheckedValues () | getCheckedItems () to obtain the selected item;
  • Remove (int position) deletes the child at the specified position;
  • SetItemChecked (int position) selects the child at the specified position;
  • SetGravigy (int gravity) sets the child alignment mode;

These methods are implemented based on common or possibly used methods. They are relatively simple and no longer post code, which is available in the above Demo links;

 

 

Over!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.