AndroidFormView

Android form view use to forms

The development process encountered a large number of form controls. So pull out the form from the original project. Refactoring code to simplify structure. There was a slight problem with the extraction, which will be covered later. Hope to use this project in the process, to help you build a form page faster. Save time for implementing business logic and writing more elegant and concise code. If you have any questions, please submit Issues and I will help you. If it’s any indication, you can also encapsulate your own business code into a component library for easy management.

How to reference


implementation 'com. Markfrain. Utils: formview: 1.0.0'

Copy the code

Project source code address

Form component scenario

Suitable for workflow, business process page, report page data entry. This project takes away the most basic forms and sets of uniform styles and multiple attributes to make the UI component presentation consistent.

The code structure

--package
|-utils
|-view
	|-FormView // Form abstract base class
		|-FormEditViewOther variants of the input box form are based on this type of implementation
			|-FormEditMultiView // At the bottom is the input box form of the input box
			|-FormEditTipsView	// Input box form with prompt at the bottom
		|-FormImageView  // Picture form
		|-FormRadioView  // A checkbox form
		|-FormSwitchView / / the switch form
	|-FormViewInterface // The form interface method extracts layoutId and initView
	|-FormClickListener The FormLongClickListener method inherits this interface
Copy the code

FormView

When you pull out all the forms, you can think of them as four controls.

1. Left icon (for example, asterisk, or image) IV_left. 2. Title (e.g. name/circle of friends/Settings) tv_title. 3. Form content (such as input field, picture, text, option box, switch, etc.) 4. Icon on the right (for example, the right arrow or various ICONS)iv_right.

Subclasses inherit from FormView, so they have all of their attribute characteristics. The parent control of each layout page is a LinearLayout. Except for the Parent Layout padding, all margins are set with the right margin.

The FormView Attribute as follows

Property field name Property description note
fv_left_image On the left side of the icon
fv_left_image_max_width Maximum width of left icon
fv_left_image_max_height Maximum height of left icon
fv_left_image_right_margin The right margin of the left icon
fv_left_image_scale_type Zoom type of the icon on the left The scaleType of the ImageView is consistent
fv_right_image The right side of the icon
fv_right_image_max_width Maximum width of the icon on the right
fv_right_image_max_height Maximum height of the icon on the right
fv_right_image_left_margin Maximum width of the icon on the right
fv_right_image_scale_type Zoom type of the icon on the right The scaleType of the ImageView is consistent
fv_left_padding Left-inside spacing of the form
fv_top_padding Internal spacing on the form
fv_bottom_padding Spacing within the form
fv_right_padding Spacing in the right of form
fv_title The title content
fv_title_width Title width mode Weight_content and wrap_content wrap_content can be used in combination with fv_TITle_min_width to give a fixed-width effect
fv_title_min_width Minimum title width
fv_title_text_size Title text size
fv_title_text_style Title text Style Bold, ITALic, and Normal
fv_title_text_width The width of the title text Panit Custom widths can be as thick as you want
fv_title_right_margin The right margin of the heading text
fv_title_color The color of the title text

FormView internal methods

The method name use
init(Context,AttributeSet) Initialize the layout and get the properties
initView(View) Control initializes findViewById
setMap(Map) Expose the external parameter method,FormClickListener and FormLongClickListener will return the value
setEnable(Boolean) Disable method, so that the details page can dynamically enable controls to edit or jump based on permissions
T getValue() Getting the value of a control, such as an input box form, returns the contents of the EditText, such as FormRadioView, returns selected status
initCustom() Subclasses override init(Context,AttributeSet) to extend the behavior of subclasses

The FormEditView input box form and its subclasses

The form content is an EditText. Depending on the scene, it can be single line, multiple lines, at the bottom, etc. The input box might be the amount, weight, height, etc. So the component also has a tvUnit TextView to display units.

FormEditView Attribute as follows

Property field name Property description note
fev_hint Input box prompt
fev_hint_color Input box prompt color
fev_text_gravity Input box text content positioning Top bottom is suitable for multi-line display with fev_text_lines>1,left right is suitable for single-line display
fev_text_lines The number of lines in the input box
fev_text_color Input box content color
fev_text_size Input box text size
fev_text_max_length Maximum number of words in the input box
fev_text_inputType Input content type Text, phone, Password, Identity card, number, Decimal
fev_text_right_margin Input box right margin
fev_text_bg Input field background
fev_unit_text Unit words
fev_unit_color Unit text color
fev_unit_text_size Unit text size
fev_unit_visible Is the unit visible
fev_unit_right_margin Unit text right margin

FormEditMultiView bottom multi-line input box

