Android Custom Control: Mobile Phone Number Input Box with Animation Effect (3-4-4 format)

Posted by yanti on Sat, 11 May 2019 13:56:24 +0200

Click "Android Technology Grocery Store" above and select "Top Public Number"

Dry article, first time service!

 

Author: Android Grassroots King
Link: https://www.jianshu.com/p/e538b35c68c3
This article is authorized by the author.

Custom controls are used in many parts of the project.

Simple points, such as customization of personality control, combination packaging of multiple components, etc.
We need to understand the basic knowledge of custom controls, which can be implemented quickly.

Complex points, such as various graphical reports (e.g. stock K-line chart, time-sharing chart control).
In addition to the basic knowledge of custom controls, we also need to master the control event interception and transmission mechanism, event callback, gesture recognition, drawing, animation, architecture design and other technologies.

 

With regard to custom controls, we will step by step elaborate on:
Today, we first implement a simple custom control, and later find time to explain how to customize the stock K-line chart and time-sharing chart control.

demand

Realize the mobile phone number input box with animation effect:

1. Input mobile phone number format is 3-4-4;
2. The default hint prompt in the input box has animation effect when the number is input:
 (a) hint moves out of the input box and stays at the specified position above the input box to display the corresponding information;
 (b) In the translation process, the text gradually changes from big to small;
3. When the input box is cleared, the negative effect animation is made.
4. Automatically verify the digital format (non-digital is not allowed to enter) and the length of the input (up to 11 mobile phone numbers)
5. When the input box is valid, the empty button appears on the far right and clicks the empty input box.
6. Input completed, callback results;

 

Analysis

This input box effect will be used in many pages, we must encapsulate it, the best encapsulation scheme here is the custom control.
In our APP, the input logic of mobile phone number input box of all pages is exactly the same, but there are small differences between individual pages (when individual pages enter mobile phone number, there is no need for animation effect, or hint content, prompt message is different, etc.).

1. Differences should be configurable:

The following contents of a custom control should be designed as configurable properties:

 Is there animation effect?
 hint text;
 hint moves to the text displayed at the top, etc.

The emphasis here is:
1. How to configure custom attributes? How to use it?

2. Custom controls are called (used)

 We should support direct new controls in our code
 We should support direct use of our controls in layout xml, configurable custom properties

3, animation

Translational effect: Tween animation, attribute animation can be achieved;
Font Scaling: Attribute animation should be used to scale according to font size, width and height will automatically change (Note: Tween animation can not do font size difference change)
To sum up, attribute animation should be used uniformly to achieve translation and scaling effects, while multiple animations triggered simultaneously, and animation sets will be used.

The emphasis here is:
1. Translating requires two coordinates (x,y) of the original point and the target point. How to get the corresponding value in the custom control?
2. Font scaling requires two font size values before and after scaling. The default font size obtained in the code is px format. How to convert it to sp?
3. Design point: To achieve the animation effect of text movement and font size scaling, we can place two text controls in the layout.
tv_message: As hint placeholder, not displayed, only used to obtain coordinates and fonts;
tv_to_message: Display as top message, display as hint, animation is executed on the control;

Of course, there are many other ways to design this animation effect. When you use it, you can design it reasonably according to your own needs.

4. The 3-4-4 format of mobile phone number is to intercept input events and process strings. There is no technical point.
5. The length of mobile phone number and special characters are prohibited from input verification, and illegal characters can be judged by regular expressions.
6. Other:

Technical Implementation Analysis

The definition of attributes needs to be defined separately in the files under res:
In the res/values directory, create the attrs.xml file

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--user-Mobile Number Input Control-Custom Properties-->
    <declare-styleable name="user.phone.edittext">
        <attr name="showTopMessage" format="boolean"/>
        <attr name="topMessage" format="string"/>
        <attr name="hint" format="string"/>
    </declare-styleable>
</resources>

name can be customized and standardized.
showTopMessage // Custom Properties: Whether to Display Top Tips (true: display, false: not display)
topMessage//Custom Properties: Top Tip Information Content
hint // custom property: input box prompt information

2. Layout of custom controls

