1. 结果展示
2. 从布局开始
新建一个Empty Activity,命名为Login,勾选Generate a Layout File和Launcher Activity。让其成为主活动。
布局开始前先会运用一些布局
- RadioGroup提供了一种多选一的选择模式
- Spinner 是android 系统下拉的一个控件
- checkbox 是一个复选框,选中再次点击它,即可取消选中
- stroke 就是边框编辑
- corners 是用来字义圆角的
在drawable下编写一个正常的密码框命名为shape_edit_normal.xml,和一个被选中的密码框shape_edit_focus.xml。再编写一个两个合起来的edittext_selector.xml。还有复选框选中的xml命名为check_selector.xml。
编写shape_edit_normal.xml
新建一个drawable-hdpi,记得在里面添加光标,选择复选框时的图片和未选中复选框时的图片。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ffffff" />
<stroke
android:width="1dp"
android:color="#FFaaaaaa"/>
<corners
android:bottomLeftRadius="5dp"
android:bottomRightRadius="5dp"
android:topLeftRadius="5dp"
android:topRightRadius="5dp"/>
<padding
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp"/>
</shape>
编写shape_edit_focus.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ffffff" />
<stroke
android:width="1dp"
android:color="#ff0000ff"/>
<corners
android:bottomLeftRadius="5dp"
android:bottomRightRadius="5dp"
android:topLeftRadius="5dp"
android:topRightRadius="5dp"/>
<padding
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp"/>
</shape>
编写edittext_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@drawable/shape_edit_focus"/>
<item android:drawable="@drawable/shape_edit_normal"/>
</selector>
编写check_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="@drawable/check_choose"/>
<item android:drawable="@drawable/check_unchoose"/>
</selector>
编写activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
android:focusableInTouchMode="true"
android:padding="5dp">
<RadioGroup
android:id="@+id/group"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<RadioButton
android:id="@+id/button_pw"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:checked="true"
android:layout_gravity="left|center"
android:text="密码登录"
android:textColor="@color/black"
android:textSize="17sp"/>
<RadioButton
android:id="@+id/button_ic"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:checked="false"
android:layout_gravity="left|center"
android:text="验证码登录"
android:textColor="@color/black"
android:textSize="17sp"/>
</RadioGroup>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:id="@+id/tv_1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text=" 我是:"
android:gravity="center"
android:layout_alignParentLeft="true"
android:textColor="@color/black"
android:textSize="17sp"/>
<Spinner
android:id="@+id/sp_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="@+id/tv_1"
android:gravity="left|center"
android:spinnerMode="dialog"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="手机号码:"
android:layout_alignParentLeft="true"
android:textColor="@color/black"
android:textSize="17sp"/>
<EditText
android:id="@+id/et_phone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="@id/tv_phone"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:gravity="left|center"
android:background="@drawable/edittext_selector"
android:hint="请输入手机号码"
android:inputType="number"
android:maxLength="11"
android:textColor="@color/black"
android:textColorHint="@color/grey"
android:textCursorDrawable="@drawable/text_cursor"
android:textSize="17sp"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:id="@+id/tv_password"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:layout_alignParentLeft="true"
android:text="登录密码:"
android:textColor="@color/black"
android:textSize="17sp"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="@id/tv_password">
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:gravity="left|center"
android:background="@drawable/edittext_selector"
android:text="请输入密码"
android:inputType="numberPassword"
android:maxLength="6"
android:textColor="@color/black"
android:textColorHint="@color/grey"
android:textCursorDrawable="@drawable/text_cursor"
android:textSize="17sp"/>
<Button
android:id="@+id/forget_password"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:layout_gravity="right"
android:text="忘记密码"
android:textColor="@color/black"
android:textSize="17sp"/>
</FrameLayout>
</RelativeLayout>
<CheckBox
android:id="@+id/ck_remember"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:button="@drawable/check_selector"
android:checked="false"
android:padding="10dp"
android:text="记住密码"
android:textColor="@color/black"
android:textSize="17sp"/>
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录"
android:textColor="@color/black"
android:textSize="22sp"/>
</LinearLayout>
新建一个Empty Activity,命名为login_forget。编写activity_login_forget.xml
<?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:focusable="true"
android:focusableInTouchMode="true"
android:orientation="vertical"
android:padding="5dp" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp" >
<TextView
android:id="@+id/tv_password_first"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:gravity="center"
android:text="输入新密码:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_password_first"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:layout_toRightOf="@+id/tv_password_first"
android:background="@drawable/edittext_selector"
android:gravity="left|center"
android:hint="请输入新密码"
android:inputType="numberPassword"
android:maxLength="11"
android:textColor="@color/black"
android:textColorHint="@color/grey"
android:textCursorDrawable="@drawable/text_cursor"
android:textSize="17sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp" >
<TextView
android:id="@+id/tv_password_second"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:gravity="center"
android:text="确认新密码:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_password_second"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:layout_toRightOf="@+id/tv_password_second"
android:background="@drawable/edittext_selector"
android:gravity="left|center"
android:hint="请再次输入新密码"
android:inputType="numberPassword"
android:maxLength="11"
android:textColor="@color/black"
android:textColorHint="@color/grey"
android:textCursorDrawable="@drawable/text_cursor"
android:textSize="17sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp" >
<TextView
android:id="@+id/tv_verifycode"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:gravity="center"
android:text=" 验证码:"
android:textColor="@color/black"
android:textSize="17sp" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="@+id/tv_verifycode" >
<EditText
android:id="@+id/et_verifycode"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:background="@drawable/edittext_selector"
android:gravity="left|center"
android:hint="请输入验证码"
android:inputType="numberPassword"
android:maxLength="6"
android:textColor="@color/black"
android:textColorHint="@color/grey"
android:textCursorDrawable="@drawable/text_cursor"
android:textSize="17sp" />
<Button
android:id="@+id/btn_verifycode"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="right"
android:gravity="center"
android:text="获取验证码"
android:textColor="@color/black"
android:textSize="17sp" />
</FrameLayout>
</RelativeLayout>
<Button
android:id="@+id/btn_confirm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="确定"
android:textColor="@color/black"
android:textSize="22sp" />
</LinearLayout>
在layout下新建两个xml,命名为item_select.xml,item_dropdown.xml。这是用户的下拉框布局。
编写item_select.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:singleLine="true"
android:gravity="center"
android:textSize="17sp"
android:textColor="#0000ff" />
编写item_dropdown.xml
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp"
android:singleLine="true"
android:gravity="center"
android:textSize="17sp"
android:textColor="#ff0000" />
整体文件如下图所示
3.最重要的环节编写主代码
编写Login.java
package com.example.login;
import androidx.appcompat.app.AppCompatActivity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemSelectedListener;
public class Login extends AppCompatActivity implements OnClickListener{
private RadioGroup group;
private RadioButton button_pw;
private RadioButton button_ic;
private EditText et_phone;
private EditText et_password;
private TextView tv_phone;
private TextView tv_password;
private Button forget_button;
private CheckBox ck_remember;
private int mRequestCode=0; // 跳转页面时的请求代码
private int mType=0; // 用户类型
private boolean bRemember=false; // 是否记住密码
private String mPassword="111111"; // 默认密码
private String mVerifyCode; // 验证码
private SharedPreferences mShare; // 声明一个共享参数对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
group=findViewById(R.id.group);
button_pw=findViewById(R.id.button_pw);
button_ic=findViewById(R.id.button_ic);
et_phone=findViewById(R.id.et_phone);
et_password=findViewById(R.id.et_password);
tv_phone=findViewById(R.id.tv_phone);
tv_password=findViewById(R.id.tv_password);
forget_button=findViewById(R.id.forget_password);
ck_remember=findViewById(R.id.ck_remember);
group.setOnCheckedChangeListener(new RadioListener());
ck_remember.setOnCheckedChangeListener(new CheckListener());
et_phone.addTextChangedListener(new HideTextWatcher(et_phone));
et_password.addTextChangedListener(new HideTextWatcher(et_password));
forget_button.setOnClickListener(this);
findViewById(R.id.login).setOnClickListener(this);
initTypeSpinner();
// 从share.xml中获取共享参数对象
mShare=getSharedPreferences("share_login",MODE_PRIVATE);
// 获取共享参数中保存的手机号码
String phone=mShare.getString("phone","");
// 获取共享参数中保存的密码
String password=mShare.getString("password","");
et_phone.setText(phone);// 给手机号码编辑框填写上次保存的手机号
et_password.setText(password);// 给密码编辑框填写上次保存的密码
}
// 初始化用户类型的下拉框
private void initTypeSpinner() {
ArrayAdapter<String> typeAdapter=new ArrayAdapter<String>(this,R.layout.item_select,typeArray);
typeAdapter.setDropDownViewResource(R.layout.item_dropdown);
Spinner sp_1=findViewById(R.id.sp_1);
sp_1.setPrompt("请选择用户类型");
sp_1.setAdapter(typeAdapter);
sp_1.setSelection(mType);
sp_1.setOnItemSelectedListener(new TypeSelectedListener());
}
private String[] typeArray={"个人用户","公司用户"};
// 定义用户类型的选择监听器
class TypeSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
mType = arg2;
}
public void onNothingSelected(AdapterView<?> arg0) {
}
}
// 定义登录方式的单选监听器
private class RadioListener implements RadioGroup.OnCheckedChangeListener{
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if(checkedId==R.id.button_pw){
tv_password.setText("登录密码:");
et_password.setHint("请输入密码");
forget_button.setText("忘记密码");
ck_remember.setVisibility(View.VISIBLE);
}else if(checkedId==R.id.button_ic){
tv_password.setText(" 验证码:");
et_password.setHint("请输入验证码");
forget_button.setText("获取验证码");
ck_remember.setVisibility(View.VISIBLE);
}
}
}
// 定义是否记住密码的勾选监听器
private class CheckListener implements CompoundButton.OnCheckedChangeListener{
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(buttonView.getId()==R.id.ck_remember){
bRemember=isChecked;
}
}
}
// 定义编辑框的文本变化监听器
private class HideTextWatcher implements TextWatcher{
private EditText mView;
private int mMaxLength;
private CharSequence mStr;
public HideTextWatcher(EditText v) {
super();
mView = v;
mMaxLength = ViewUtil.getMaxLength(v);
}
// 在编辑框的输入文本变化前触发
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
// 在编辑框的输入文本变化时触发
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
mStr = s;
}
// 在编辑框的输入文本变化后触发
@Override
public void afterTextChanged(Editable s) {
if (TextUtils.isEmpty(mStr))
return;
// 手机号码输入达到11位,或者密码/验证码输入达到6位,都关闭输入法软键盘
if ((mStr.length() == 11 && mMaxLength == 11) ||
(mStr.length() == 6 && mMaxLength == 6)) {
ViewUtil.hideOneInputMethod(Login.this, mView);
}
}
}
@Override
public void onClick(View v) {
String phone = et_phone.getText().toString();
if (v.getId() == R.id.forget_password) { // 点击了“忘记密码”按钮
if (phone.length() < 11) { // 手机号码不足11位
Toast.makeText(this, "请输入正确的手机号", Toast.LENGTH_SHORT).show();
return;
}
if (button_pw.isChecked()) { // 选择了密码方式校验,此时要跳到找回密码页面
Intent intent = new Intent(this, login_forget.class);
// 携带手机号码跳转到找回密码页面
intent.putExtra("phone", phone);
startActivityForResult(intent, mRequestCode);
}else if (button_ic.isChecked()) { // 选择了验证码方式校验,此时要生成六位随机数字验证码
// 生成六位随机数字的验证码
mVerifyCode = String.format("%06d", (int) (Math.random() * 1000000 % 1000000));
// 弹出提醒对话框,提示用户六位验证码数字
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("请记住验证码");
builder.setMessage("手机号" + phone + ",本次验证码是" + mVerifyCode + ",请输入验证码");
builder.setPositiveButton("好的", null);
AlertDialog alert = builder.create();
alert.show();
}
}else if (v.getId() == R.id.login) { // 点击了“登录”按钮
if (phone.length() < 11) { // 手机号码不足11位
Toast.makeText(this, "请输入正确的手机号", Toast.LENGTH_SHORT).show();
return;
}
if (button_pw.isChecked()) { // 密码方式校验
if (!et_password.getText().toString().equals(mPassword)) {
Toast.makeText(this, "请输入正确的密码", Toast.LENGTH_SHORT).show();
} else { // 密码校验通过
loginSuccess(); // 提示用户登录成功
}
} else if (button_ic.isChecked()) { // 验证码方式校验
if (!et_password.getText().toString().equals(mVerifyCode)) {
Toast.makeText(this, "请输入正确的验证码", Toast.LENGTH_SHORT).show();
} else { // 验证码校验通过
loginSuccess(); // 提示用户登录成功
}
}
}
}
// 从后一个页面携带参数返回当前页面时触发
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == mRequestCode && data != null) {
// 用户密码已改为新密码,故更新密码变量
mPassword = data.getStringExtra("new_password");
}
}
// 从修改密码页面返回登录页面,要清空密码的输入框
@Override
protected void onRestart() {
et_password.setText("");
super.onRestart();
}
// 校验通过,登录成功
private void loginSuccess() {
String desc = String.format("您的手机号码是%s,类型是%s。恭喜你通过登录验证,点击“确定”按钮返回上个页面",
et_phone.getText().toString(), typeArray[mType]);
// 弹出提醒对话框,提示用户登录成功
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("登录成功");
builder.setMessage(desc);
builder.setPositiveButton("确定返回", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
builder.setNegativeButton("我再看看", null);
AlertDialog alert = builder.create();
alert.show();
// 如果勾选了“记住密码”,则把手机号码和密码都保存到共享参数中
if (bRemember) {
SharedPreferences.Editor editor = mShare.edit(); // 获得编辑器的对象
editor.putString("phone", et_phone.getText().toString()); // 添加名叫phone的手机号码
editor.putString("password", et_password.getText().toString()); // 添加名叫password的密码
editor.commit(); // 提交编辑器中的修改
}
}
}
编写login_forget.java
package com.example.login;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class login_forget extends AppCompatActivity implements View.OnClickListener {
private EditText et_password_first;
private EditText et_password_second;
private EditText et_verifycode;
private String mVerifyCode; // 验证码
private String mPhone; // 手机号码
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_forget);
et_password_first = findViewById(R.id.et_password_first);
et_password_second = findViewById(R.id.et_password_second);
et_verifycode = findViewById(R.id.et_verifycode);
findViewById(R.id.btn_verifycode).setOnClickListener(this);
findViewById(R.id.btn_confirm).setOnClickListener(this);
// 从前一个页面获取要修改密码的手机号码
mPhone = getIntent().getStringExtra("phone");
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_verifycode) { // 点击了“获取验证码”按钮
if (mPhone == null || mPhone.length() < 11) {
Toast.makeText(this, "请输入正确的手机号", Toast.LENGTH_SHORT).show();
return;
}
// 生成六位随机数字的验证码
mVerifyCode = String.format("%06d", (int) (Math.random() * 1000000 % 1000000));
// 弹出提醒对话框,提示用户六位验证码数字
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("请记住验证码");
builder.setMessage("手机号" + mPhone + ",本次验证码是" + mVerifyCode + ",请输入验证码");
builder.setPositiveButton("好的", null);
AlertDialog alert = builder.create();
alert.show();
} else if (v.getId() == R.id.btn_confirm) { // 点击了“确定”按钮
String password_first = et_password_first.getText().toString();
String password_second = et_password_second.getText().toString();
if (password_first.length() < 6 || password_second.length() < 6) {
Toast.makeText(this, "请输入正确的新密码", Toast.LENGTH_SHORT).show();
return;
}
if (!password_first.equals(password_second)) {
Toast.makeText(this, "两次输入的新密码不一致", Toast.LENGTH_SHORT).show();
return;
}
if (!et_verifycode.getText().toString().equals(mVerifyCode)) {
Toast.makeText(this, "请输入正确的验证码", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "密码修改成功", Toast.LENGTH_SHORT).show();
// 把修改好的新密码返回给前一个页面
Intent intent = new Intent();
intent.putExtra("new_password", password_first);
setResult(Activity.RESULT_OK, intent);
finish();
}
}
}
}
新建一个java class命名为ViewUtil
package com.example.login;
import android.app.Activity;
import android.content.Context;
import android.text.InputFilter;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import java.lang.reflect.Field;
/**
* Created by ouyangshen on 2017/9/24.
*/
public class ViewUtil {
// 获取编辑框的最大长度,通过反射机制调用隐藏方法
public static int getMaxLength(EditText et) {
int length = 0;
try {
InputFilter[] inputFilters = et.getFilters();
for (InputFilter filter : inputFilters) {
Class<?> c = filter.getClass();
if (c.getName().equals("android.text.InputFilter$LengthFilter")) {
Field[] f = c.getDeclaredFields();
for (Field field : f) {
if (field.getName().equals("mMax")) {
field.setAccessible(true);
length = (Integer) field.get(filter);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return length;
}
public static void hideAllInputMethod(Activity act) {
// 从系统服务中获取输入法管理器
InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isActive()) { // 软键盘如果已经打开则关闭之
imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
}
}
public static void hideOneInputMethod(Activity act, View v) {
// 从系统服务中获取输入法管理器
InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);
// 关闭屏幕上的输入法软键盘
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}