The layout structure is up and down. The left icon, title, and right icon are shown above. The following structure is relative layout, which has input box, word count TextView.

FormEditMultiView Attribute as follows

Property field name Property description note
femv_title_layout_left_padding Header layout left inside spacing
femv_title_layout_top_padding Internal spacing on header layout
femv_title_layout_right_padding Inside right spacing of header layout
femv_title_layout_bottom_padding Internal spacing under heading layout
femv_content_left_padding Left inner spacing of input box
femv_content_top_padding Inner spacing on input box
femv_content_right_padding Input box right inner spacing
femv_content_bottom_padding Input box spacing
femv_count_view_visible Visibility of word count
femv_count_left_text_color Word count/left text color (not included /)
femv_count_right_text_color Word count/Color of text on the right (including /)

FormEditTipsView has a prompt input box at the bottom

The layout structure is up and down. The left icon, title, and right icon are shown above. The following structure is TextView to display prompt text

FormEditTipsView Attribute as follows

Property field name Property description note
fetv_tips_top_margin Tip text margin
fetv_tips_text Prompt text content
fetv_tips_text_color Prompt text color
fetv_tips_text_size Prompt text size

FormImageView Image form

The attribute has only one fiv_src. This is the same as the SRC of the ImageView.

FormRadioView

FormRadioView Attribute as follows

Property field name Property description note
frv_layout_min_width The minimum width of RadioGroup. The default is wrap_content
frv_text_gravity RadioGroup Content alignment left/right. They’re all vertically centered
frv_text_drawable_padding The drawabPadding property of a radio button
frv_text_drawable_left Option Button icon on the left
frv_text_drawable_top Option Button icon on the upper side
frv_text_drawable_right Option Button icon on the right
frv_text_drawable_bottom Option Button icon on the lower side
frv_left_to_right_margin The right margin of the left radio button
frv_text_color Radio button text color
frv_text_size Radio button text size
frv_left_text Left radio button text content
frv_right_text Option button on the right text content

FormSwitchView

FormSwitchView Attribute as follows

Property field name Property description note
fsv_checkbox_drawable The CheckBox drawableLeft
fsv_checkbox_right_margin The right margin of the CheckBox

If you are using

According to the above attribute analysis, and view the source code. Every code has comments, I hope to help you.


	<com.markfrain.formview.view.FormSwitchView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="15dp"
            android:paddingTop="18dp"
            android:paddingRight="15dp"
            android:paddingBottom="18dp"
            app:fsv_checkbox_drawable="@drawable/checkbox_switch"
            app:fsv_checkbox_right_margin="10dp"
            app:fv_left_image="@drawable/ic_svg_star"
            app:fv_right_image="@drawable/ic_svg_right_arrow"
            app:fv_title="Test text title"
            app:fv_title_right_margin="15dp"
            app:fv_title_width="weight_content" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:background="@android:color/darker_gray" />

        <com.markfrain.formview.view.FormRadioView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="15dp"
            android:paddingTop="18dp"
            android:paddingRight="15dp"
            android:paddingBottom="18dp"
            app:frv_layout_min_width="150dp"
            app:frv_left_to_right_margin="10dp"
            app:frv_text_color="@color/radio_text_color"
            app:frv_text_drawable_left="@drawable/radio_check"
            app:frv_text_drawable_padding="10dp"
            app:fv_left_image="@drawable/ic_svg_star"
            app:fv_right_image="@drawable/ic_svg_right_arrow"
            app:fv_title="Test text title"
            app:fv_title_right_margin="15dp"
            app:fv_title_width="weight_content" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:background="@android:color/darker_gray" />

        <com.markfrain.formview.view.FormImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="15dp"
            android:paddingTop="18dp"
            android:paddingRight="15dp"
            android:paddingBottom="18dp"
            app:fiv_src="@mipmap/base_iv_header_member"
            app:fv_left_image="@drawable/ic_svg_star"
            app:fv_right_image="@drawable/ic_svg_right_arrow"
            app:fv_title="Test text title"
            app:fv_title_right_margin="15dp" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:background="@android:color/darker_gray" />

        <com.markfrain.formview.view.FormEditView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="15dp"
            android:paddingTop="18dp"
            android:paddingRight="15dp"
            android:paddingBottom="18dp"
            app:fev_hint="Test prompt text"
            app:fev_text_bg="@null"
            app:fev_text_gravity="top"
            app:fev_unit_text="Unit"
            app:fv_left_image="@drawable/ic_svg_star"
            app:fv_right_image="@drawable/ic_svg_right_arrow"
            app:fv_title="Test text title"
            app:fv_title_right_margin="15dp" />

Copy the code

About RadioButton sharing Drawable results. There is a BUG in switching the presentation.