Use relative layout, including:
Enter box et_phone (need to be set to no background color, because UI personnel have fixed the input line color)
The bottom line of the input box View,
The empty button iv_phone_clear of the input box,
The text control tv_message above the input box (used as a placeholder for hint positions and fonts)
The text control tv_to_message at the top of the input box (for displaying prompt information)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="10dip"
    android:paddingRight="10dip">
    <TextView
        android:id="@+id/tv_to_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:textColor="#999999"
        android:textSize="14sp"
        android:text="Please enter your cell phone number."
        android:visibility="visible"/>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:id="@+id/rl"
        android:layout_below="@+id/tv_to_message">

        <ImageView
            android:id="@+id/iv_phone_clear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerInParent="true"
            android:src="@mipmap/close_white"
            android:visibility="invisible" />

        <EditText
            android:id="@+id/et_phone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@+id/iv_phone_clear"
            android:background="@null"
            android:inputType="phone"
            android:textColor="#2A2A2A"
            android:textColorHint="#999999"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/tv_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:clickable="false"
            android:textColor="#999999"
            android:gravity="center_vertical"
            android:layout_centerVertical="true"
            android:textSize="16sp"
            android:text="Please enter your cell phone number."
            android:visibility="invisible"/>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_alignParentBottom="true"
            android:background="#EBEBEB" />
    </RelativeLayout>
</RelativeLayout>

3. Create custom control classes (defined as new controls that can be used directly in xml)

Constructor:
Custom controls must use specific constructors:
1. A constructor for a parameter that can be used to directly new current controls in other code
  UserPhoneEditText(Context context)
2. Constructors with more than two parameters can be used to directly use the current control in layout xml, and AttributeSet can be used to obtain the properties we set in xml; (explained later)
UserPhoneEditText(Context context, AttributeSet attrs)

More constructor-related information, please find the information yourself!!!

The code parses to get custom parameters:

//Get the set of attributes defined in attrs. XML UserPhone EditText
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.user_phone_edittext);
showTopMessage = typedArray.getBoolean(R.styleable.user_phone_edittext_showTopMessage, false);
topMessage = typedArray.getString(R.styleable.user_phone_edittext_topMessage);
hint = typedArray.getString(R.styleable.user_phone_edittext_hint);
//release
typedArray.recycle();

1. R.styleable.user_phone_edittext We are here. res/attrs.xml Names defined in the id
2. When you get the parameters, you must remember to TypedArray Release, remember!!!

Create custom controls and get detailed code for custom parameters:

/**
 * Class: UserPhone EditText
 * Author: qxc
 * Date: 2018/3/2.
 */
public class UserPhoneEditText extends RelativeLayout {
    private Context context;//context

    private boolean showTopMessage;//Custom Properties: Whether to Display Top Tips (true: display, false: not display)
    private String topMessage;//Custom Properties: Top Tip Information Content
    private String hint;//Input box prompt information

    private EditText et_phone;//Telephone Number Input Box
    private ImageView iv_phone_clear;//Clear the input box button
    private TextView tv_message;//Message text in input box
    private TextView tv_to_message;//Out-of-box message text

    public UserPhoneEditText(Context context) {
        super(context);
        this.context = context;
        LoadView(context);
    }

    public UserPhoneEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        getAttrs(context,attrs);
        LoadView(context);
    }

    /**
     * Get custom attributes for configuration
     * @param context context
     * @param attrs Attribute set
     */
    private void getAttrs(Context context,AttributeSet attrs){
        //Get the set of attributes defined in attrs. XML UserPhone EditText
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.user_phone_edittext);
        showTopMessage = typedArray.getBoolean(R.styleable.user_phone_edittext_showTopMessage, false);
        topMessage = typedArray.getString(R.styleable.user_phone_edittext_topMessage);
        hint = typedArray.getString(R.styleable.user_phone_edittext_hint);
        //release
        typedArray.recycle();
    }

    /**
     * Initialize view
     * @param context context
     */
    private void LoadView(Context context){
        View view = LayoutInflater.from(context).inflate(R.layout.user_phone_edittext, this);
        initView(view);//Initialization component
        initEvent();//Initialization events
    }

    /**
     * Initialization component
     */
    private void initView(View view){
        et_phone = (EditText) view.findViewById(R.id.et_phone);
        iv_phone_clear = (ImageView) view.findViewById(R.id.iv_phone_clear);
        tv_message = (TextView) view.findViewById(R.id.tv_message);
        tv_to_message = (TextView) view.findViewById(R.id.tv_to_message);

        //Display components according to custom properties
        //Setting Text Information
        if(topMessage!=null){
            tv_to_message.setText(hint);
        }
    }

    /**
     * Initialization events
     */
    private void initEvent(){
        //Clear the contents of the input box
        iv_phone_clear.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                et_phone.setText("");
            }
        });
        //Input Box Content Change Event
        //If the input box starts typing characters, tv_message uses animation to move to the location of tv_to_message.
        //If the input box becomes empty, tv_message moves back from the location of tv_to_message
        et_phone.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }
            @Override
            public void afterTextChanged(Editable editable) {
                String text = editable.toString();
                if (text.length() == 0) {
                        //Clear the input box
                        //Execution animation

                    }
                }
                //If the input box starts typing characters, tv_message uses animation to move to the location of tv_to_message.
                else if (text.length() == 1 && tvPosition == 0) {
                        //Change of input box content
                        //Execution animation
                        //When the 11-digit mobile phone number is entered, the execution result callback
                }
            }
        });
    }
}

