欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

视频项目笔记(3)

程序员文章站 2022-07-14 19:23:24
...

天天防腐笔记(3)

坚持看视频,忘记的东西只能铭记

  • 1、说明注册的流程,这里盗用老师的讲解图片

视频项目笔记(3)

所以要实现的是如何调用这个验证码接口,还有处理注册后页面跳转后所有问题,包括界面以及数据的存储

  • 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!

相关标签: 视频