Android 自定义组合控件-编写一个登录界面

  • Post author:
  • Post category:其他


看完这篇文章你可以学到什么知识呢?

  • 自定义组合控件
  • 封装view,只暴露接口,模块独立,设计思想。
  • 会用到正则表达式
  • 如何禁止EditText在获取到焦点的时候拉起键盘
  • 学会登录中获取验证码的倒计时效果
  • EditText的内容添加与删除



效果图

效果图



分析

首先分析一下!其实对于UI来说,也是可以封装起来的,比如说,我们这里面涉及到了手机号码的检查、涉及到验证码的检查,涉及到是否同意协议来控制按钮是否可用,这些动作我们都可以隐藏起来。把这些所有看得见的,都当成一个View,这个View具备这些功能。

然后使用暴露接口:1、获取验证码按钮被点击了;2、协议内容被点击了;3、登录按钮被点击了。

除此之外,应该没有其他动作给使用者使用了。

问题点:

1、因为这个界面是平板的界面,我们自己写了一个键盘输入,所以当我们的输入框获取到焦点的时候,不弹出键盘出来

2、做一个倒计时的效果

3、获取到焦点的edittext内容输入



实现

我们要实现这个类,要用一个很大的坑来填充这些内容,所以我们要写一个LoginPageView,继承自FrameLayout。这个时候,需要实现一些方法:

实现构造方法

有没有注意到,前两个构造函数都是this,表示调用自己的构造函数。不同的参数而已。这样子就可以确保不管是哪种方式创建这个类的,都会进入第三个构造函数,也就是同一个入口。

这个类的全部代码如下:

package com.lwm.customview.customview.loginpage;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.lwm.customview.R;
import com.lwm.customview.customview.App;
import java.lang.reflect.Field;

/**
 * 点击获取验证码条件
 * 1、手机号码是正确
 * 点击登录条件
 * 1、正确的手机号码 + 验证码 + 同意了协议
 */
public class LoginPageView extends FrameLayout {

    private static final String TAG = "LoginPageView";
    public static final int SIZE_VERIFY_CODE_DEFAULT = 4;
    private int mColor;
    private int mVerifyCodeSize = SIZE_VERIFY_CODE_DEFAULT;
    private CheckBox mIsConfirm; // 同意协议的选择框
    private OnLoginPageActionListener mActionListener = null;
    private LoginKeyboardView mLoginKeyboardView; // 登录键盘
    private EditText mVerifyCodeInput;  // 验证码输入框
    private EditText mPhoneNumInp; // 输入框
    private TextView mGetVerifyCodeBtn;
    private boolean isPhoneNumOk = false;
    private boolean isAgreementOk = false;
    private boolean isVerifyCodeOk = false;
    private boolean isCountDowning = false;
    // 手机号码的规则
    public static final String REGEX_MOBILE_EXACT = "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|(147))\\d{8}$";
    private View mLoginBtn; // 确定按钮
    private TextView mAgreementView;  // 服务条款的文字

    /**
     * 倒计时关注点:
     * 1、倒计时多长时间:duration
     * 2、时间间隔:dt
     * 3、通知 UI 更新
     */
    public static final int DURATION_DEFAULT = 60 * 1000;

    // duration ms
    private int mCountDownDuration = DURATION_DEFAULT;
    // dt ms
    public static final int D_TIME_DEFAULT = 1000;
    private int restTime = mCountDownDuration;
    private Handler mHandler;
    private CountDownTimer mCountDownTimer;