4. The execution of animation

Animation 1: Flat Moving Painting

Two coordinate points need to be obtained:
Coordinate point 1: hint text position (tv_message)
Coordinate Point 2: The location of message text (tv_top_message)

Let's first define two values to store coordinates of coordinate points.
private int[] position1 = new int[2];//tv_message's default location coordinates
 Private int [] position 2 = New Int [2]; //tv_to_message's default location coordinates

x moves, y moves, so use animation set AnimatorSet
 If it's just moving, the code is as follows:
AnimatorSet set = new AnimatorSet();
set.playTogether(
      ObjectAnimator.ofFloat(tv_to_message , "TranslationX" , startX , endX),
      ObjectAnimator.ofFloat(tv_to_message , "TranslationY" ,startY, endY));
set.setDuration(duration).start();

Be careful:
startX and endX equivalents do not refer to the absolute coordinate address on the screen (e.g. coordinates (200,200)), but to the numerical change of translation on the x axis.
For example:
startX = 0 indicates that the X position of the current control changes to 0.
endX =100 means moving 100 pixels to the right starting from startX.
EndX = 100 means moving 100 pixels to the left starting from startX.
After ofFloat, the value of X can be added to represent the path process of movement on the X axis.
Our actual startX and endX values are subtracted from the coordinate X of tv_message and tv_top_message, that is, the relative distance of the control is calculated as the distance or position of the animation movement.

Animation 2: Size change

The font value of two texts needs to be obtained( tv_message,tv_top_message)
//Let's first define an array to store the font values of two texts.
private float[] fonts = new float[2];//Default size of tv_to_message

//Animation execution, if the font is scaled while moving, can continue to use AnimatorSet, the code will be transformed into:
/**
     * Play animation
     * @param startX Start X
     * @param endX Target X
     * @param startY Start Y
     * @param endY Target Y
     * @param startFont Start font size
     * @param endFont Target font size
     */
    private void startAnim(float startX, float endX, float startY, float endY, float startFont, float endFont){
        AnimatorSet set = new AnimatorSet();
        set.playTogether(
                ObjectAnimator.ofFloat(tv_to_message , "TranslationX" , startX , endX),
                ObjectAnimator.ofFloat(tv_to_message , "TranslationY" ,startY, endY),
                ObjectAnimator.ofFloat(tv_to_message , "TextSize" , startFont, endFont));
        set.setDuration(duration).start();
    }

A key:
How to get the coordinates and size of tv_message, tv_top_message in the custom control?

Custom controls have their own function cycles, and different functions do different things.
Such as onSizeChanged, onMeasure, onLayout, onDraw, etc. If you do not understand what these methods do, please look for information on your own.

Let's write a code to do an experiment. First, let's look at the execution order of the custom control functions.
Customize a simple view to test the code:

package iwangzhe.testcustomview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
 * Class: TestView
 * Author: qxc
 * Date: 2018/2/27.
 */
public class TestView extends View {
    final String Tag = "TestView";
    public TestView(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.i(Tag,"Constructor TestView");
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        Log.i(Tag,"onFinishInflate");
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.i(Tag,"onMeasure");
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.i(Tag,"onLayout");
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Log.i(Tag,"onSizeChanged");
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.i(Tag,"onDraw");
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(Tag,"onTouchEvent");
        invalidate();
        return super.onTouchEvent(event);
    }

