zoukankan      html  css  js  c++  java
  • 【Android】登陆界面设计

    界面布局

    布局其实很简单,用相对布局累起来就可以了,然后注册和记住密码这两个控件放在一个水平线性布局里

    界面底部还设置了一个QQ一键登录的入口,可以直接用。

    控件的ID命名有点乱

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingTop="100dp"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:background="@drawable/im_background">
        <ImageView
            android:id="@+id/iv_1"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_centerHorizontal="true"
            android:scaleType="fitCenter"
            android:src="@drawable/image_boy"/>
        <EditText
            android:id="@+id/et_username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:lines="1"
            android:layout_marginTop="10dp"
            android:hint="@string/hint_username"
            android:textColorHint="@color/app_white"
            android:textColor="@color/app_white"
            android:layout_below="@id/iv_1"/>
        <EditText
            android:id="@+id/et_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:lines="1"
            android:hint="@string/hint_password"
            android:inputType="textPassword"
            android:layout_marginTop="10dp"
            android:layout_below="@id/et_username"
            android:textColor="@color/app_white"
            android:textColorHint="@color/app_white"/>
        <LinearLayout
            android:id="@+id/ly_1"
            android:layout_below="@id/et_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <CheckBox
                android:id="@+id/cb_rm"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/et_password"
                android:text="@string/checkbox_re"
                android:textColor="@color/app_white"
                android:layout_gravity="left"
                android:layout_weight="3"/>
    
            <TextView
                android:id="@+id/btn_register"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:layout_gravity="right"
                android:gravity="center"
                android:text="@string/button_cancel"
                android:textColor="@color/app_white"
                android:textSize="14sp"
                android:layout_weight="1"/>
        </LinearLayout>
    
        <Button
            android:id="@+id/btn_confirm"
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:layout_below="@id/ly_1"
            android:layout_marginTop="35dp"
            android:background="@drawable/btn_bg_red"
            android:text="@string/button_confirm"
            android:layout_centerHorizontal="true"
            android:textColor="@color/app_white" />
    
    
    <ImageButton
        android:id="@+id/im_qq"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:src="@drawable/qq"
        android:scaleType="fitCenter"/>
    
    </RelativeLayout>

    注意一下我在styles中把所有框颜色改成了白色(为了更好适配黑色背景),你也可以选择不改

    <resources>
    
        <!-- Base application theme. -->
            <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
                <item name="colorAccent">@color/colorPrimary</item>
                <!-- AppCompatEditText默认状态状态设置底线颜色 -->
                <item name="colorControlNormal">#FFFFFF</item>
                <!-- AppCompatEditText选择的底线颜色 -->
                <item name="colorControlActivated">#FFFFFF</item>
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        </style>
    
    </resources>
    styles.xml

    逻辑实现

    数据存储:

    因为map正好key与value一一对应(即name与password一一对应),所以选择使用map来进行账号的保存。

    而本地的存储是用的SharedPreference来实现的,但是由于SharedPreference无法直接对map进行存储,所以需要单独写一个方法来实现。

    密码是用Base64做了一次转码操作,增强了一丢丢的安全性。

    import android.content.Context;
    import android.content.SearchRecentSuggestionsProvider;
    import android.content.SharedPreferences;
    import android.util.Base64;
    
    import com.paul.notebook.LoginActivity;
    
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    /**
     * 作者:created by 巴塞罗那的余晖 on 2019/3/10 18:45
     * 邮箱:zhubaoluo@outlook.com
     * 不会写BUG的程序猿不是好程序猿,嘤嘤嘤
     */
    public class userData {
        private Map<String,String> root;//用户数据根目录
        private static String flag="user_data";//标识
        private  boolean loginState=false;
        private Context mcontext;
        public userData(Context context)
        {
            root=new HashMap<>();
            mcontext=context;
            readData(flag);
        }
        public boolean findExistUsername(String name)//查找是否有该用户名
        {
            return root.containsKey(name);
        }
        public boolean verifyPassword(String name,String password)//验证用户名和密码是否匹配
        {
            if(findExistUsername(name))
            {
                if(root.get(name).equals(password))
                {
                    return true;
                }
            }
            return false;
        }
        public boolean getRegister(String name,String password)//注册,并返回是否注册成功
        {
            if(findExistUsername(name)||name.equals("")||password.equals(""))
            {
                return false;
            }
            root.put(name,password);
            saveData(flag);
            return true;
        }
        private void saveData(String key)//保存数据到本地
        {
            //原理:将Map格式转换成json字符串,并指定一个特定标识来记录,Value按照发生器逐一保存就好
            JSONArray mJsonArray = new JSONArray();
            Iterator<Map.Entry<String, String>> iterator = root.entrySet().iterator();
            JSONObject object = new JSONObject();
    
            while (iterator.hasNext()) {
                Map.Entry<String, String> entry = iterator.next();
                try {
                    String password_encode=entry.getValue();
                    password_encode=Base64.encodeToString(password_encode.getBytes(),Base64.NO_WRAP);
                    object.put(entry.getKey(), password_encode);
    
                } catch (JSONException e) {
                    //异常处理
                    //妈咪妈咪哄!BUG快离开!
                }
            }
            mJsonArray.put(object);
            SharedPreferences sp=mcontext.getSharedPreferences("config",Context.MODE_PRIVATE);
            SharedPreferences.Editor editor=sp.edit();
            editor.putString(key,mJsonArray.toString());
            editor.commit();
        }
         private void readData(String key)//sharedpreferences从本地读取数据
        {
            root.clear();
            SharedPreferences sp=mcontext.getSharedPreferences("config",Context.MODE_PRIVATE);
            String result=sp.getString(key,"");
            try {
                JSONArray array=new JSONArray(result);
                for(int i=0;i<array.length();i++)
                {
                    JSONObject itemObject =array.getJSONObject(i);
                    JSONArray names=itemObject.names();
                    if(names!=null)
                    {
                        for(int j=0;j<names.length();j++)
                        {
                            String name=names.getString(j);
                            String value=itemObject.getString(name);
                            value=new String(Base64.decode(value.getBytes(),Base64.NO_WRAP));
                            root.put(name,value);
                        }
                    }
                }
            }catch (JSONException e){
                //异常处理
                //妈咪妈咪哄!BUG快离开!
            }
        }
        private void clearLocalData()//清除本地数据,这个方法一般用不到,故写成私有的
        {
            SharedPreferences preferences = mcontext.getSharedPreferences("config", Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = preferences.edit();
            editor.clear();
            editor.commit();
        }
    
    }

    主代码:

    由于有一个保存密码功能,所以需要单独再使用SharedPreference来存储记住密码的账号啥的。同样也是使用Base64进行转码操作。

    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Base64;
    import android.view.View;
    import android.widget.Button;
    import android.widget.CheckBox;
    import android.widget.CompoundButton;
    import android.widget.EditText;
    import android.widget.ImageButton;
    import android.widget.ImageView;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.bumptech.glide.Glide;
    import com.paul.notebook.Tools.Base64Activity;
    import com.paul.notebook.dataBase.userData;
    
    /**
     * 作者:created by 巴塞罗那的余晖 on 2019/3/10 18:35
     * 邮箱:zhubaoluo@outlook.com
     * 不会写BUG的程序猿不是好程序猿,嘤嘤嘤
     */
    public class LoginActivity extends AppCompatActivity {
        //声明变量
        private Boolean login_state=false;
        private TextView mregister;
        private ImageView mimageView;
        private ImageButton mimageButton;
        private EditText muesername, mpassword;
        private Button mconfirm;
        private userData data;
        private CheckBox checkBox;
        private String LOCAL_USER_NAME="paul";
        private String portraitURL = "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2281052020,3958255485&fm=27&gp=0.jpg";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_login);
            //找布局控件对应ID
            mimageView = findViewById(R.id.iv_1);
            mimageButton = findViewById(R.id.im_qq);
            muesername = findViewById(R.id.et_username);
            mpassword = findViewById(R.id.et_password);
            mconfirm = findViewById(R.id.btn_confirm);
            mregister = findViewById(R.id.btn_register);
            checkBox=findViewById(R.id.cb_rm);
            data=new userData(LoginActivity.this);
            //Glide.with(LoginActivity.this).load(portraitURL).into(mimageView);
            initLogin();
            mregister.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    {
                        if(data.getRegister(muesername.getText().toString(),mpassword.getText().toString()))
                        {
                            Toast.makeText(LoginActivity.this,"注册成功!",Toast.LENGTH_SHORT).show();
                        }
                        else
                        {
                            Toast.makeText(LoginActivity.this,"注册失败!用户名重复或者为空!",Toast.LENGTH_SHORT).show();
                        }
                    }
    
    
                }
            });
            mconfirm.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(data.verifyPassword(muesername.getText().toString(),mpassword.getText().toString()))
                    {
                        Intent intent=new Intent(LoginActivity.this, Base64Activity.class);
                        saveLogin(login_state);
                        startActivity(intent);
                        //Toast.makeText(LoginActivity.this,muesername.getText().toString()+"欢迎您!",Toast.LENGTH_SHORT).show();
                    }
                    else
                    {
                        Toast.makeText(LoginActivity.this,"登陆失败!请检查用户是否存在或者密码错误!",Toast.LENGTH_SHORT).show();
                    }
                }
            });
            checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    login_state=isChecked;
                }
            });
        }
        private void saveLogin(boolean flag)
        {
            SharedPreferences sharedPreferences = LoginActivity.this.getSharedPreferences("ACCOUNT_REMEMBER", MODE_PRIVATE);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            String secreat_name = muesername.getText().toString();
            String secreat_password=mpassword.getText().toString();
            if(sharedPreferences.getBoolean("flag",false))
            {
                return;
                //验证通过,不需要再次保存了
            }
            if(flag) {
                secreat_password=Base64.encodeToString(secreat_password.getBytes(),Base64.NO_WRAP);
                editor.putString("name",secreat_name);
                editor.putString("password", secreat_password);
                editor.putBoolean("flag", flag);
                editor.commit();
            }
            else{
                editor.clear();
                editor.putString("name",secreat_name);
                editor.putBoolean("flag",false);
                editor.commit();
            }
    
        }
        private void cleanState()
        {
            SharedPreferences sharedPreferences = LoginActivity.this.getSharedPreferences("ACCOUNT_REMEMBER", MODE_PRIVATE);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.clear();
            editor.commit();
        }
        private void initLogin()
        {
            SharedPreferences sharedPreferences=LoginActivity.this.getSharedPreferences("ACCOUNT_REMEMBER", MODE_PRIVATE);
            if(sharedPreferences.getBoolean("flag",false)){
                String decode_password=sharedPreferences.getString("password","");
                decode_password= new String(Base64.decode(decode_password.getBytes(),Base64.NO_WRAP));
                muesername.setText(sharedPreferences.getString("name",""));
                mpassword.setText(decode_password);
                checkBox.setChecked(true);
            }
            else {
                muesername.setText(sharedPreferences.getString("name",""));
                checkBox.setChecked(false);
            }
        }
    }

    Base64测试

    当时考虑到安全性问题所以就写了这个来测试Base64的加密解密,但是加密后无法通过解密获得原文,超级烦!!!

    下面列出两个我遇到的(应该大部分就是这两个)问题。

    1.字符串转换问题

    当Base64解密的时候返回的是一个byte[],肯定需要转成字符串,所以我就直接使用了toString方法,结果发现转换出来的是@开头的一堆乱码。

    在网上查阅资料后发现转换String有两种方法,一种是直接toString,还有一种是new String

    toString()与new String ()用法区别
    
    str.toString是调用了b这个object对象的类的toString方法。一般是返回这么一个String:[class name]@[hashCode]。
    new String(str)是根据parameter是一个字节数组,使用java虚拟机默认的编码格式,将这个字节数组decode为对应的字符。若虚拟机默认的编码格式是ISO-8859-1,按照ascii编码表即可得到字节对应的字符。
    
    什么时候用什么方法呢?
    
    new String()一般使用字符转码的时候,byte[]数组的时候
    toString()将对象打印的时候使用 
    --------------------- 
    作者:火星码农 
    来源:CSDN 
    原文:https://blog.csdn.net/u014622411/article/details/45147363 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    2.编码方式选择问题

    在网上看到的Base64基本使用方法都是直接传Base64.DEFAULT,使用默认编码方式来进行编码。看似没有问题,但是!!!如果字符串过长,比如80位,那么在转码的过程中会自动添加换行符,如果是和别的模块进行对接就会出现错误···

    所以我们一般就强制设置成忽略所有换行符就OK了

    具体五种参数的含义:

    CRLF:这个参数看起来比较眼熟,它就是Win风格的换行符,意思就是使用CR LF这一对作为一行的结尾而不是Unix风格的LF
    
    DEFAULT:这个参数是默认,使用默认的方法来编码
    
    NO_PADDING:这个参数是略去编码字符串最后的“=”
    
    NO_WRAP:这个参数意思是略去所有的换行符(设置后CRLF就没用了)
    
    URL_SAFE:这个参数意思是编码时不使用对URL和文件名有特殊意义的字符来作为编码字符,具体就是以-和_取代+和/
    --------------------- 
    原文:https://blog.csdn.net/z191726501/article/details/52778478 

    3.使用方法

    //加密
     String Econtent=“我是原文”;//自己定义一个字符串就好
     Econtent= Base64.encodeToString(Econtent.getBytes(),Base64.NO_WRAP);
    //解密
     String Dcontent=“我是密文”;
    Dcontent=new String(Base64.decode(Dcontent.getBytes(),Base64.NO_WRAP));//在这里特别注意一下使用new String就行

    最终效果图(仅供参考,还是很丑的···)

    背景、头像和QQ一键登录图片自己去到网上找一下就可以了,注意一下大小什么的,虽然用了fit_center属性。

     

  • 相关阅读:
    JavaScript递归函数解“汉诺塔”
    《JavaScript DOM编程艺术》学习笔记(三)
    《JavaScript DOM编程艺术》学习笔记(二)
    《JavaScript DOM编程艺术》学习笔记(一)
    《手把手教你实现电商网站开发》课程学习总结
    学校水卡、本地公交卡破解记
    第一篇博客
    mysql5.7.5以上版本使用distinct和order by 报错备忘录
    group_concat长度限制踩坑备忘录
    ArrayList、LinkedList、Vector的区别
  • 原文地址:https://www.cnblogs.com/robotpaul/p/10541585.html
Copyright © 2011-2022 走看看