版权声明:本文为Yiwent原创文章,转载必须注明出处。
1.登陆界面
对于一个网络APP来说,刚开始首先必须登录,膜拜使用有手机登陆,当然还有微信什么其他的,不过绑定微信后又要绑定手机,所以手机登陆是必须的。
先看效果图:
2.界面还比较简单,两个et 和两个bt.
xml文件:
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
android:id="@+id/toolbar_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
app:leftButtonIcon="@drawable/places_ic_clear"
app:showSearchView="false"
app:title="手机验证"
>
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/toolbar_login"
android:layout_margin="20dp"
android:orientation="vertical">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#F2F2F2"
android:orientation="horizontal">
android:layout_marginLeft="3dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="手机号"
android:textColor="#333333"
android:textSize="18dp"/>
android:id="@+id/et_phone"
android:layout_marginRight="3dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入手机号"
android:inputType="phone"
android:textColorHint="#ABABAB"/>
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:background="#F2F2F2"
android:orientation="horizontal">
android:layout_width="0dp"
android:layout_marginLeft="3dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="验证码"
android:textColor="#333333"
android:textSize="18dp"/>
android:id="@+id/et_code"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="4"
android:hint="请输入验证码"
android:inputType="phone"
android:textColorHint="#ABABAB"/>
android:id="@+id/get_code"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="4"
android:background="@color/gray"
android:text="获取验证码"
android:textColor="@color/white"/>
android:id="@+id/loin_voice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="2dp"
android:layout_marginTop="8dp"
android:clickable="true"
android:text="收不到短信,试试语音短信"
android:textColor="@color/red"/>
android:id="@+id/login_query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:background="@color/gray"
android:text="确定"
android:textColor="@color/white"/>
android:layout_marginTop="15dp"
android:gravity="center"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
android:text="点击-确定,即表示已阅读并同意"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:id="@+id/login_services"
android:text="《用车服务条款》"
android:textColor="@color/red"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
其中
3.MyToolBar和ClearEditText 为自定义控件。
myToolBar代码:
package com.yiwen.mobike.views;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.StringRes;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.TintTypedArray;
import android.support.v7.widget.Toolbar;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import com.yiwen.mobike.R;
/**
* User: Yiwen(https://github.com/yiwent)
* Date: 2017-05-02
* Time: 11:00
* FIXME
*/
public class MyToolBar extends Toolbar {
private LayoutInflater mInflater;
private View mView;
private TextView toolbar_title;
private EditText toolbar_searchview;
private ImageView toolbar_leftButton;
private ImageView toolbar_rightButton;
private boolean showSearchView;
private Drawable left_button_icon;
private Drawable right_button_icon;
private String title;
public MyToolBar(Context context) {
this(context, null);
}
public MyToolBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyToolBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initview();
if (attrs != null) {
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
R.styleable.MyToolBar, defStyleAttr, 0);
showSearchView = a.getBoolean(R.styleable.MyToolBar_showSearchView, false);
left_button_icon = a.getDrawable(R.styleable.MyToolBar_leftButtonIcon);
right_button_icon = a.getDrawable(R.styleable.MyToolBar_rightButtonIcon);
title = a.getString(R.styleable.MyToolBar_myTitle);
a.recycle();
}
isShouw();
setContentInsetsRelative(15, 15);
initListener();
}
private void initListener() {
toolbar_leftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onLeftButtonClickListener != null) {
onLeftButtonClickListener.onClick();
}
}
});
toolbar_rightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onRightButtonClickListener != null) {
onRightButtonClickListener.onClick();
}
public void isShouw() {
if (showSearchView) {
showSearchview();
hideTitle();
} else {
hideSearchview();
showTitle();
if (title != null) {
toolbar_title.setText(title);
}
Log.d("left_button_icon", "initview:5"+left_button_icon);
if (left_button_icon != null) {
toolbar_leftButton.setVisibility(VISIBLE);
toolbar_leftButton.setBackground(left_button_icon);
}
if (right_button_icon != null) {
toolbar_rightButton.setVisibility(VISIBLE);
toolbar_rightButton.setImageDrawable(right_button_icon);
}
public interface OnLeftButtonClickListener {
void onClick();
}
public interface OnRightButtonClickListener {
void onClick();
}
private OnLeftButtonClickListener onLeftButtonClickListener;
private OnRightButtonClickListener onRightButtonClickListener;
public void setOnLeftButtonClickListener(OnLeftButtonClickListener listener) {
onLeftButtonClickListener = listener;
}
public void setOnRightButtonClickListener(OnRightButtonClickListener listener) {
onRightButtonClickListener = listener;
}
private void initview() {
if (mView == null) {
mInflater = LayoutInflater.from(getContext());
mView = mInflater.inflate(R.layout.toolbar, null);
toolbar_rightButton = (ImageView) mView.findViewById(R.id.id_btn_right);
toolbar_title = (TextView) mView.findViewById(R.id.id_tv_title);
toolbar_searchview = (EditText) mView.findViewById(R.id.id_et_search);
toolbar_leftButton = (ImageView) mView.findViewById(R.id.id_ib_navigation);
ActionBar.LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
addView(mView, lp);
// if (showSearchView) {
// showSearchview();
// hideTitle();
// } else {
// hideSearchview();
// showTitle();
// if (title != null) {
// toolbar_title.setText(title);
// }
// }
// Log.d("left_button_icon", "initview:5"+left_button_icon);
// if (left_button_icon != null) {
//
// toolbar_leftButton.setBackground(left_button_icon);
// toolbar_leftButton.setVisibility(VISIBLE);
// }
//
// if (right_button_icon != null) {
// toolbar_rightButton.setImageDrawable(right_button_icon);
// toolbar_rightButton.setVisibility(VISIBLE);
// }
@Override
public void setTitle(@StringRes int resId) {
setTitle(getContext().getString(resId));
}
@Override
public void setTitle(CharSequence title) {
initview();
if (toolbar_title != null) {
toolbar_title.setText(title);
showTitle();
public void showSearchview() {
if (toolbar_searchview != null) {
toolbar_searchview.setVisibility(VISIBLE);
}
public void hideSearchview() {
if (toolbar_searchview != null) {
toolbar_searchview.setVisibility(GONE);
}
public void showTitle() {
if (toolbar_title != null) {
toolbar_title.setVisibility(VISIBLE);
}
public void hideTitle() {
if (toolbar_title != null) {
toolbar_title.setVisibility(GONE);
}
/**
* 设置左右按钮的图标
*
* @param d
*/
public void setLeftButtonIconDrawable(Drawable d) {
toolbar_leftButton.setImageDrawable(d);
toolbar_leftButton.setVisibility(VISIBLE);
}
public void setRightButtonIconDrawable(Drawable d) {
toolbar_rightButton.setImageDrawable(d);
toolbar_rightButton.setVisibility(VISIBLE);
}
/**
* 标题与搜索框的切换
*/
public void setShowSearchView() {
hideTitle();
showSearchview();
}
public void setShowTitleView(String title) {
hideSearchview();
showTitle();
toolbar_title.setText(title);
4.ClearEditText代码:
package com.yiwen.mobike.views;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.widget.AppCompatEditText;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.yiwen.mobike.R;
public class ClearEditText extends AppCompatEditText implements View.OnTouchListener, View.OnFocusChangeListener, TextWatcher {
private Drawable mClearTextIcon;
private OnFocusChangeListener mOnFocusChangeListener;
private OnTouchListener mOnTouchListener;
public ClearEditText(final Context context) {
super(context);
init(context);
}
public ClearEditText(final Context context, final AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ClearEditText(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(final Context context) {
final Drawable drawable = ContextCompat.getDrawable(context, R.mipmap.places_ic_clear);
final Drawable wrappedDrawable = DrawableCompat.wrap(drawable); //Wrap the drawable so that it can be tinted pre Lollipop
DrawableCompat.setTint(wrappedDrawable, getCurrentHintTextColor());
mClearTextIcon = wrappedDrawable;
// mClearTextIcon= context.getResources().getDrawable(R.drawable.icon_delete_32);
mClearTextIcon.setBounds(0, 0, mClearTextIcon.getIntrinsicHeight(), mClearTextIcon.getIntrinsicHeight());
setClearIconVisible(false);
/*
* 设置父类的监听器,还可以单独给该类设置监听器
* */
super.setOnTouchListener(this);
super.setOnFocusChangeListener(this);
addTextChangedListener(this);
}
@Override
public void setOnFocusChangeListener(OnFocusChangeListener l) {
mOnFocusChangeListener = l;
}
@Override
public void setOnTouchListener(OnTouchListener l) {
mOnTouchListener = l;
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
setClearIconVisible(getText().length() > 0);
} else {
setClearIconVisible(false);
}
if (mOnFocusChangeListener != null) {
mOnFocusChangeListener.onFocusChange(v, hasFocus);
}
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
final int x = (int) motionEvent.getX();
/*
判断是否触摸在清楚按钮上
* */
if (mClearTextIcon.isVisible() && x > getWidth() - getPaddingRight() - mClearTextIcon.getIntrinsicWidth()) {
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
setError(null);
setText("");
}
return true;
}
return mOnTouchListener != null && mOnTouchListener.onTouch(view, motionEvent);
}
@Override
public final void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
if (isFocused()) {
setClearIconVisible(text.length() > 0);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
}
private void setClearIconVisible(final boolean visible) {
mClearTextIcon.setVisible(visible, false);
final Drawable[] compoundDrawables = getCompoundDrawables();
setCompoundDrawables(
compoundDrawables[0],
compoundDrawables[1],
visible ? mClearTextIcon : null,
compoundDrawables[3]);
}
}
非常简单的自定义控件,加了几个属性。
5.LoginActivity包含手机验证,和判断手机输入对错,控制控件颜色,膜拜主色为黑 、白、红还有灰色。
package com.yiwen.mobike.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.yiwen.mobike.R;
import com.yiwen.mobike.utils.MyConstains;
import com.yiwen.mobike.utils.ToastUtils;
import com.yiwen.mobike.views.ClearEditText;
import com.yiwen.mobike.views.CountTimerView;
import com.yiwen.mobike.views.MyToolBar;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cn.smssdk.EventHandler;
import cn.smssdk.SMSSDK;
import cn.smssdk.utils.SMSLog;
public class LoginActivity extends AppCompatActivity {
private static final String TAG = "LoginActivity";
@BindView(R.id.toolbar_login)
MyToolBar mToolbarLogin;
@BindView(R.id.et_phone)
ClearEditText mEtPhone;
@BindView(R.id.et_code)
EditText mEtCode;
@BindView(R.id.get_code)
Button mGetCode;
@BindView(R.id.loin_voice)
TextView mLoinVoice;
@BindView(R.id.login_query)
Button mLoginQuery;
@BindView(R.id.login_services)
TextView mLoginServices;
private boolean isNeedLogin = true;
private TextView mTvCountryCode;
private CountTimerView mCountTimeView;
private int phoneLength = 0;
private int codeLength = 0;
// 默认使用中国区号
private static final String DEFAULT_COUNTRY_ID = "42";
private SmsEventHandler mEventHandler;
private boolean isSendCode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
intView();
initDate();
initEvent();
}
private void intView() {
ButterKnife.bind(this);
}
private void initDate() {
SMSSDK.initSDK(this, "1***3", "4***7");//mob注册获取密钥
mEventHandler = new SmsEventHandler();
SMSSDK.registerEventHandler(mEventHandler);
}
private void initEvent() {
mToolbarLogin.setOnLeftButtonClickListener(new MyToolBar.OnLeftButtonClickListener() {
@Override
public void onClick() {
Go2Main();
}
});
mEtPhone.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) {
phoneLength = s.length();
if (phoneLength > 0) {
setRed(mGetCode);
} else {
setGray(mGetCode);
}
if (phoneLength > 0 && codeLength > 0) {
setRed(mLoginQuery);
} else {
setGray(mLoginQuery);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
mEtCode.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) {
codeLength = s.length();
if (phoneLength > 0 && codeLength > 0) {
setRed(mLoginQuery);
} else {
setGray(mLoginQuery);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
/**
* 改变bt颜色red设置可点击
*
* @param bt
*/
private void setRed(Button bt) {
bt.setClickable(true);
bt.setBackgroundResource(R.color.red);
}
/**
* 改变bt颜色gray设置不可点击
*
* @param bt
*/
private void setGray(Button bt) {
bt.setClickable(false);
bt.setBackgroundResource(R.color.gray);
}
class SmsEventHandler extends EventHandler {
@Override
public void afterEvent(final int event, final int result, final Object data) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//回调完成
if (result == SMSSDK.RESULT_COMPLETE) {
//返回支持发送验证码的国家列表
if (event == SMSSDK.EVENT_GET_SUPPORTED_COUNTRIES) {
// SMSSDK.getSupportedCountries();
onCountryListGot((ArrayList>) data);
//获取验证码成功
} else if (event == SMSSDK.EVENT_GET_VERIFICATION_CODE) {
// 请求验证码后,跳转到验证码填写页面
afterVerificationCodeRequested((Boolean) data);
//提交验证码成功
} else if (event == SMSSDK.EVENT_SUBMIT_VERIFICATION_CODE) {
// ToastUtils.show(LoginActivity.this, "验证码已发送");
mEtCode.setText("");
RegOK();
}
} else {
// 根据服务器返回的网络错误,给toast提示
try {
((Throwable) data).printStackTrace();
Throwable throwable = (Throwable) data;
JSONObject object = new JSONObject(
throwable.getMessage());
String des = object.optString("detail");
if (!TextUtils.isEmpty(des)) {
ToastUtils.show(LoginActivity.this, des);
return;
}
} catch (Exception e) {
SMSLog.getInstance().w(e);
}
}
}
});
}
private void RegOK() {
// ToastUtils.show(LoginActivity.this, "注册成功");
getSharedPreferences(MyConstains.IS_NEED_LOGIN, MODE_PRIVATE)
.edit()
.putBoolean(MyConstains.IS_NEED_LOGIN, false)
.apply();
Go2Main();
}
}
/**
* 获得支持的国家列表
*
* @param data
*/
private void onCountryListGot(ArrayList> data) {
for (HashMap country : data) {
String code = (String) country.get("zone");
String rule = (String) country.get("rule");
if (TextUtils.isEmpty(code) || TextUtils.isEmpty(rule)) {
continue;
}
Log.d(TAG, "onCountryListGot: " + code + ":" + rule);
}
}
/**
* 请求验证码成功后跳转
*
* @param data
*/
private void afterVerificationCodeRequested(Boolean data) {
String phone = mEtPhone.getText().toString().trim().replace("\\s*", "");
// String countryCode = mTvCountryCode.getText().toString().trim();
String countryCode = "86";
if (countryCode.startsWith("+")) {
countryCode = countryCode.substring(1);
}
isSendCode = false;
}
private void Go2Main() {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
@OnClick({R.id.get_code, R.id.loin_voice, R.id.login_query, R.id.login_services})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.get_code:
getCode();
break;
case R.id.loin_voice:
ToastUtils.show(LoginActivity.this,"语音验证");
break;
case R.id.login_query:
submitCode();
break;
case R.id.login_services:
ToastUtils.show(LoginActivity.this,"服务点击");
break;
}
}
/**
* 获取验证码
*/
private void getCode() {
String phone = mEtPhone.getText().toString().trim().replace("\\s*", "");
// String countryCode = mTvCountryCode.getText().toString().trim();
String countryCode = "+86";
// String countryCode = mTvCountryCode.getText().toString().trim();
if (checkPhoneNum(phone, countryCode)) {
/*请求获得验证码*/
Log.d(TAG, "getCode: " + phone + "**" + countryCode);
SMSSDK.getVerificationCode(countryCode, phone);
mCountTimeView = new CountTimerView(mGetCode);
mCountTimeView.start();
}
}
/**
* 检查手机号格式
*
* @param phone
* @param countryCode
*/
private boolean checkPhoneNum(String phone, String countryCode) {
if (countryCode.startsWith("+")) {
countryCode = countryCode.substring(1);
}
if (TextUtils.isEmpty(phone)) {
mEtPhone.setError("手机号格式有误");
//ToastUtils.show(this, "请输入手机号码");
// dissmissDialog();
return false;
}
if (countryCode.equals("86")) {
if (phone.length() != 11) {
mEtPhone.setError("手机号长度不正确");
// ToastUtils.show(this, "手机号长度不正确");
// dissmissDialog();
return false;
}
}
String rule = "^1(3|5|7|8|4)\\d{9}";
Pattern compile = Pattern.compile(rule);
Matcher matcher = compile.matcher(phone);
if (!matcher.matches()) {
mEtPhone.setError("您输入的手机号码格式不正确");
// ToastUtils.show(this, "您输入的手机号码格式不正确");
// dissmissDialog();
return false;
}
return true;
}
private void submitCode() {
String code = mEtCode.getText().toString().trim();
String mPhone = mEtPhone.getText().toString().trim().replace("\\s*", "");
if (TextUtils.isEmpty(code)) {
mEtCode.setError("请输入验证码");
// ToastUtils.show(this, "请输入验证码");
return;
}
Log.d(TAG, "submitCode: " + mPhone + code);
SMSSDK.submitVerificationCode("86", mPhone, code);
}
@Override
protected void onDestroy() {
super.onDestroy();
SMSSDK.unregisterEventHandler(mEventHandler);
}
}
6.最后说明:
验证手机号用了mob的SDK,经测试还是可以发短信的,不过用同一号码,两三次就没有用了,估计是防止频繁获取短信吧
今天就到这里了,具体代码可以到GitHub下载查看
写博客不容易,喜欢希望多给个start,老铁,抱拳了。