    @Override
    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
        Log.i(Tag,"onFocusChanged");
    }

    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        Log.i(Tag,"onWindowFocusChanged");
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        Log.i(Tag,"onAttachedToWindow");
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Log.i(Tag,"onDetachedFromWindow");
    }

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        Log.i(Tag,"onWindowVisibilityChanged");
    }
}

Output results:

03-05 17:38:55.690 23189-23189/iwangzhe.testcustomview I/TestView: Constructor TestView
03-05 17:38:55.690 23189-23189/iwangzhe.testcustomview I/TestView: onFinishInflate
03-05 17:38:55.770 23189-23189/iwangzhe.testcustomview I/TestView: onAttachedToWindow
03-05 17:38:55.770 23189-23189/iwangzhe.testcustomview I/TestView: onWindowVisibilityChanged
03-05 17:38:55.780 23189-23189/iwangzhe.testcustomview I/TestView: onMeasure
03-05 17:38:55.780 23189-23189/iwangzhe.testcustomview I/TestView: onMeasure
03-05 17:38:55.820 23189-23189/iwangzhe.testcustomview I/TestView: onSizeChanged
03-05 17:38:55.820 23189-23189/iwangzhe.testcustomview I/TestView: onLayout
03-05 17:38:55.830 23189-23189/iwangzhe.testcustomview I/TestView: onDraw
03-05 17:38:55.860 23189-23189/iwangzhe.testcustomview I/TestView: onWindowFocusChanged
03-05 17:38:55.880 23189-23189/iwangzhe.testcustomview I/TestView: onMeasure
03-05 17:38:55.880 23189-23189/iwangzhe.testcustomview I/TestView: onMeasure
03-05 17:38:55.880 23189-23189/iwangzhe.testcustomview I/TestView: onLayout
03-05 17:38:55.880 23189-23189/iwangzhe.testcustomview I/TestView: onDraw
......(repeat onMeasure,onLayout,onDraw)

We see that the onWindows FocusChanged method will be executed after the page loads the custom control. Before this method, initialization, calculation, layout and drawing display have been performed, and the position of the control has been assigned. So in the onWindows FocusChanged method, we can get the corresponding attributes, the code is as follows:

/**
     * Custom control is ready to get the location and other data of each component.
     */
    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        //Get location information for message text
        tv_message.getLocationInWindow(position1);
        tv_to_message.getLocationOnScreen(position2);

        //Get the font information of the message text
        fonts[0] =  PxUtils.px2sp(context,tv_message.getTextSize());
        fonts[1] = PxUtils.px2sp(context,tv_to_message.getTextSize());

        //Initialization location, number (tv_to_message setting is consistent with tv_message display)
        tv_to_message.setTextSize(fonts[0]);
        tv_to_message.setTranslationX(position1[0]-position2[0]);
        tv_to_message.setTranslationY(position1[1]-position2[1]);
    }

5. Mobile phone number 3-4-4 format

The code is relatively simple, as follows:

/**
     * Telephone 344 format (i.e., XXXX XXX xxx)
     * Telephone length 11 digits
     * @param view Input box
     * @param text text
     */
    public static void onTextChanged344(EditText view, String text) {
        if (view== null || text == null || text.length() == 0) return;
        char space = ' ';
        int indexSpace1 = 3;
        int indexSpace2 = 8;
        StringBuilder sb = new StringBuilder();
        //1. Remove all characters, remove''and illegal characters
        for (int i = 0; i < text.length(); i++) {
            //If the number of digits is greater than 11 digits, remove the following digits
            if(sb.length() >= 11){
                break;
            }

            //Valid Characters (0-9) (Regular Expressions)
            Pattern pattern = Pattern.compile("^[0-9]*$");
            Matcher matcher = pattern.matcher(String.valueOf(text.charAt(i)));
            if (text.charAt(i) != space && matcher.matches()) {
                sb.append(text.charAt(i));
            }
        }

        //2. Adding''according to length
        if(sb.length() > indexSpace1){
            sb.insert(indexSpace1, space);
        }
        if(sb.length() > indexSpace2){
            sb.insert(indexSpace2, space);
        }
        //3. Setting text and cursor positions
        if(!sb.toString().equals(text)){
            view.setText(sb.toString());
            view.setSelection(sb.length());
        }
    }

Complete code

