最近一个android小程序需要登录功能,我简单实现了一下。现在记录下来也当做个笔记,同时也希望可以相互学习。所以,如果我的代码有问题,还各位请提出来。多谢了!
下面,就简述一下此实例的主要内容:
输入用户名和密码 ,从本地文件userinfo.json中读取users。判断此用户名是否在users中,如果不在则加入users,每次退出Activity都使用AES算法加密users,然后保存到userinfo.json中。用户名下拉菜单是由PopupWindow + ListView 实现。
运行效果图:
主要的代码:
1、用户类User
01.
package
com.example.logindemo;
02.
03.
import
org.json.JSONException;
04.
import
org.json.JSONObject;
05.
import
android.util.Log;
06.
07.
public
class
User {
08.
private
String mId;
09.
private
String mPwd;
10.
private
static
final
String masterPass<A
class
=keylink href=
"http://www.it165.net/edu/ebg/"
target=_blank>word</A> =
"FORYOU"
; // AES加密算法的种子
11.
private
static
final
String JSON_ID =
"user_id"
;
12.
private
static
final
String JSON_PWD =
"user_pwd"
;
13.
private
static
final
String TAG =
"User"
;
14.
15.
public
User(String id, String pwd) {
16.
this
.mId = id;
17.
this
.mPwd = pwd;
18.
}
19.
20.
public
User(JSONObject json)
throws
Exception {
21.
if
(json.has(JSON_ID)) {
22.
String id = json.getString(JSON_ID);
23.
String pwd = json.getString(JSON_PWD);
24.
// 解密后存放
25.
mId = AESUtils.decrypt(masterPass<A
class
=keylink href=
"http://www.it165.net/edu/ebg/"
target=_blank>word</A>, id);
26.
mPwd = AESUtils.decrypt(masterPassword, pwd);
27.
}
28.
}
29.
30.
public
JSONObject toJSON()
throws
Exception {
31.
// 使用AES加密算法加密后保存
32.
String id = AESUtils.encrypt(masterPassword, mId);
33.
String pwd = AESUtils.encrypt(masterPassword, mPwd);
34.
Log.i(TAG,
"加密后:"
+ id +
" "
+ pwd);
35.
JSONObject json =
new
JSONObject();
36.
try
{
37.
json.put(JSON_ID, id);
38.
json.put(JSON_PWD, pwd);
39.
}
catch
(JSONException e) {
40.
e.printStackTrace();
41.
}
42.
return
json;
43.
}
44.
45.
public
String getId() {
46.
return
mId;
47.
}
48.
49.
public
String getPwd() {
50.
return
mPwd;
51.
}
52.
}
2、保存和加载本地User列表
01.
package
com.example.logindemo;
02.
03.
import
java.io.BufferedReader;
04.
import
java.io.FileInputStream;
05.
import
java.io.FileNotFoundException;
06.
import
java.io.IOException;
07.
import
java.io.InputStreamReader;
08.
import
java.io.OutputStream;
09.
import
java.io.OutputStreamWriter;
10.
import
java.io.Writer;
11.
import
java.util.ArrayList;
12.
import
org.json.JSONArray;
13.
import
org.json.JSONException;
14.
import
org.json.JSONTokener;
15.
16.
import
android.content.Context;
17.
import
android.util.Log;
18.
19.
public
class
Utils {
20.
21.
private
static
final
String FILENAME =
"userinfo.json"
;
// 用户保存文件名
22.
private
static
final
String TAG =
"Utils"
;
23.
24.
/* 保存用户登录信息列表 */
25.
public
static
void
saveUserList(Context context, ArrayList<User> users)
26.
throws
Exception {
27.
/* 保存 */
28.
Log.i(TAG,
"正在保存"
);
29.
Writer writer =
null
;
30.
OutputStream out =
null
;
31.
JSONArray array =
new
JSONArray();
32.
for
(User user : users) {
33.
array.put(user.toJSON());
34.
}
35.
try
{
36.
out = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
// 覆盖
37.
writer =
new
OutputStreamWriter(out);
38.
Log.i(TAG,
"json的值:"
+ array.toString());
39.
writer.write(array.toString());
40.
}
finally
{
41.
if
(writer !=
null
)
42.
writer.close();
43.
}
44.
45.
}
46.
47.
/* 获取用户登录信息列表 */
48.
public
static
ArrayList<User> getUserList(Context context) {
49.
/* 加载 */
50.
FileInputStream in =
null
;
51.
ArrayList<User> users =
new
ArrayList<User>();
52.
try
{
53.
54.
in = context.openFileInput(FILENAME);
55.
BufferedReader reader =
new
BufferedReader(
56.
new
InputStreamReader(in));
57.
StringBuilder jsonString =
new
StringBuilder();
58.
JSONArray jsonArray =
new
JSONArray();
59.
String line;
60.
while
((line = reader.readLine()) !=
null
) {
61.
jsonString.append(line);
62.
}
63.
Log.i(TAG, jsonString.toString());
64.
jsonArray = (JSONArray)
new
JSONTokener(jsonString.toString())
65.
.nextValue();
// 把字符串转换成JSONArray对象
66.
for
(
int
i =
0
; i < jsonArray.length(); i++) {
67.
User user =
new
User(jsonArray.getJSONObject(i));
68.
users.add(user);
69.
}
70.
71.
}
catch
(FileNotFoundException e) {
72.
e.printStackTrace();
73.
}
catch
(IOException e) {
74.
e.printStackTrace();
75.
}
catch
(JSONException e) {
76.
e.printStackTrace();
77.
}
catch
(Exception e) {
78.
e.printStackTrace();
79.
}
80.
81.
return
users;
82.
}
83.
}
3、AES加密/解密
01.
package
com.example.logindemo;
02.
03.
04.
import
java.security.SecureRandom;
05.
06.
import
javax.crypto.Cipher;
07.
import
javax.crypto.KeyGenerator;
08.
import
javax.crypto.SecretKey;
09.
import
javax.crypto.spec.IvParameterSpec;
10.
import
javax.crypto.spec.SecretKeySpec;
11.
12.
public
class
AESUtils {
13.
public
static
String encrypt(String seed, String cleartext)
14.
throws
Exception {
15.
byte
[] rawKey = getRawKey(seed.getBytes());
16.
byte
[] result = encrypt(rawKey, cleartext.getBytes());
17.
return
toHex(result);
18.
}
19.
20.
public
static
String decrypt(String seed, String encrypted)
21.
throws
Exception {
22.
byte
[] rawKey = getRawKey(seed.getBytes());
23.
byte
[] enc = toByte(encrypted);
24.
byte
[] result = decrypt(rawKey, enc);
25.
return
new
String(result);
26.
}
27.
28.
private
static
byte
[] getRawKey(
byte
[] seed)
throws
Exception {
29.
KeyGenerator kgen = KeyGenerator.getInstance(
"AES"
);
30.
SecureRandom sr = SecureRandom.getInstance(
"SHA1PRNG"
,
"Crypto"
);
31.
sr.setSeed(seed);
32.
kgen.init(
128
, sr);
33.
SecretKey skey = kgen.generateKey();
34.
byte
[] raw = skey.getEncoded();
35.
return
raw;
36.
}
37.
38.
private
static
byte
[] encrypt(
byte
[] raw,
byte
[] clear)
throws
Exception {
39.
SecretKeySpec skeySpec =
new
SecretKeySpec(raw,
"AES"
);
40.
Cipher cipher = Cipher.getInstance(
"AES"
);
41.
cipher.init(Cipher.ENCRYPT_MODE, skeySpec,
new
IvParameterSpec(
42.
new
byte
[cipher.getBlockSize()]));
43.
byte
[] encrypted = cipher.doFinal(clear);
44.
return
encrypted;
45.
}
46.
47.
private
static
byte
[] decrypt(
byte
[] raw,
byte
[] encrypted)
48.
throws
Exception {
49.
SecretKeySpec skeySpec =
new
SecretKeySpec(raw,
"AES"
);
50.
Cipher cipher = Cipher.getInstance(
"AES"
);
51.
cipher.init(Cipher.DECRYPT_MODE, skeySpec,
new
IvParameterSpec(
52.
new
byte
[cipher.getBlockSize()]));
53.
byte
[] decrypted = cipher.doFinal(encrypted);
54.
return
decrypted;
55.
}
56.
57.
private
static
String toHex(String txt) {
58.
return
toHex(txt.getBytes());
59.
}
60.
61.
private
static
String fromHex(String hex) {
62.
return
new
String(toByte(hex));
63.
}
64.
65.
private
static
byte
[] toByte(String hexString) {
66.
int
len = hexString.length() /
2
;
67.
byte
[] result =
new
byte
[len];
68.
for
(
int
i =
0
; i < len; i++)
69.
result[i] = Integer.valueOf(hexString.substring(
2
* i,
2
* i +
2
),
70.
16
).byteValue();
71.
return
result;
72.
}
73.
74.
private
static
String toHex(
byte
[] buf) {
75.
if
(buf ==
null
)
76.
return
""
;
77.
StringBuffer result =
new
StringBuffer(
2
* buf.length);
78.
for
(
int
i =
0
; i < buf.length; i++) {
79.
appendHex(result, buf[i]);
80.
}
81.
return
result.toString();
82.
}
83.
84.
private
final
static
String HEX =
"0123456789ABCDEF"
;
85.
86.
private
static
void
appendHex(StringBuffer sb,
byte
b) {
87.
sb.append(HEX.charAt((b >>
4
) &
0x0f
)).append(HEX.charAt(b &
0x0f
));
88.
}
89.
}
4、LoginActivity.java
001.
package
com.example.logindemo;
002.
003.
import
java.util.ArrayList;
004.
005.
import
android.app.Activity;
006.
import
android.app.Dialog;
007.
import
android.graphics.drawable.ColorDrawable;
008.
import
android.os.Bundle;
009.
import
android.text.Editable;
010.
import
android.text.TextWatcher;
011.
import
android.util.DisplayMetrics;
012.
import
android.util.Log;
013.
import
android.view.View;
014.
import
android.view.ViewGroup;
015.
import
android.view.Window;
016.
import
android.view.WindowManager;
017.
import
android.view.View.OnClickListener;
018.
import
android.view.ViewGroup.LayoutParams;
019.
import
android.view.animation.Animation;
020.
import
android.view.animation.AnimationUtils;
021.
import
android.widget.AdapterView;
022.
import
android.widget.AdapterView.OnItemClickListener;
023.
import
android.widget.ArrayAdapter;
024.
import
android.widget.Button;
025.
import
android.widget.EditText;
026.
import
android.widget.ImageView;
027.
import
android.widget.LinearLayout;
028.
import
android.widget.ListView;
029.
import
android.widget.PopupWindow;
030.
import
android.widget.PopupWindow.OnDismissListener;
031.
import
android.widget.TextView;
032.
import
android.widget.Toast;
033.
034.
public
class
LoginActivity
extends
Activity
implements
OnClickListener,
035.
OnItemClickListener, OnDismissListener {
036.
protected
static
final
String TAG =
"LoginActivity"
;
037.
private
LinearLayout mLoginLinearLayout;
// 登录内容的容器
038.
private
LinearLayout mUserIdLinearLayout;
// 将下拉弹出窗口在此容器下方显示
039.
private
Animation mTranslate;
// 位移动画
040.
private
Dialog mLoginingDlg;
// 显示正在登录的Dialog
041.
private
EditText mIdEditText;
// 登录ID编辑框
042.
private
EditText mPwdEditText;
// 登录密码编辑框
043.
private
ImageView mMoreUser;
// 下拉图标
044.
private
Button mLoginButton;
// 登录按钮
045.
private
ImageView mLoginMoreUserView;
// 弹出下拉弹出窗的按钮
046.
private
String mIdString;
047.
private
String mPwdString;
048.
private
ArrayList<User> mUsers;
// 用户列表
049.
private
ListView mUserIdListView;
// 下拉弹出窗显示的ListView对象
050.
private
MyAapter mAdapter;
// ListView的监听器
051.
private
PopupWindow mPop;
// 下拉弹出窗
052.
053.
@Override
054.
public
void
onCreate(Bundle savedInstanceState) {
055.
super
.onCreate(savedInstanceState);
056.
setContentView(R.layout.activity_login);
057.
initView();
058.
setListener();
059.
mLoginLinearLayout.startAnimation(mTranslate);
// Y轴水平移动
060.
061.
/* 获取已经保存好的用户密码 */
062.
mUsers = Utils.getUserList(LoginActivity.
this
);
063.
064.
if
(mUsers.size() >
0
) {
065.
/* 将列表中的第一个user显示在编辑框 */
066.
mIdEditText.setText(mUsers.get(
0
).getId());
067.
mPwdEditText.setText(mUsers.get(
0
).getPwd());
068.
}
069.
070.
LinearLayout parent = (LinearLayout) getLayoutInflater().inflate(
071.
R.layout.userifo_listview,
null
);
072.
mUserIdListView = (ListView) parent.findViewById(android.R.id.list);
073.
parent.removeView(mUserIdListView);
// 必须脱离父子关系,不然会报错
074.
mUserIdListView.setOnItemClickListener(
this
);
// 设置点击事
075.
mAdapter =
new
MyAapter(mUsers);
076.
mUserIdListView.setAdapter(mAdapter);
077.
078.
}
079.
080.
/* ListView的适配器 */
081.
class
MyAapter
extends
ArrayAdapter<User> {
082.
083.
public
MyAapter(ArrayList<User> users) {
084.
super
(LoginActivity.
this
,
0
, users);
085.
}
086.
087.
public
View getView(
final
int
position, View convertView,
088.
ViewGroup parent) {
089.
if
(convertView ==
null
) {
090.
convertView = getLayoutInflater().inflate(
091.
R.layout.listview_item,
null
);
092.
}
093.
094.
TextView userIdText = (TextView) convertView
095.
.findViewById(R.id.listview_userid);
096.
userIdText.setText(getItem(position).getId());
097.
098.
ImageView deleteUser = (ImageView) convertView
099.
.findViewById(R.id.login_delete_user);
100.
deleteUser.setOnClickListener(
new
OnClickListener() {
101.
// 点击删除deleteUser时,在mUsers中删除选中的元素
102.
@Override
103.
public
void
onClick(View v) {
104.
105.
if
(getItem(position).getId().equals(mIdString)) {
106.
// 如果要删除的用户Id和Id编辑框当前值相等,则清空
107.
mIdString =
""
;
108.
mPwdString =
""
;
109.
mIdEditText.setText(mIdString);
110.
mPwdEditText.setText(mPwdString);
111.
}
112.
mUsers.remove(getItem(position));
113.
mAdapter.notifyDataSetChanged();
// 更新ListView
114.
}
115.
});
116.
return
convertView;
117.
}
118.
119.
}
120.
121.
private
void
setListener() {
122.
mIdEditText.addTextChangedListener(
new
TextWatcher() {
123.
124.
public
void
onTextChanged(CharSequence s,
int
start,
int
before,
125.
int
count) {
126.
mIdString = s.toString();
127.
}
128.
129.
public
void
beforeTextChanged(CharSequence s,
int
start,
int
count,
130.
int
after) {
131.
}
132.
133.
public
void
afterTextChanged(Editable s) {
134.
}
135.
});
136.
mPwdEditText.addTextChangedListener(
new
TextWatcher() {
137.
138.
public
void
onTextChanged(CharSequence s,
int
start,
int
before,
139.
int
count) {
140.
mPwdString = s.toString();
141.
}
142.
143.
public
void
beforeTextChanged(CharSequence s,
int
start,
int
count,
144.
int
after) {
145.
}
146.
147.
public
void
afterTextChanged(Editable s) {
148.
}
149.
});
150.
mLoginButton.setOnClickListener(
this
);
151.
mLoginMoreUserView.setOnClickListener(
this
);
152.
}
153.
154.
private
void
initView() {
155.
mIdEditText = (EditText) findViewById(R.id.login_edtId);
156.
mPwdEditText = (EditText) findViewById(R.id.login_edtPwd);
157.
mMoreUser = (ImageView) findViewById(R.id.login_more_user);
158.
mLoginButton = (Button) findViewById(R.id.login_btnLogin);
159.
mLoginMoreUserView = (ImageView) findViewById(R.id.login_more_user);
160.
mLoginLinearLayout = (LinearLayout) findViewById(R.id.login_linearLayout);
161.
mUserIdLinearLayout = (LinearLayout) findViewById(R.id.userId_LinearLayout);
162.
mTranslate = AnimationUtils.loadAnimation(
this
, R.anim.my_translate);
// 初始化动画对象
163.
initLoginingDlg();
164.
}
165.
166.
public
void
initPop() {
167.
int
width = mUserIdLinearLayout.getWidth() -
4
;
168.
int
height = LayoutParams.WRAP_CONTENT;
169.
mPop =
new
PopupWindow(mUserIdListView, width, height,
true
);
170.
mPop.setOnDismissListener(
this
);
// 设置弹出窗口消失时监听器
171.
172.
// 注意要加这句代码,点击弹出窗口其它区域才会让窗口消失
173.
mPop.setBackgroundDrawable(
new
ColorDrawable(
0xffffffff
));
174.
175.
}
176.
177.
/* 初始化正在登录对话框 */
178.
private
void
initLoginingDlg() {
179.
180.
mLoginingDlg =
new
Dialog(
this
, R.style.loginingDlg);
181.
mLoginingDlg.setContentView(R.layout.logining_dlg);
182.
183.
Window window = mLoginingDlg.getWindow();
184.
WindowManager.LayoutParams params = window.getAttributes();
185.
// 获取和mLoginingDlg关联的当前窗口的属性,从而设置它在屏幕中显示的位置
186.
187.
// 获取屏幕的高宽
188.
DisplayMetrics dm =
new
DisplayMetrics();
189.
getWindowManager().getDefaultDisplay().getMetrics(dm);
190.
int
cxScreen = dm.widthPixels;
191.
int
cyScreen = dm.heightPixels;
192.
193.
int
height = (
int
) getResources().getDimension(
194.
R.dimen.loginingdlg_height);
// 高42dp
195.
int
lrMargin = (
int
) getResources().getDimension(
196.
R.dimen.loginingdlg_lr_margin);
// 左右边沿10dp
197.
int
topMargin = (
int
) getResources().getDimension(
198.
R.dimen.loginingdlg_top_margin);
// 上沿20dp
199.
200.
params.y = (-(cyScreen - height) /
2
) + topMargin;
// -199
201.
/* 对话框默认位置在屏幕中心,所以x,y表示此控件到"屏幕中心"的偏移量 */
202.
203.
params.width = cxScreen;
204.
params.height = height;
205.
// width,height表示mLoginingDlg的实际大小
206.
207.
mLoginingDlg.setCanceledOnTouchOutside(
true
);
// 设置点击Dialog外部任意区域关闭Dialog
208.
}
209.
210.
/* 显示正在登录对话框 */
211.
private
void
showLoginingDlg() {
212.
if
(mLoginingDlg !=
null
)
213.
mLoginingDlg.show();
214.
}
215.
216.
/* 关闭正在登录对话框 */
217.
private
void
closeLoginingDlg() {
218.
if
(mLoginingDlg !=
null
&& mLoginingDlg.isShowing())
219.
mLoginingDlg.dismiss();
220.
}
221.
222.
@Override
223.
public
void
onClick(View v) {
224.
switch
(v.getId()) {
225.
case
R.id.login_btnLogin:
226.
// 启动登录
227.
showLoginingDlg();
// 显示"正在登录"对话框,因为此Demo没有登录到web服务器,所以效果可能看不出.可以结合情况使用
228.
Log.i(TAG, mIdString +
" "
+ mPwdString);
229.
if
(mIdString ==
null
|| mIdString.equals(
""
)) {
// 账号为空时
230.
Toast.makeText(LoginActivity.
this
,
"请输入账号"
, Toast.LENGTH_SHORT)
231.
.show();
232.
}
else
if
(mPwdString ==
null
|| mPwdString.equals(
""
)) {
// 密码为空时
233.
Toast.makeText(LoginActivity.
this
,
"请输入密码"
, Toast.LENGTH_SHORT)
234.
.show();
235.
}
else
{
// 账号和密码都不为空时
236.
boolean
mIsSave =
true
;
237.
try
{
238.
Log.i(TAG,
"保存用户列表"
);
239.
for
(User user : mUsers) {
// 判断本地文档是否有此ID用户
240.
if
(user.getId().equals(mIdString)) {
241.
mIsSave =
false
;
242.
break
;
243.
}
244.
}
245.
if
(mIsSave) {
// 将新用户加入users
246.
User user =
new
User(mIdString, mPwdString);
247.
mUsers.add(user);
248.
}
249.
250.
}
catch
(Exception e) {
251.
e.printStackTrace();
252.
}
253.
closeLoginingDlg();
// 关闭对话框
254.
Toast.makeText(
this
,
"登录成功"
, Toast.LENGTH_SHORT).show();
255.
finish();
256.
}
257.
break
;
258.
case
R.id.login_more_user:
// 当点击下拉栏
259.
if
(mPop ==
null
) {
260.
initPop();
261.
}
262.
if
(!mPop.isShowing() && mUsers.size() >
0
) {
263.
// Log.i(TAG, "切换为角向上图标");
264.
mMoreUser.setImageResource(R.drawable.login_more_down);
// 切换图标
265.
mPop.showAsDropDown(mUserIdLinearLayout,
2
,
1
);
// 显示弹出窗口
266.
}
267.
break
;
268.
default
:
269.
break
;
270.
}
271.
272.
}
273.
274.
@Override
275.
public
void
onItemClick(AdapterView<?> parent, View view,
int
position,
276.
long
id) {
277.
mIdEditText.setText(mUsers.get(position).getId());
278.
mPwdEditText.setText(mUsers.get(position).getPwd());
279.
mPop.dismiss();
280.
}
281.
282.
/* PopupWindow对象dismiss时的事件 */
283.
@Override
284.
public
void
onDismiss() {
285.
// Log.i(TAG, "切换为角向下图标");
286.
mMoreUser.setImageResource(R.drawable.login_more_up);
287.
}
288.
289.
/* 退出此Activity时保存users */
290.
@Override
291.
public
void
onPause() {
292.
super
.onPause();
293.
try
{
294.
Utils.saveUserList(LoginActivity.
this
, mUsers);
295.
}
catch
(Exception e) {
296.
e.printStackTrace();
297.
}
298.
}
299.
300.
}
其他一些布局和资源配置我就不详细列出了,想看的可以下载 http://www.it165.net/uploadfile/files/2014/1031/LoginDemo.zip