Android开发(2):实现简单的注册登录功能

Github地址:美食分享平台,欢迎star和fork

效果图

在这里插入图片描述
在这里插入图片描述

思路

实现简单的注册登录(账号密码登录)功能,除了注册登录界面的设计,需要数据库和加密功能的配合。思路就是

  1. 注册界面注册账号,进行用户数据初始化。
  2. 对密码进行加密,并存入数据库。
  3. 登录界面登录账号,进行账号密码验证,并设置是否记住密码
  4. 密码正确则进入主界面。
    特殊情况:
  5. 如果退出登录,那么记住密码选项应该保存,账号密码也保存。
  6. 如果是第一次登录,则判断数据库中是否有记住密码的账户,有的话则直接登录该用户。

为了解决第一次登录和退出登录的不同,我的解决方法是:新建一个MyApp活动作为入口活动,然后在这个活动中启动Login活动(并发送"first_in"==true),Login活动对此进行判断是否是第一次进入APP。

//MyAPP
public class FoodShareTest extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = new Intent(FoodShareTest.this, Login.class);
        intent.putExtra("first_in", true);
        startActivity(intent);
    }
}

//Login
//...
        //如果是第一次打开并且不是退出登录,则直接登录
        if(getIntent().getBooleanExtra("first_in",false)) {
            Log.d("food","first_in");
            List<User> users = LitePal.findAll(User.class);
            for (User u : users) {
                if (u.getRemember().equals(1)) {
                    //登入并存入LoginUser
                    LoginUser.getInstance().login(u);
                    //启动主界面
                    Intent intent1 = new Intent(Login.this, ButtomTab.class);
                    startActivity(intent1);
                    toastUtils.showShort(Login.this, "账户" + u.getName() + " 登录成功");
                    break;
                }
            }
        }
 //...

界面设计

登录和注册界面的较为简单,就不多做阐述,代码在
登录界面注册界面
然后是主界面的退出登录,上一节我们使用了bottomTab,我在设置界面放了退出登录,为了简便测试,你可以直接在MainActicity里面放个button代替。

其他准备

1. 数据库

使用的是郭神的LitePal数据库,操作起来比较方便,可以参考郭神的第一行代码第二版或者参考项目地址
接着再项目java的com.***.***文件夹内new directory,命名为db,在里面新建一个model文件夹和一个LoginUser类,如下(忽略DBManager):
在这里插入图片描述
其中User是model,LoginUser是用来临时存放登录的用户数据,而且用来管理登陆账户较为方便,LoginUser使用单例模式实现
(User类中只有get,set等,而LoginUser有login,logout,update等方法)

User类(此处代码缺少get和set方法,在Android Studio中右键Generate Getter and Setter,然后全选,在ok即可)

package com.foodsharetest.android.db.model;
import android.util.Log;
import androidx.annotation.NonNull;
import org.litepal.crud.LitePalSupport;
/**
LitePal巨坑之一!下面的属性remember本可以设计为boolean值,但是LitePal的boolean值update不了!int尝试也不行!所以只能用Integer代替。
可能如int和boolean这种基本类型可能都是设为不可更改(但是第一次set还是成功了呀。。。后面就失败了)
 **/
public class User extends LitePalSupport implements Comparable<User> {
    private int id;
    private String name;
    private String password;
    private Integer remember;
    private byte[] portrait;
    private String region;
    private String gender;
    private String brithday;


    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", remember=" + remember +
                ", portrait='" + portrait + '\'' +
                ", region='" + region + '\'' +
                ", gender='" + gender + '\'' +
                ", brithday='" + brithday + '\'' +
                '}';
    }

    //check是传入未MD5加密的
    public boolean checkPassword(String str){
//        if (remember.equals(0)) str = com.foodsharetest.android.util.MD5.md5(str);
        if (password.equals(str)) return true;
        else return false;
    }

    @Override
    public boolean equals(Object o) {
        if (o != null) {
            User UserInfo = (User) o;
            return (getId()==UserInfo.getId());
        } else {
            return false;
        }
    }

    @Override
    public int compareTo(@NonNull User User) {
        return this.getName().compareTo(User.getName());
    }

//缺少getter和setter
}

LoginUser类(同上,需要Generate getter and setter)

package com.foodsharetest.android.db;

import android.app.Application;
import android.util.Log;
import androidx.annotation.Nullable;
import com.foodsharetest.android.db.model.User;
import org.litepal.LitePal;
import java.util.List;