Above, the basic technical points have been solved, so let's string up the code and paste out the complete code (the Demo source address will be given later).

Class 1: Help Class for PxUtils px and sp Conversion

 

/**
 * Class: PxUtils
 * Author: qxc
 * Date: 2018/3/5.
 */

public class PxUtils {
    /**
     * px Turn sp
     * @param context context
     * @param pxValue px
     * @return sp
     */
    public static int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }
}

 

Class 2: Telephone number format processing class

 

/**
 * Class: PhoneFormat
 * Author: qxc
 * Date: 2018/3/5.
 */
public class PhoneFormat {
    /**
     * Telephone 344 format (i.e., XXXX XXX xxx)
     * Telephone length 11 digits
     * @param view Input box
     * @param text text
     */
    public static void onTextChanged344(EditText view, String text) {
        if (view== null || text == null || text.length() == 0) return;
        char space = ' ';
        int indexSpace1 = 3;
        int indexSpace2 = 8;
        StringBuilder sb = new StringBuilder();
        //1. Remove all characters, remove''and illegal characters
        for (int i = 0; i < text.length(); i++) {
            //If the number of digits is greater than 11 digits, remove the following digits
            if(sb.length() >= 11){
                break;
            }

            //Valid Characters (0-9)
            Pattern pattern = Pattern.compile("^[0-9]*$");
            Matcher matcher = pattern.matcher(String.valueOf(text.charAt(i)));
            if (text.charAt(i) != space && matcher.matches()) {
                sb.append(text.charAt(i));
            }
        }

        //2. Adding''according to length
        if(sb.length() > indexSpace1){
            sb.insert(indexSpace1, space);
        }
        if(sb.length() > indexSpace2){
            sb.insert(indexSpace2, space);
        }
        //3. Setting text and cursor positions
        if(!sb.toString().equals(text)){
            view.setText(sb.toString());
            view.setSelection(sb.length());
        }
    }

    /**
     * Get the phone number entered, excluding spaces
     * @param editText Input control
     * @return Telephone number
     */
    public static String getPhoneNumber(EditText editText){
        if (editText== null || editText.getText() == null) return "";
        String text = editText.getText().toString();
        char space = ' ';
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < text.length(); i++) {
            if (text.charAt(i) != space) {
                sb.append(text.charAt(i));
            }
        }
        return sb.toString();
    }
}

 

Class 3: Custom control class (core class)

 

/**
 * Class: UserPhone EditText
 * Author: qxc
 * Date: 2018/3/2.
 */
public class UserPhoneEditText extends RelativeLayout {
    private Context context;//context

    private boolean showTopMessage;//Custom Properties: Whether to Display Top Tips (true: display, false: not display)
    private String topMessage;//Custom Properties: Top Tip Information Content
    private String hint;//Input box prompt information

    private EditText et_phone;//Telephone Number Input Box
    private ImageView iv_phone_clear;//Clear the input box button
    private TextView tv_message;//Message text in input box
    private TextView tv_to_message;//Out-of-box message text

    private int duration = 200;//Animation execution time
    private int tvPosition = 0;//The current location of tv_message, 0: in the input box; 1: in the location of tv_to_message (judgement before animation)
    private int[] position1 = new int[2];//Default position coordinates of tv_message
    private int[] position2 = new int[2];//Default position coordinates of tv_to_message
    private float[] fonts = new float[2];//Default size of tv_to_message

    public UserPhoneEditText(Context context) {
        super(context);
        this.context = context;
        LoadView(context);
    }