    // 倒计时方式一:
    public void startCountDown() {
        mHandler = App.getHandler();
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                restTime -= D_TIME_DEFAULT;
                Log.d(TAG, "rest = " + restTime);
                if (restTime > 0) {
                    mHandler.postDelayed(this, D_TIME_DEFAULT);
                    // 直接更新 UI
                    mGetVerifyCodeBtn.setText("(" + restTime / 1000 + ")秒");
                    mGetVerifyCodeBtn.setEnabled(false);
                    isCountDowning = true;
                } else {
                    restTime = mCountDownDuration;
                    mGetVerifyCodeBtn.setEnabled(true);
                    isCountDowning = false;
                    mGetVerifyCodeBtn.setText("获取验证码");
                    updateAllBtnState();
                }
            }
        });
    }

    // 倒计时方式二:
    private void beginCountDown() {
        isCountDowning = true;
        mGetVerifyCodeBtn.setEnabled(false);
        mCountDownTimer = new CountDownTimer(mCountDownDuration, D_TIME_DEFAULT) {
            public void onTick(long millisUntilFinished) {
                // 通知 UI 更新
                int res = (int) (millisUntilFinished / 1000);
                mGetVerifyCodeBtn.setText("( " + res + " )秒");
            }

            public void onFinish() {
                // 倒计时结束
                mGetVerifyCodeBtn.setEnabled(true);
                isCountDowning = false;
                mGetVerifyCodeBtn.setText("获取验证码");
                updateAllBtnState();
                mCountDownTimer = null;
            }

        }.start();
    }

    /**
     * 验证码错误
     */
    public void onVerifyCodeError() {
        // 第二步:清空验证码输入框里的内容
        mVerifyCodeInput.getText().clear();
        // 第三步:停止倒计时
        if (isCountDowning && mCountDownTimer != null) {
            isCountDowning = false;
            mCountDownTimer.cancel(); // 取消倒计时
            mCountDownTimer.onFinish();
        }
    }

    public LoginPageView(@NonNull Context context) {
        this(context, null);
    }

    public LoginPageView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoginPageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 初始化属性
        // 设置 get和 set 方法
        initAttrs(context, attrs);
        // 初始化控件
        initView();
        disableEdtFocus2Keypad(); // 禁止拉起软键盘
        // 监听点击事件
        initEvent();
    }

    // 禁止拉起软键盘
    @SuppressLint("NewApi")
    private void disableEdtFocus2Keypad() {
        mVerifyCodeInput.setShowSoftInputOnFocus(false); // 禁止拉起软键盘
        mPhoneNumInp.setShowSoftInputOnFocus(false); // 禁止拉起软键盘
    }

    private void initEvent() {
        mLoginKeyboardView.setOnKeyPressListener(new LoginKeyboardView.OnKeyPressListener() {
            @Override
            public void onNumberPress(int number) {
                // 数字被点击
                // 插入数字( EditText谁有焦点就插入谁)
                EditText focusEdt = getFocusEdt();
                if (focusEdt != null) {
                    Editable text = focusEdt.getText();
                    int index = focusEdt.getSelectionEnd(); // 获取光标的最后位置
                    text.insert(index, String.valueOf(number));
                }
            }

            @Override
            public void onBackPress() {
                // 退格键被点击
                EditText focusEdt = getFocusEdt();
                if (focusEdt != null) {
                    int index = focusEdt.getSelectionEnd(); // 获取光标的最后位置
                    Log.d(TAG, "index ==" + index);
                    Editable editable = focusEdt.getText();
                    if (index > 0) {
                        editable.delete(index - 1, index);
                    }
                }
            }
        });
        mGetVerifyCodeBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // todo:get verify code.
                if (mActionListener != null) {
                    // 拿到手机号码
                    String phoneNum = mPhoneNumInp.getText().toString().trim();
                    Log.d(TAG, "phoneNum = " + phoneNum);
                    mActionListener.onGetVerifyCodeClick(phoneNum);
                    // 开启倒计时
                    // startCountDown(); // 倒计时方式一
                    beginCountDown(); // 倒计时方式二
                } else {
                    throw new IllegalArgumentException("no action to get verify code");
                }
            }
        });

        // 手机号码输入监听
        mPhoneNumInp.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                // 变化的时候去检查手机号码是否符合格式
                // TODO:
                Log.d(TAG, "content:" + s);
                String phoneNum = s.toString();
                boolean isMatch = phoneNum.matches(REGEX_MOBILE_EXACT);
                isPhoneNumOk = phoneNum.length() == 11 && isMatch;
                updateAllBtnState();
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        // 验证码输入监听
        mVerifyCodeInput.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                isVerifyCodeOk = s.length() == 4;
                updateAllBtnState();
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        // 协议按钮
        mIsConfirm.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                isAgreementOk = isChecked;
                updateAllBtnState();
            }
        });

        // 协议文字
        mAgreementView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "mIsConfirm --> click...");
            }
        });

        // 登录按钮
        mLoginBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // todo:根据产品经理的要求,点击登录以后是否禁止按钮
                // TODO:UI 上防止重复提交
                // 登录
                if (mActionListener != null) {
                    // 拿到手机号码和验证码
                    mActionListener.onConfirmClick(getVerifyCode(), getPhoneNum());
                }
            }
        });
    }

    // 获取手机号码
    private String getPhoneNum() {
        return mPhoneNumInp.getText().toString().trim();
    }

    // 获取验证码
    private String getVerifyCode() {
        return mVerifyCodeInput.getText().toString().trim();
    }

    private void initAttrs(@NonNull Context context, @Nullable AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LoginPageView);
        mColor = a.getColor(R.styleable.LoginPageView_mainColor, -1);
        mVerifyCodeSize = a.getInt(R.styleable.LoginPageView_verifyCodeSize, SIZE_VERIFY_CODE_DEFAULT);
        mCountDownDuration = a.getInt(R.styleable.LoginPageView_countDownDuration, DURATION_DEFAULT);
        a.recycle();
    }

    private void initView() {
        LayoutInflater.from(getContext()).inflate(R.layout.login_page_view, this);
        mIsConfirm = this.findViewById(R.id.report_check_box);
        mVerifyCodeInput = this.findViewById(R.id.verify_code_input_box);
        if (mColor != -1) {
            mIsConfirm.setTextColor(mColor);
        }
        mVerifyCodeInput.setFilters(new InputFilter[]{new InputFilter.LengthFilter(mVerifyCodeSize)});
        mLoginKeyboardView = this.findViewById(R.id.number_key_pad);
        mPhoneNumInp = this.findViewById(R.id.phone_num_input_box);
        mPhoneNumInp.requestFocus(); // 设置焦点在手机号码哪里
        mGetVerifyCodeBtn = this.findViewById(R.id.get_verify_code_btn);
        disableCopyAndPaste(mPhoneNumInp); // 禁止 EditText 选中复制粘贴
        disableCopyAndPaste(mVerifyCodeInput); // 禁止 EditText 选中复制粘贴
        mLoginBtn = this.findViewById(R.id.login_btn);
        mAgreementView = this.findViewById(R.id.agreement_text_tips);
        this.updateAllBtnState();
    }

    private void updateAllBtnState() {
        if (!isCountDowning) {
            mGetVerifyCodeBtn.setEnabled(isPhoneNumOk);
        }
        // 修改文字颜色
        mAgreementView.setTextColor(isAgreementOk ? getResources().getColor(R.color.mainColor) : getResources().getColor(R.color.mainDeepColor));

        mLoginBtn.setEnabled(isPhoneNumOk && isAgreementOk && isVerifyCodeOk);
    }


    /**
     * 获取当前有焦点的输入框
     * <p>
     * 使用要注意判空
     *
     * @return null or EditText instanceof
     */
    private EditText getFocusEdt() {
        View view = this.findFocus();
        if (view instanceof EditText) {
            return (EditText) view;
        }
        return null;
    }

    public int getmColor() {
        return mColor;
    }

    public void setmColor(int mColor) {
        this.mColor = mColor;
    }

    public int getmVerifyCodeSize() {
        return mVerifyCodeSize;
    }

    public void setmVerifyCodeSize(int mVerifyCodeSize) {
        this.mVerifyCodeSize = mVerifyCodeSize;
    }

    public void setOnLoginPageActionListener(OnLoginPageActionListener listener) {
        this.mActionListener = listener;
    }

    public interface OnLoginPageActionListener {

        void onGetVerifyCodeClick(String phoneNum);

        void onOpenAgreementClick();

        void onConfirmClick(String verifyCode, String phoneNum);
    }

    // 禁止 EditText 选中复制粘贴
    @SuppressLint("ClickableViewAccessibility")
    public void disableCopyAndPaste(final EditText editText) {
        try {
            if (editText == null) {
                return;
            }

            editText.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    return true;
                }
            });
            editText.setLongClickable(false);
            editText.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        // setInsertionDisabled when user touches the view
                        setInsertionDisabled(editText);
                    }
                    return false;
                }
            });
            editText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
                @Override
                public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                    return false;
                }

                @Override
                public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                    return false;
                }

                @Override
                public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                    return false;
                }

                @Override
                public void onDestroyActionMode(ActionMode mode) {

                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void setInsertionDisabled(EditText editText) {
        try {
            Field editorField = TextView.class.getDeclaredField("mEditor");
            editorField.setAccessible(true);
            Object editorObject = editorField.get(editText);

            // if this view supports insertion handles
            Class editorClass = Class.forName("android.widget.Editor");
            Field mInsertionControllerEnabledField = editorClass.getDeclaredField("mInsertionControllerEnabled");
            mInsertionControllerEnabledField.setAccessible(true);
            mInsertionControllerEnabledField.set(editorObject, false);

            // if this view supports selection handles
            Field mSelectionControllerEnabledField = editorClass.getDeclaredField("mSelectionControllerEnabled");
            mSelectionControllerEnabledField.setAccessible(true);
            mSelectionControllerEnabledField.set(editorObject, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public int getmCountDownDuration() {
        return mCountDownDuration;
    }

    // 暴露出去进行设置
    public void setmCountDownDuration(int mCountDownDuration) {
        this.mCountDownDuration = mCountDownDuration;
    }
}

代码讲解:

一开始,我们把view绑定到这个坑里头,也就是这句代码:

LayoutInflater.from(getContext()).inflate(R.layout.login_page_view, this); // 默认为true
// LayoutInflater.from(getContext()).inflate(R.layout.login_page_view, this,true); // 与上面等同

inflate,后面两个是重点,this,表示当前这个容器,也就是我们创建的LoginPageView,true表示绑定到这个容器里。所以,这行代码的意思是把这个布局文件里的内容,加载进来,放到这个ViewGrop里面,也就是放到LoginPageView里面。

键盘的输入等会再说,接着我们是初始化事件:

我们有什么事件呢?

1、监听两个输入框内容的变化,第一个输入框,只有匹配了手机号码的规则,获取验证码才可以适用,否则是disable,也就是不能点击,除非手机号码是对的;监听第二个输入框是用于判断有没有输入验证码,如果没有输入,那么,登录按钮不可以点击。

2、获取验证码这个按钮,点击以后要disable,并且开始倒计时,调用暴露的接口。

3、checkbox这个被点击以后,更新登录按钮是否可以点击,其实登录按钮要满足三个条件才可以点击:手机号码是对的,验证码有输入,同意适用协议。

4、登录按钮不需要做太多的动作,因为前面的动作已经做了相关的检查了。

布局文件的代码如下:

<?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:background="#000"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="30dp"
        android:drawableLeft="@mipmap/user"
        android:drawablePadding="20dp"
        android:text="账号登录"
        android:textColor="@color/mainColor"
        android:textSize="30sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:padding="10dp">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="3"
            android:orientation="vertical"
            android:padding="15dp">

            <EditText
                android:id="@+id/phone_num_input_box"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/shape_edit_text_bg"
                android:drawableLeft="@mipmap/phone"
                android:drawablePadding="10dp"
                android:hint="请输入11位手机号码"
                android:inputType="number"
                android:maxLength="11"
                android:paddingHorizontal="10dp"
                android:paddingVertical="10dp"
                android:singleLine="true"
                android:textColor="@color/white_bai"
                android:textColorHint="#7E7E7E"
                android:textCursorDrawable="@drawable/shape_edt_cursor"
                android:textSize="24sp" />

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp">

                <EditText
                    android:id="@+id/verify_code_input_box"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@drawable/shape_edit_text_bg"
                    android:drawableLeft="@mipmap/password"
                    android:drawablePadding="10dp"
                    android:hint="请输入验证码"
                    android:inputType="number"
                    android:paddingVertical="10dp"
                    android:paddingLeft="10dp"
                    android:paddingRight="160dp"
                    android:singleLine="true"
                    android:textColor="@color/white_bai"
                    android:textColorHint="#7E7E7E"
                    android:textCursorDrawable="@drawable/shape_edt_cursor"
                    android:textSize="24sp" />

                <TextView
                    android:id="@+id/get_verify_code_btn"
                    android:layout_width="120dp"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:layout_centerVertical="true"
                    android:layout_marginRight="20dp"
                    android:gravity="center"
                    android:text="@string/text_get_verifyt_code"
                    android:textColor="@drawable/selector_text_color"
                    android:textSize="20sp" />

                <View
                    android:layout_width="1dp"
                    android:layout_height="40dp"
                    android:layout_centerVertical="true"
                    android:layout_marginRight="6dp"
                    android:layout_toLeftOf="@id/get_verify_code_btn"
                    android:background="@drawable/shape_line_bg" />

            </RelativeLayout>

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginTop="10dp"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <CheckBox
                    android:id="@+id/report_check_box"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:button="@null"
                    android:drawableLeft="@drawable/selector_check_box"
                    android:drawablePadding="10dp" />

                <TextView
                    android:id="@+id/agreement_text_tips"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/text_user_report"
                    android:textColor="@color/mainDeepColor"
                    android:textSize="20sp"
                    android:textStyle="bold" />
            </LinearLayout>


            <TextView
                android:id="@+id/login_btn"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:background="@drawable/selector_btn_bg"
                android:gravity="center"
                android:padding="15dp"
                android:text="@string/text_confirm"
                android:textColor="@color/white_bai" />

        </LinearLayout>

        <com.lwm.customview.customview.loginpage.LoginKeyboardView
            android:id="@+id/number_key_pad"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="2"
            android:padding="15dp" />

    </LinearLayout>

</LinearLayout>

效果是这样子的:

满足全部条件效果图

这里面有一个键盘,它的代码其实是个view来的,跟上面一样,编写一个组合控件。

代码如下:

package com.lwm.customview.customview.loginpage;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.Nullable;

import com.lwm.customview.R;

public class LoginKeyboardView extends LinearLayout implements View.OnClickListener {

    private static final String TAG = "LoginKeyboard";
    private OnKeyPressListener mKeyPressListener = null;

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

    public LoginKeyboardView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoginKeyboardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //
        initView();
    }

    private void initView() {
        LayoutInflater.from(getContext()).inflate(R.layout.num_key_pad, this);
        this.findViewById(R.id.number_1).setOnClickListener(this);
        this.findViewById(R.id.number_2).setOnClickListener(this);
        this.findViewById(R.id.number_3).setOnClickListener(this);
        this.findViewById(R.id.number_4).setOnClickListener(this);
        this.findViewById(R.id.number_5).setOnClickListener(this);
        this.findViewById(R.id.number_6).setOnClickListener(this);
        this.findViewById(R.id.number_7).setOnClickListener(this);
        this.findViewById(R.id.number_8).setOnClickListener(this);
        this.findViewById(R.id.number_9).setOnClickListener(this);
        this.findViewById(R.id.number_0).setOnClickListener(this);
        this.findViewById(R.id.back).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int viewId = v.getId();
        if (mKeyPressListener == null) {
            Log.d(TAG, "mKeyPressListener is null need not callback..");
            return;
        }
        if (viewId == R.id.back) {
            // 走back(处理删除按钮被点击了)
            mKeyPressListener.onBackPress();
        } else {
            // 走数字结果通知
            String text = ((TextView) v).getText().toString();
            Log.d(TAG, "click text = " + text);
            mKeyPressListener.onNumberPress(Integer.parseInt(text));
        }
    }

    public void setOnKeyPressListener(OnKeyPressListener listener) {
        this.mKeyPressListener = listener;
    }

    public interface OnKeyPressListener {
        void onNumberPress(int number);

        void onBackPress();
    }
}



使用

使用很简单,直接把登录的View的全路径名称,复制到要适用的地方即可。

使用方式

接着,我们在Activity的代码里找到这个View,给它设置相关的接口实现即可。

public class LoginActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE); // 去掉标题栏,设置为全屏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_login);
        LoginPageView loginPageView = this.findViewById(R.id.login_page_view);
        loginPageView.setOnLoginPageActionListener(new LoginPageView.OnLoginPageActionListener() {
            @Override
            public void onGetVerifyCodeClick(String phoneNum) {
                // todo:去获取验证码
                Toast.makeText(LoginActivity.this, "验证码已发送", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onOpenAgreementClick() {
                // todo:打开协议页面
            }

            @Override
            public void onConfirmClick(String verifyCode, String phoneNum) {
                // todo:登录
                App.getHandler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        // 第一步:给个 Toast 提示
                        Toast.makeText(LoginActivity.this, "验证码错误", Toast.LENGTH_SHORT).show();
                        loginPageView.onVerifyCodeError();
                    }
                },4000);
            }
        });
    }
}



总结

  • 点击获取验证码把号码给到外面即可。
  • 点击确定以后,把所输入的号码和验证码告诉外面即可。
  • 其他的号码校验,按键处理,内容检查内部处理好。
  • 这就是自定义组合控件,把别人不关心的封装起来,把别人关心的暴露出去。