//使用饿汉模式实现单例的登录用户信息记录
//但是此处是存储用户信息的副本,这点是否合适有待考虑,可以考虑使用User类
//LoginUser相对于模拟登陆,并且作为存储数据库的一个缓冲区
public class LoginUser extends Application {
    private static LoginUser login_user = new LoginUser();
    private static User _user;
    private int id;
    private String name;
    private byte[] portrait;
    private String region;
    private String gender;
    private String brithday;


    public static LoginUser getInstance(){
        return login_user;
    }

    public User getUser(){
        return _user;
    }

    //保存至数据库
    public void update(){
        if(_user.getId()==this.id){
            _user.setName(this.name);
            _user.setPortrait(this.portrait);
            _user.setGender(this.gender);
            _user.setRegion(this.region);
            _user.setBrithday(this.brithday);
            _user.update(_user.getId());
        }
    }

    //重新init
    public void reinit(){
        login_user.id = _user.getId();
        login_user.name = _user.getName();
        login_user.portrait = _user.getPortrait();
        login_user.region = _user.getRegion();
        login_user.gender = _user.getGender();
        login_user.brithday = _user.getBrithday();
    }

    public boolean login(User user) {
        _user = user;
        login_user.id = user.getId();
        login_user.name = user.getName();
        login_user.portrait = user.getPortrait();
        login_user.region = user.getRegion();
        login_user.gender = user.getGender();
        login_user.brithday = user.getBrithday();
        return true;
    }

    public static boolean logout(){
        if(login_user.id == -1) return false;
        login_user.id = -1;
        login_user.name = null;
        login_user.portrait = null;
        login_user.region = null;
        login_user.gender = null;
        login_user.brithday = null;
        return true;
    }

    @Override
    public String toString() {
        return "LoginUser{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", portrait ='" + portrait + '\'' +
                ", region='" + region + '\'' +
                ", gender='" + gender + '\'' +
                ", brithday='" + brithday + '\'' +
                '}';
    }

2. 加密

只实现简单的注册登录功能,此处也可以略过,在这里我使用的是MD5加密。网上有特别多的解密代码,这里提供一个:
在java的包文件夹下新建一个utils文件夹,然后在里面新建一个MD5类
代码如下:

package com.foodsharetest.android.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

//MD5加密算法
public class MD5 {
    public static String md5(String content) {
        byte[] hash;
        try {
            hash = MessageDigest.getInstance("MD5").digest(content.getBytes());

            StringBuilder hex = new StringBuilder(hash.length * 2);
            for (byte b : hash) {
                if ((b & 0xFF) < 0x10){
                    hex.append("0");
                }
                hex.append(Integer.toHexString(b & 0xFF));
            }
            return hex.toString();

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("NoSuchAlgorithmException",e);
        }
    }
}

活动设计

这也是全部最关键的地方(此处自建了一个ActivityCollector类来方便管理活动,可以直接删掉与这个相关的代码,不影响运行)

1. 注册

直接放代码,注册逻辑较简单。

package com.foodsharetest.android.ui.activity;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.foodsharetest.android.R;
import com.foodsharetest.android.db.model.User;
import com.foodsharetest.android.util.ActivityCollector;
import com.foodsharetest.android.util.MD5;
import com.foodsharetest.android.util.PhotoUtils;

import org.litepal.LitePal;
import org.litepal.tablemanager.Connector;

import java.util.List;

public class Register extends AppCompatActivity implements View.OnClickListener {
    private EditText et_account_name,et_password;
    private Button register;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityCollector.addActivity(this);
        setContentView(R.layout.activity_register);


        et_account_name = (EditText) findViewById(R.id.et_account_name);
        et_password = (EditText) findViewById(R.id.et_password);
        register = (Button)findViewById(R.id.register);
        Connector.getDatabase();
        //注册逻辑实现
        register.setOnClickListener(this);
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.register:
                String name = et_account_name.getText().toString();
                String password = et_password.getText().toString();
                password = MD5.md5(password);  //MD5加密
                List<User> users = LitePal.where("name==?", name).find(User.class);
                Toast mToast = Toast.makeText(this, null, Toast.LENGTH_SHORT); //下面用setText不用makeText,为了取消小米手机自带的Toast应用名

                //判断用户名是否存在
                if (!users.isEmpty()) {
                    mToast.setText("该用户名已存在");
                    mToast.show();
                }
                //如果用户名不存在,则新建用户
                else {
                    User user = new User();
                    user.setName(name);
                    user.setPassword(password);
                    //默认不记住密码,并设置默认头像
                    user.setPortrait((new PhotoUtils()).file2byte(this ,"default_portrait.jpg"));
                    user.setRemember(0);
                    user.save();

                    mToast.setText("注册成功");
                    mToast.show();

                    Intent intent = new Intent(Register.this, Login.class);
                    startActivity(intent);
                }
        }
    }

}