    public UserPhoneEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        getAttrs(context,attrs);
        LoadView(context);
    }
    /**
     * Get custom attributes for configuration
     * @param context context
     * @param attrs Attribute set
     */
    private void getAttrs(Context context,AttributeSet attrs){
        //Get the set of attributes defined in attrs. XML UserPhone EditText
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.user_phone_edittext);
        showTopMessage = typedArray.getBoolean(R.styleable.user_phone_edittext_showTopMessage, false);
        topMessage = typedArray.getString(R.styleable.user_phone_edittext_topMessage);
        hint = typedArray.getString(R.styleable.user_phone_edittext_hint);
        //release
        typedArray.recycle();
    }

    /**
     * Initialize view
     * @param context context
     */
    private void LoadView(Context context){
        View view = LayoutInflater.from(context).inflate(R.layout.user_phone_edittext, this);
        initView(view);//Initialization component
        initEvent();//Initialization events
    }

    /**
     * Initialization component
     */
    private void initView(View view){
        et_phone = (EditText) view.findViewById(R.id.et_phone);
        iv_phone_clear = (ImageView) view.findViewById(R.id.iv_phone_clear);
        tv_message = (TextView) view.findViewById(R.id.tv_message);
        tv_to_message = (TextView) view.findViewById(R.id.tv_to_message);

        //Display components according to custom properties
        //Setting Text Information
        if(topMessage!=null){
            tv_to_message.setText(hint);
        }
    }

    /**
     * Initialization events
     */
    private void initEvent(){
        //Clear the contents of the input box
        iv_phone_clear.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                et_phone.setText("");
            }
        });
        //Input Box Content Change Event
        //If the input box starts typing characters, tv_message uses animation to move to the location of tv_to_message.
        //If the input box becomes empty, tv_message moves back from the location of tv_to_message
        et_phone.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }
            @Override
            public void afterTextChanged(Editable editable) {
                String text = editable.toString();
                //If the input box becomes empty, tv_message moves back from the location of tv_to_message
                if (text.length() == 0) {
                    iv_phone_clear.setVisibility(View.INVISIBLE);
                    //If you don't display the top message, you don't need the animation effect.
                    if(!showTopMessage) {
                        tv_to_message.setVisibility(VISIBLE);
                    }else {
                        tvPosition = 0;
                        float startX = 0;
                        float endX = position1[0] - position2[0];
                        float startY = 0;
                        float endY = position1[1] - position2[1];
                        //Execution animation
                        startAnim(startX, endX, startY, endY, fonts[1], fonts[0]);
                        tv_to_message.setText(hint);
                    }
                }
                //If the input box starts typing characters, tv_message uses animation to move to the location of tv_to_message.
                else if (text.length() == 1 && tvPosition == 0) {
                    iv_phone_clear.setVisibility(View.VISIBLE);
                    if(!showTopMessage) {
                        tv_to_message.setVisibility(INVISIBLE);
                    }else {
                        tvPosition = 1;
                        float startX = position1[0] - position2[0];
                        float endX = 0;
                        float startY = position1[1] - position2[1];
                        float endY = 0;
                        //Execution animation
                        startAnim(startX, endX, startY, endY, fonts[0], fonts[1]);
                        tv_to_message.setText(topMessage);
                    }
                }
                //344 Telephone Format Processing
                PhoneFormat.onTextChanged344(et_phone,editable.toString());
                //Callback
                if(et_phone.getText().length()==13&&onSuccessListener!=null){
                    onSuccessListener.onSuccess(et_phone.getText().toString());
                }
            }
        });
    }

    /**
     * Custom control is ready to get the location and other data of each component.
     */
    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        //Get location information for message text
        tv_message.getLocationInWindow(position1);
        tv_to_message.getLocationOnScreen(position2);

        //Get the font information of the message text
        fonts[0] =  PxUtils.px2sp(context,tv_message.getTextSize());
        fonts[1] = PxUtils.px2sp(context,tv_to_message.getTextSize());

        //Initialization location, number (tv_to_message setting is consistent with tv_message display)
        tv_to_message.setTextSize(fonts[0]);
        tv_to_message.setTranslationX(position1[0]-position2[0]);
        tv_to_message.setTranslationY(position1[1]-position2[1]);
    }

    /**
     * Play animation
     * @param startX Start X
     * @param endX Target X
     * @param startY Start Y
     * @param endY Target Y
     * @param startFont Start font size
     * @param endFont Target font size
     */
    private void startAnim(float startX, float endX, float startY, float endY, float startFont, float endFont){
        AnimatorSet set = new AnimatorSet();
        set.playTogether(
                ObjectAnimator.ofFloat(tv_to_message , "TranslationX" , startX , endX),
                ObjectAnimator.ofFloat(tv_to_message , "TranslationY" ,startY, endY),
                ObjectAnimator.ofFloat(tv_to_message , "TextSize" , startFont, endFont));
        set.setDuration(duration).start();
    }

    /**
     * Get the input phone number
     * @return Input telephone number
     */
    public String getPhone(){
        return PhoneFormat.getPhoneNumber(et_phone);
    }

    /**
     * Get the input phone number for display
     * @return Input telephone number 334 format
     */
    public String getText(){
        return et_phone.getText().toString();
    }

    /**
     * Input completion callback
     */
    public interface OnSuccessListener{
        /**
         * Input complete
         * @param phone Telephone number
         */
        void onSuccess(String phone);
    }
    private OnSuccessListener onSuccessListener;

    /**
     * Set up monitoring
     * @param onSuccessListener
     */
    public void setOnSuccessListener(OnSuccessListener onSuccessListener){
        this.onSuccessListener = onSuccessListener;
    }
}

 