The FormRadioView currently has the following four properties.

 	<attr name="frv_text_drawable_left" format="reference" />
        <attr name="frv_text_drawable_top" format="reference" />
        <attr name="frv_text_drawable_right" format="reference" />
        <attr name="frv_text_drawable_bottom" format="reference" />
        <attr name="frv_left_to_right_margin" format="dimension" />
Copy the code

Earlier, I wrote the following code: after obtaining the Drawable, two RadioButtons set the Drawable at the same time.

     
    rbLeft.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, topDrawable, rightDrawable, bottomDrawable);
	rbRight.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, topDrawable, rightDrawable, bottomDrawable);

Copy the code

There is nothing wrong at first glance. Interested friends, you can try their own implementation.

I’ll just say the result:

1. Setting the same Drawable causes both RadioButtons to hold references to Drawable

2. When toggle Drawable, due to shared references. RadioButton’s Drawable is inconsistent with its mChecked.

Why is that?

1. Debug the fault location. Since the Drawable is set to a selector, the StateListDrawable is obtained after typeDarray.getDrawable.

2. Next, RadioButton looks up the parent class. Read TextView source code key code is as follows:

	public TextView(
            Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes); .Typedarray. getDrawable drawableLeft, drawableTop, drawableRight, drawableBottom Set to TextView
        setCompoundDrawablesWithIntrinsicBounds(
                drawableLeft, drawableTop, drawableRight, drawableBottom);
    }
    / / 2. The setCompoundDrawablesWithIntrinsicBounds last execution to this method
     public void setCompoundDrawables(@Nullable Drawable left, @Nullable Drawable top,
            @Nullable Drawable right, @Nullable Drawable bottom) {...if(left ! =null) {... left.setCallback(this);
	        //3. There is a callback function called setCallBack
	        Public class View implements Drawable.Callback. }}//4. Look at the StateListDrawable class again
	// There must be a problem with the state-related methods
	@Override
    protected boolean onStateChange(int[] stateSet) {
        // Get the state array index from the state
        intidx = mStateListState.indexOfStateSet(stateSet); .SelectDrawable (idx); selectDrawable(idx);

        return selectDrawable(idx) || changed;
    }
    //5. Trace to the DrawableContainer class via selectDrawable
    public boolean selectDrawable(int index) {... invalidateSelf();// Look at the name called redraw yourself
    }
    //6. Trace to the Drawable class
    public void invalidateSelf(a) {
        final Callback callback = getCallback();
        if(callback ! =null) {
            callback.invalidateDrawable(this); }}TextView's invalidateDrawable overwrites the View method
    @Override
    public void invalidateDrawable(@NonNull Drawable drawable) {}//8. CompoundButton source code:
     @Override
    public void setChecked(boolean checked) {... refreshDrawableState(); . }public void refreshDrawableState(a) {... drawableStateChanged(); . }DrawableStateChanged is overridden in CompoundButton
	@Override
    protected void drawableStateChanged(a) {
        super.drawableStateChanged();

        final Drawable buttonDrawable = mButtonDrawable;
        if(buttonDrawable ! =null&& buttonDrawable.isStateful() && buttonDrawable.setState(getDrawableState())) { invalidateDrawable(buttonDrawable); }}/ / 10. ButtonDrawable. SetState (getDrawableState ()) to track to the Drawable class
    // This brings us back to point 4
    public boolean setState(@NonNull final int[] stateSet) {
        if(! Arrays.equals(mStateSet, stateSet)) { mStateSet = stateSet;return onStateChange(stateSet);
        }
        return false;
    }

Copy the code

Drawable and CompoundButton are related by CallBack. The two influence each other.

	// The StateListDrawable CallBack is rbLeft
	rbLeft.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, topDrawable, rightDrawable, bottomDrawable);
	// CallBack to StateListDrawable is rbRight
	rbRight.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, topDrawable, rightDrawable, bottomDrawable);
	// Due to the above source code analysis.
	//rbLeft holds the Drawable reference
	//rbRight holds the Drawable reference. The Drawable CallBack is set to rbRight.

	// This will result in an out-of-order situation
	// Set RadioGroup to rbLeft by default.
	//1. Click the right button. The selected status is as follows:
	// I/rbRight: true
	// I/rbLeft: false
	// The button effect is selected on the right, and the button effect is not selected on the left.
	//2. Click the left button to select the following state:
	// I/rbLeft: true
	// I/rbRight: false
	// Both Radiobuttons are selected.
	/ / why. Because rbLeft changes the state of StateListDrawable.
	// While StateListDrawable CallBack bind rbRight.
	// So the display of rbRight has changed, but the state is still false.
Copy the code