视频项目笔记(3)
天天防腐笔记(3)
坚持看视频,忘记的东西只能铭记
- 1、说明注册的流程,这里盗用老师的讲解图片
所以要实现的是如何调用这个验证码接口,还有处理注册后页面跳转后所有问题,包括界面以及数据的存储
- 2、使用html样式去实现界面
// html方式设置文字样式不一
mUserAgreementTv.setText(Html.fromHtml("我已阅读并同意<font color='#24cfa2'>《天天防腐》用户协议</font>"));
- 3、验证码功能实现(老师给的代码直接拿来用)
首先我们来分析一下要声明有几种状态:
1)能够点击 - ->设置一个正常的状态
2)不能点击稍后 - ->向后台请求数据呈现的样子 多久之后可以再次获取
3)倒计时状态- ->停止获取验证码
然后写对应状态的代码,开启handler不断去更新状态,计算每一秒过去后按钮上数字的变化,如果秒数>0就减去一,接着往下继续倒数,否则就停止,这就是handler里面要做的事情
package com.gzucm.daydayfanfu.ui;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.widget.Button;
import android.widget.EditText;
import com.gzucm.daydayfanfu.R;
import com.gzucm.daydayfanfu.Util.GeneralUtil;
@SuppressLint("HandlerLeak")
public class VerificationCodeButton extends Button {
private int mTime;
private String mNormalStr;
private ColorStateList mNormalColorList;
private Drawable mNormalDrawable;
private int mTimerBackground;
private int mTimerColor;
// Bind input box
private EditText mBindPhoneEt;
// 是否开始加载
private boolean mStartLoad = false;
/**
* 利用handler做计时器
*/
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
if (mTime > 0) {
mTime -= 1;
timer();
} else {
stopRequest();
}
};
};
public VerificationCodeButton(Context context) {
this(context, null);
}
public VerificationCodeButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VerificationCodeButton(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
// 获取之前的属性(方便将它设置为正常状态)
mNormalStr = getText().toString();
mNormalColorList = getTextColors();
mNormalDrawable = getBackground();
TypedArray array = context.obtainStyledAttributes(attrs,
R.styleable.VerificationCodeButton);
// 计时的时候背景
mTimerBackground = array.getResourceId(
R.styleable.VerificationCodeButton_timing_background, 0);
mTimerColor = array.getColor(
R.styleable.VerificationCodeButton_timing_textColor, 0);
array.recycle();
}
/**
* 设置一个正常状态
*/
@SuppressLint("NewApi")
public void setNormal() {
this.setEnabled(true);
this.setText(mNormalStr);
this.setTextColor(mNormalColorList);
this.setBackground(mNormalDrawable);
}
/**
* 向后台请求数据呈现的样子
*/
public void startLoad() {
mStartLoad = true;
this.setEnabled(false);
setAttribute();
this.setText(" 请稍后... ");
}
private void setAttribute() {
if (mTimerBackground != 0) {
this.setBackgroundResource(mTimerBackground);
}
if (mTimerColor != 0) {
this.setTextColor(mTimerColor);
}
}
/**
* 多久之后可以再次获取
*
* @param time
*/
public void aginAfterTime(int time) {
setAttribute();
this.mTime = time;
// 一旦开始计时,这个按钮就不能点击了
this.setEnabled(false);
timer();
}
/**
* 停止获取验证码
*/
private void stopRequest() {
mStartLoad = false;
setNormal();
}
/**
* 计时开始获取
*/
private void timer() {
this.setText(formatDuring(mTime));
mHandler.sendEmptyMessageDelayed(0, 1000);
}
private CharSequence formatDuring(int mTime) {
if (mTime < 10) {
return "0" + mTime + "秒后重获";
}
return mTime + "秒后重获";
}
/**
* Binding a telephone box
*/
public void bindPhoneEditText(EditText editText) {
this.mBindPhoneEt = editText;
this.setEnabled(false);
setAttribute();
// 监听输入框的状态改变
this.mBindPhoneEt
.addTextChangedListener(phoneNumbersAddSpacesTextWatcher);
}
/**
* 自动添加空格的TextWatcher
*/
private TextWatcher phoneNumbersAddSpacesTextWatcher = 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) {
if (s == null || s.length() == 0)
return;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (i != 3 && i != 8 && s.charAt(i) == ' ') {
continue;
} else {
sb.append(s.charAt(i));
if ((sb.length() == 4 || sb.length() == 9)
&& sb.charAt(sb.length() - 1) != ' ') {
sb.insert(sb.length() - 1, ' ');
}
}
}
if (!sb.toString().equals(s.toString())) {
int index = start + 1;
if (sb.charAt(start) == ' ') {
if (before == 0) {
index++;
} else {
index--;
}
} else {
if (before == 1) {
index--;
}
}
mBindPhoneEt.setText(sb.toString());
mBindPhoneEt.setSelection(index);
}
// 上面就是加空格的逻辑
// Number is correct first, and then control VerificationCodeButton this is available
String trimStr = removeAllSpace(sb.toString());
if(GeneralUtil.judgePhoneQual(trimStr) && !mStartLoad){
// If is phone VerificationCodeButton is able
setNormal();
}else{
// Else VerificationCodeButton is enable
VerificationCodeButton.this.setEnabled(false);
setAttribute();
}
}
@Override
public void afterTextChanged(Editable s) {
}
};
public static String removeAllSpace(String str) {
String tmpstr = str.replace(" ", "");
return tmpstr;
}
}
在验证码的监听事件中,点击后就开始获取没法点击
case R.id.send_code_bt:
// 按钮点击,向后台发送请求
// 1.把按钮置为请稍后状态
mSendCodeBt.startLoad();
// 2.请求
requestUserCode();
// 3.获取后台返回值之后判断后台反馈,如果成功倒计时,否则你要把按钮的状态置又可以点
然后我们使用okHttp先发起对服务器的请求requestUserCode()
- - > 处理验证码接口返回的数据 dealCodeResult() ,此时我们需要开启一个handler去post一个线程,这个Runnable()的run方法是运行在主线程的,所以就可以更新UI , 这里可以参考 我的第一篇笔记,里面也遇到过这个问题
private void requestUserCode() {
// 向后台发送请求
// 1.本地验证
String userPhone = mUserPhoneEt.getText().toString().trim();
if(TextUtils.isEmpty(userPhone)){
Toast.makeText(this,"请输入手机号",Toast.LENGTH_LONG).show();
return;
}
// 2.往后台提交数据
// OKhttp
// 1.创建一个OkhttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
// 2.构建参数的body MultipartBody.FORM 表单形式
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
// 2.2封装参数
builder.addFormDataPart("appid", "1");
builder.addFormDataPart("sms_type","3");
builder.addFormDataPart("cell_phone",userPhone); // 添加多个参数
// 3. 构建一个请求 post 提交里面是参数的builder url()请求路径
Request request = new Request.Builder().url("http://v2.ffu365.com/index.php?m=Api&c=Util&a=sendVerifyCode")
.post(builder.build()).build();
// 4.发送一个请求
okHttpClient.newCall(request).enqueue(new Callback() {// 请求的回调
@Override
public void onFailure(Call call, IOException e) {
// 失败
}
@Override
public void onResponse(Call call, Response response) throws IOException { // 这个不是运行在主线程中
// 成功 数据在response里面 获取后台给我们的JSON 字符串
String result = response.body().string();
Log.e("TAG", result);
Gson gson = new Gson();
UserRequestCodeResult codeResult = gson.fromJson(result, UserRequestCodeResult.class);
dealCodeResult(codeResult);
}
});
}
/**
* 处理请求验证码的返回接口
* @param codeResult
*/
private void dealCodeResult(final UserRequestCodeResult codeResult) {
mHandler.post(new Runnable() {
@Override
public void run() {
// 3.获取后台返回值之后判断后台反馈,如果成功倒计时,否则你要把按钮的状态置又可以点
if(codeResult.errcode == 1){
mSendCodeBt.aginAfterTime(60);
}else{
Toast.makeText(UserRegisterActivity.this,codeResult.errmsg,Toast.LENGTH_LONG).show();
mSendCodeBt.setNormal();// 按钮恢复默认
}
}
});
}
- 4、注册逻辑
case R.id.user_register_bt:
dealUserRegister();
break;
这里进行本地验证,证明本地有输入注册信息,然后去访问后台,得到后台返回的JSON字符串(就是UserLoginResult类),这里有个注意的地方,就是此时返回的是登录的状态,不需要我们去再次登录,所以就可以跳转界面了,此时又得开启handler去更新界面,使用SharedPreferences设置登录状态为true , 然后把JSON字符串保存在该文件中。
模拟注册成功,先关闭当前页面,然后关闭登录页面,调用自己的activity管理类关闭其他页面。
private void dealUserRegister() {
// 1.本地验证
String userPhone = mUserPhoneEt.getText().toString().trim();
String password = mUserPasswordEt.getText().toString().trim();
String userCode = mUserCodeEt.getText().toString().trim();
if(TextUtils.isEmpty(userPhone)){
Toast.makeText(this,"请输入用户名",Toast.LENGTH_LONG).show();
return;
}
if(TextUtils.isEmpty(password)){
Toast.makeText(this,"请输入密码",Toast.LENGTH_LONG).show();
return;
}
if(TextUtils.isEmpty(userCode)){
Toast.makeText(this,"请输入验证码",Toast.LENGTH_LONG).show();
return;
}
// 2.往后台提交数据
// OKhttp
// 1.创建一个OkhttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
// 2.构建参数的body MultipartBody.FORM 表单形式
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
// 2.2封装参数 美团 20 15公共参数
builder.addFormDataPart("appid", "1");
builder.addFormDataPart("verify_code",userCode);
builder.addFormDataPart("cell_phone",userPhone); // 添加多个参数
builder.addFormDataPart("password", password);// MD5 AES
// 3. 构建一个请求 post 提交里面是参数的builder url()请求路径
Request request = new Request.Builder().url("http://v2.ffu365.com/index.php?m=Api&c=Member&a=register")
.post(builder.build()).build();
// 4.发送一个请求
okHttpClient.newCall(request).enqueue(new Callback() {// 请求的回调
@Override
public void onFailure(Call call, IOException e) {
// 失败
}
@Override
public void onResponse(Call call, Response response) throws IOException { // 这个不是运行在主线程中
// 成功 数据在response里面 获取后台给我们的JSON 字符串
// 注册完之后不需要用户再次登录 返回的结果就是登录的结果
String result = response.body().string();
Log.e("TAG", result);
Gson gson = new Gson();
final UserLoginResult loginResult = gson.fromJson(result, UserLoginResult.class);
mHandler.post(new Runnable() {
@Override
public void run() {
dealRegisterResult(loginResult);
}
});
}
});
}
// 3.处理返回的数据
private void dealRegisterResult(UserLoginResult loginResult) {
// 首先判断有没有成功
if(loginResult.getErrcode() == 1){
// 成功处理
// 1.需要保存登录状态 当前设置为已登录
SharedPreferences sp = getSharedPreferences("info",MODE_PRIVATE);
sp.edit().putBoolean("is_login",true).commit();
// 2.需要保存用户信息
UserLoginResult.DataBean userData = loginResult.getData();
// SharedPreferences 怎么保存对象 把对象转为JSON String --> SharedPreferences
Gson gson = new Gson();
String uesrInfoStr = gson.toJson(userData);
// 保存的用户信息为Json格式的字符串
sp.edit().putString("user_info",uesrInfoStr).commit();
ActivityManagerUtil.getInstance().finishActivity(this);
ActivityManagerUtil.getInstance().finishActivity(UserLoginActivity.class);
}else{
// 登录失败
Toast.makeText(this,loginResult.getErrmsg(),Toast.LENGTH_LONG).show();
}
}
- 5、活动的管理类,单例模式(开发时经常使用)
定义:只有一个实例在整个应用中,不适用于变化的对象
应用场景:某些操作配置的类(比如你要换应用的皮肤)、管理的一些类(本文管理Activity的类)、任务管理器、网站的计数器、数据库连接池的设计
考虑同步:在 if(mInstance == null) 里面可能两个线程进去后都在那里睡觉,睡完都有可能起来去new一个对象,这就不符合我们的单例模式了
public class ActivityManagerUtil {
private Map<String,Activity> mActivitys;
private static ActivityManagerUtil mInstance;
//整个应用里面只有单个实例
//为什么私有,不让其他类new
private ActivityManagerUtil(){
mActivitys = new HashMap<>();
}
//其他地方需要用的获取方式
public static ActivityManagerUtil getInstance(){
if(mInstance == null){
//需要考虑线程并发以及同步问题
synchronized (ActivityManagerUtil.class){
if(mInstance == null){
mInstance = new ActivityManagerUtil();
}
}
}
return mInstance;
}
//添加Activity
public void addActivity(Activity activity){
mActivitys.put(activity.getClass().getName(),activity);
}
//关闭Activity
public void finishActivity(Activity activity){
Activity finish = mActivitys.get(activity.getClass().getName());
finish.finish();
mActivitys.remove(activity.getClass().getName());
}
//关闭Activity
public void finishActivity(Class<? extends Activity> activityClazz){
Activity finish = mActivitys.get(activityClazz.getName());
finish.finish();
mActivitys.remove(activityClazz.getName());
}
}
做到这里,我发现都是这样做的:去访问后台拿到数据解析,看要不要保存到本地文件记录,然后开启handler在主线程更新UI , 根据拿来的数据去判断UI界面的状态,我们拿到数据JSON,什么时候保存都可以,但是我们需要处理逻辑,在登录的时候才需要保存用户的信息,所以在这里才保存SharedPreferences。刷新了我对SharedPreferences的看法,好多地方我都看到它的身影,所以在项目中作为小型存储,是很不错的选择,又可以设置登录的状态,还可以保存用户信息,重新进入就可以不用重新登录。可以说,就是登录状态的控制器,用户信息的数据库。
天天防腐的视频教学第四天完成,谢谢老师的一番讲解,记录下来,方便以后遇到的问题可以查找帮助。Thank you!
推荐阅读
-
利用selenium和ffmpeg爬取m3u8 ts视频《进击的巨人》
-
python3下载抖音视频的完整代码
-
格式工厂视频画面怎么设置为4:3比例? 格式工厂修改视频比例的教程
-
Premiere Pro CS3怎么给视频配音? pr给视频加配音的教程
-
3dmax怎么制作动画? 3dmax制作动画视频的教程
-
Linux计划任务Crontab学习笔记(3):配置文件
-
惠普ProBook 446 G3值得买吗?惠普ProBook 446 G3笔记本全面深度评测图解
-
前端笔记知识点整合之HTML5&CSS3(上)新特性&音频视频&本地存储
-
CSS3中的Media Queries学习笔记
-
Python3 多进程编程 - 学习笔记