Layout: user_phone_edittext.xml

 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="10dip"
    android:paddingRight="10dip">
    <TextView
        android:id="@+id/tv_to_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:textColor="#999999"
        android:textSize="14sp"
        android:text="Please enter your cell phone number."
        android:visibility="visible"/>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:id="@+id/rl"
        android:layout_below="@+id/tv_to_message">
        <ImageView
            android:id="@+id/iv_phone_clear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerInParent="true"
            android:src="@mipmap/close_white"
            android:visibility="invisible" />

        <EditText
            android:id="@+id/et_phone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@+id/iv_phone_clear"
            android:background="@null"
            android:inputType="phone"
            android:textColor="#2A2A2A"
            android:textColorHint="#999999"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/tv_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:clickable="false"
            android:textColor="#999999"
            android:gravity="center_vertical"
            android:layout_centerVertical="true"
            android:textSize="16sp"
            android:text="Please enter your cell phone number."
            android:visibility="invisible"/>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_alignParentBottom="true"
            android:background="#EBEBEB" />
    </RelativeLayout>
</RelativeLayout>Custom Properties attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--user-Mobile Number Input Control-Custom Properties-->
    <declare-styleable name="user.phone.edittext">
        <attr name="showTopMessage" format="boolean"/>
        <attr name="topMessage" format="string"/>
        <attr name="hint" format="string"/>
    </declare-styleable>
</resources>

 

Test class MainActivity (test call custom control)
.xml

 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:userphoneedittext="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="iwangzhe.testcustomview.MainActivity">

    <Button
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:id="@+id/btn2"
        android:text="B"
        android:visibility="gone"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:id="@+id/btn1"
        android:layout_below="@id/btn2"
        android:text="A"
        android:visibility="gone"/>

    <iwangzhe.testcustomview.TestView
        android:layout_below="@id/btn1"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:id="@+id/tv1"/>

    <iwangzhe.testcustomview.userphone.UserPhoneEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv1"
        android:id="@+id/upet1"
        userphoneedittext:showTopMessage="true"
        userphoneedittext:topMessage="Test message information"
        userphoneedittext:hint="default hint Message information">
    </iwangzhe.testcustomview.userphone.UserPhoneEditText>

    <Button
        android:text="Verification of mobile phone number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="18dp"
        android:id="@+id/btnPhone"
        android:layout_below="@+id/upet1"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

To use custom attributes, we need to set the naming control: xmlns:userphoneedittext="http://schemas.android.com/apk/res-auto";
Userphone EditText can be defined by itself, and other formats are fixed.

public class MainActivity extends BaseActivity {

    Button btn1;
    Button btn2;
    Button btnPhone;
    UserPhoneEditText upet;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnPhone = (Button) findViewById(R.id.btnPhone);
        upet = (UserPhoneEditText) findViewById(R.id.upet1);
        //Set up callback monitor to get input completed callback data (passive callback)
        upet.setOnSuccessListener(new UserPhoneEditText.OnSuccessListener(){
            @Override
            public void onSuccess(String phone) {
                Toast.makeText(MainActivity.this, phone, Toast.LENGTH_SHORT).show();
            }
        });

        //Get text information for custom controls (active acquisition)
        btnPhone.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String text = upet.getPhone();
                Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show();
            }
        });
    }
}

If you still don't understand Demo, please leave a message or check your own information.
This article is mainly to let you know the process of custom control. If you want to use it in your own project, please adjust and optimize it according to your needs.

Demo address:
https://pan.baidu.com/s/1g5Ro3ZUWcdLwQezSmYFrBA

Dry goods in the past

1

Summary of MPAndroid Chart Curve Drawing

2

Mobile Card of Demand Solution Series I to Realize Answering Function

3

How to gracefully implement Android fingerprint verification in complex business scenarios?

Topics: Android xml Mobile Attribute