【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属性。

【Android】登陆界面设计的更多相关文章
- QML与C++交互:登陆界面设计
QML与C++交互:登陆界面设计 本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 环境: 主机:WIN7 开发环境:Qt5.2.1 说明: QML设计前 ...
- Android的界面设计工具 DroidDraw
Android的界面设计工具 DroidDraw DroidDraw 下载地址:http://code.google.com/p/droiddraw/ 如图 也可以使用在线的版本(http://www ...
- Android典型界面设计(3)——访网易新闻实现双导航tab切换
一.问题描述 双导航tab切换(底部区块+区域内头部导航),实现方案底部区域使用FragmentTabHost+Fragment, 区域内头部导航使用ViewPager+Fragment,可在之前博客 ...
- Android典型界面设计(5)——使用SlidingMenu和DrawerLayout分别实现左右侧边栏
一.问题描述 侧边栏是Android应用中十分常见的界面效果,可随主屏在左侧或右侧联动,是特别适应手机等小屏幕特性的典型界面设计方案之一,常用作应用的操作菜单,如图所示 实现侧边栏可以使用第三方组件s ...
- Android典型界面设计(6)——ActionBar Tab+ViewPager+Fagment实现滑动导航
一.问题描述 在Android典型界面设计一文中,实现典型滑动导航界面,其实使用ActionBar 也可以轻松实现这一效果,甚至也可实现类似Android典型界面设计(3)的双导航效果.可见Actio ...
- Android典型界面设计(7) ——DrawerLayout+Fragement+ViewPager+PagerTabStrip实现双导航
一.问题描述 在Android典型界面设计(3)的我们实现了双导航效果,即外层底部导航和内部区域的头部导航,如网易新闻等很多应用采用了这种导航,但Google提供DrawerLayout可实现抽屉式导 ...
- android ui界面设计参数讲解
百度文库: http://wenku.baidu.com/link?url=s66Hw6byBEzmjL77doYL1YQN4Y_39F7MovaHKs5mVGrzTDOQCAmiM-1N_6Cdm- ...
- Android典型界面设计-访网易新闻实现双导航tab切换
一.问题描述 双导航tab切换(底部区块+区域内头部导航),实现方案底部区域使用FragmentTabHost+Fragment, 区域内头部导航使用ViewPager+Fragment,可在之前博客 ...
- WDA演练一:用户登陆界面设计(二)
一,登陆界面设计: 1.将系统编号灰显,默认初值 2.密码栏勾选密码显示,这样就不会明文显示在页面上了: Init方法中添加默认值代码: METHOD wddoinit . DATA lo_nd_zh ...
- Android典型界面设计——ViewPage+Fragment实现区域顶部tab滑动切换
一.问题描写叙述 本系列将结合案例应用,陆续向大家介绍一些Android典型界面的设计,首先说说tab导航,导航分为一层和两层(底部区块+区域内头部导航).主要实现方案有RadioGroup+View ...
随机推荐
- 初探C++运算符重载学习笔记<2> 重载为友元函数
初探C++运算符重载学习笔记 在上面那篇博客中,写了将运算符重载为普通函数或类的成员函数这两种情况. 以下的两种情况发生.则我们须要将运算符重载为类的友元函数 <1>成员函数不能满足要求 ...
- cgi程序读取post发送的特殊字符,尤其适合于微信公众平台开发中发送被动消息
[问题]用c编写cgi程序怎样取出html表单post来的数据? [分析]html表单post来的数据形如username="zhang"&&password=&q ...
- research plan
- 使用Vitamio插件显示花屏
Vitamio是一款 Android 与 iOS 平台上的全能多媒体开发框架,全面支持硬件解码与 GPU 渲染. 使用vitamio进行播放器的开发非常便捷,使用vitamio的解码,自己编写播放器界 ...
- vim插件ctags的安装和使用【转】
本文转载自:http://blog.csdn.net/g_brightboy/article/details/16830395 [ctags功能]: 为源码的变量/对象.结构体/类.函数/接口.宏等产 ...
- system.web section group下的section
private Configuration _configuration; private ConfigurationSectionGroupCollection sectionGroups; pri ...
- 【POJ 1995】 Raising Modulo Numbers
[题目链接] http://poj.org/problem?id=1995 [算法] 快速幂 [代码] #include <algorithm> #include <bitset&g ...
- PCB Genesis 外形加内角孔实现方法
在PCB工程制作CAM时,经常会遇到外形拐角处直角的,而客户对内角是要求,比如最大内角要求R0.5mm或者不接受内角, 但成型方式为铣方式,又不是啤板成型,那怎么处理才可以达到要求效果呢,在这里介绍2 ...
- PCB 录屏工具Screen2Exe GifCam ScreenToGif
我们完成的软件作品后,需要向客户或领导演示软件功能介绍,这里力推3款录屏工具 一.Screen2Exe工具,录制exe视频文件 下载地址 http://pcbren.cn/ShareFiles/Sc ...
- codevs1040统计单词个数(区间+划分型dp)
1040 统计单词个数 2001年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 给出一个长度不超 ...