2. 登录

登录界面较为复杂,为了防止代码冗杂,我把import部分略去了
代码如下:

public class Login extends AppCompatActivity implements View.OnClickListener {
    private EditText et_name,et_password;
    private Button login,register;
    private ImageView iv_eye,iv_more_account;
    private CheckBox cb_remember;
    private boolean passwordVisible = false;
    private ToastUtils toastUtils = new ToastUtils();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityCollector.addActivity(this);
        setContentView(R.layout.activity_login);


        login = (Button) findViewById(R.id.login);
        register = (Button) findViewById(R.id.register);
        et_name = (EditText) findViewById(R.id.et_account_name);
        et_password = (EditText) findViewById(R.id.et_password);
        iv_eye = (ImageView) findViewById(R.id.iv_eye);
        iv_more_account = (ImageView) findViewById(R.id.iv_more_accout);
        cb_remember = (CheckBox) findViewById(R.id.cb_remember);


        //设置监听器
        login.setOnClickListener(this);
        register.setOnClickListener(this);
        iv_eye.setOnClickListener(this);
        iv_more_account.setOnClickListener(this);

        //如果是第一次打开并且不是退出登录,则直接登录
        if(getIntent().getBooleanExtra("first_in",false)) {
            Log.d("food","first_in");
            List<User> users = LitePal.findAll(User.class);
            for (User u : users) {
                if (u.getRemember().equals(1)) {
                    //登入并存入LoginUser
                    LoginUser.getInstance().login(u);
                    //启动主界面
                    Intent intent1 = new Intent(Login.this, ButtomTab.class);
                    startActivity(intent1);
                    toastUtils.showShort(Login.this, "账户" + u.getName() + " 登录成功");
                    break;
                }
            }
        }
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }

    @Override
    protected void onStart(){
        super.onStart();
        //从本地数据库判断是否记住密码
        List<User> users = LitePal.findAll(User.class);
        for (User u:users){
            if(u.getRemember().equals(1)){
                et_name.setText(u.getName());
                et_password.setText("12345678");  //因为限制密码不能是全数字,利用12345678为通用密码简化验证,但会降低安全性
                cb_remember.setChecked(true);
                break;
            }
        }
    }

    @Override
    public void onClick(View v){
        String name = et_name.getText().toString();
        String password=et_password.getText().toString();
        switch (v.getId()){
            //注册按钮的逻辑
            case R.id.register:
                Intent intent = new Intent(Login.this, Register.class);
                startActivity(intent);
                break;
            //登录按钮的逻辑
            case R.id.login:
                boolean login_flag = false; //是否登录成功的标志,
                User user = LitePal.where("name=?",name).findFirst(User.class);

                //根据user的remember状态,判断是否需要MD5加密
                if (password.equals("12345678")) password = user.getPassword();
                else password = MD5.md5(password);
                //密码正确则登录成功
                if (user.checkPassword(password)){
                    //更新remember状态
                    if(cb_remember.isChecked()) {
                        user.setRemember(1);
                    }else{
                        user.setRemember(0);
                    }
                    user.update(user.getId());
                    //用户登入,存入LoginUser
                    LoginUser.getInstance().login(user);
                    //启动主界面
                    Intent intent1 = new Intent(Login.this, ButtomTab.class);
                    startActivity(intent1);
                    login_flag = true;
                    toastUtils.showShort(Login.this,"账户"+user.getName()+" 登录成功");
                    break;
                }else {
                    user.setRemember(0);
                }

                if(login_flag == false){
                    toastUtils.showShort(Login.this,"登录失败");
                }
                break;
            //隐藏密码功能
            case R.id.iv_eye:
                if(passwordVisible){  //如果可见,则转为不可见
                    iv_eye.setSelected(false);
                    et_password.setTransformationMethod(PasswordTransformationMethod.getInstance());
                    passwordVisible = false;
                }else {  //如果不可见,则转为可见
                    iv_eye.setSelected(true);
                    et_password.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
                    passwordVisible = true;
                }
                break;
            //显示本地所有登录信息
            case R.id.iv_more_accout:
                List<User> users1 = LitePal.findAll(User.class);
                for(User u:users1) Log.d("food",""+u.toString());
                break;
        }
    }

}

参考链接:

如何优雅的实现登录注册
Litepal实现登录注册
inputtype取值
EditText的可见不可见
浅谈android的数据加密
MD5加密
Android设计模式之单例模式的七种写法
android使用全局变量的两种方法

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值