Android 实现登录界面和功能实例
最后更新于:2022-04-01 19:47:43
最近一个android小程序需要登录功能,我简单实现了一下。现在记录下来也当做个笔记,同时也希望可以相互学习。所以,如果我的代码有问题,还各位请提出来。多谢了!
下面,就简述一下此实例的主要内容:
输入用户名和密码 ,从本地文件userinfo.json中读取users。判断此用户名是否在users中,如果不在则加入users,每次退出Activity都使用AES算法加密users,然后保存到userinfo.json中。用户名下拉菜单是由PopupWindow + ListView 实现。
运行效果图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396c88575.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396c9d0e2.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396cabe82.jpg)
主要的代码:
1、用户类User
~~~
package com.example.logindemo;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
public class User {
private String mId;
private String mPwd;
private static final String masterPassword = "FORYOU"; // AES加密算法的种子
private static final String JSON_ID = "user_id";
private static final String JSON_PWD = "user_pwd";
private static final String TAG = "User";
public User(String id, String pwd) {
this.mId = id;
this.mPwd = pwd;
}
public User(JSONObject json) throws Exception {
if (json.has(JSON_ID)) {
String id = json.getString(JSON_ID);
String pwd = json.getString(JSON_PWD);
// 解密后存放
mId = AESUtils.decrypt(masterPassword, id);
mPwd = AESUtils.decrypt(masterPassword, pwd);
}
}
public JSONObject toJSON() throws Exception {
// 使用AES加密算法加密后保存
String id = AESUtils.encrypt(masterPassword, mId);
String pwd = AESUtils.encrypt(masterPassword, mPwd);
Log.i(TAG, "加密后:" + id + " " + pwd);
JSONObject json = new JSONObject();
try {
json.put(JSON_ID, id);
json.put(JSON_PWD, pwd);
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
public String getId() {
return mId;
}
public String getPwd() {
return mPwd;
}
}
~~~
2、保存和加载本地User列表
~~~
package com.example.logindemo;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONTokener;
import android.content.Context;
import android.util.Log;
public class Utils {
private static final String FILENAME = "userinfo.json"; // 用户保存文件名
private static final String TAG = "Utils";
/* 保存用户登录信息列表 */
public static void saveUserList(Context context, ArrayList users)
throws Exception {
/* 保存 */
Log.i(TAG, "正在保存");
Writer writer = null;
OutputStream out = null;
JSONArray array = new JSONArray();
for (User user : users) {
array.put(user.toJSON());
}
try {
out = context.openFileOutput(FILENAME, Context.MODE_PRIVATE); // 覆盖
writer = new OutputStreamWriter(out);
Log.i(TAG, "json的值:" + array.toString());
writer.write(array.toString());
} finally {
if (writer != null)
writer.close();
}
}
/* 获取用户登录信息列表 */
public static ArrayList getUserList(Context context) {
/* 加载 */
FileInputStream in = null;
ArrayList users = new ArrayList();
try {
in = context.openFileInput(FILENAME);
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
StringBuilder jsonString = new StringBuilder();
JSONArray jsonArray = new JSONArray();
String line;
while ((line = reader.readLine()) != null) {
jsonString.append(line);
}
Log.i(TAG, jsonString.toString());
jsonArray = (JSONArray) new JSONTokener(jsonString.toString())
.nextValue(); // 把字符串转换成JSONArray对象
for (int i = 0; i < jsonArray.length(); i++) {
User user = new User(jsonArray.getJSONObject(i));
users.add(user);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return users;
}
}
~~~
3、AES加密/解密
~~~
package com.example.logindemo;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESUtils {
public static String encrypt(String seed, String cleartext)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result);
}
public static String decrypt(String seed, String encrypted)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = toByte(encrypted);
byte[] result = decrypt(rawKey, enc);
return new String(result);
}
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(seed);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(
new byte[cipher.getBlockSize()]));
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private static byte[] decrypt(byte[] raw, byte[] encrypted)
throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(
new byte[cipher.getBlockSize()]));
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
private static String toHex(String txt) {
return toHex(txt.getBytes());
}
private static String fromHex(String hex) {
return new String(toByte(hex));
}
private static byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2),
16).byteValue();
return result;
}
private static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}
~~~
4、LoginActivity.java
~~~
package com.example.logindemo;
import java.util.ArrayList;
import android.app.Activity;
import android.app.Dialog;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.PopupWindow.OnDismissListener;
import android.widget.TextView;
import android.widget.Toast;
public class LoginActivity extends Activity implements OnClickListener,
OnItemClickListener, OnDismissListener {
protected static final String TAG = "LoginActivity";
private LinearLayout mLoginLinearLayout; // 登录内容的容器
private LinearLayout mUserIdLinearLayout; // 将下拉弹出窗口在此容器下方显示
private Animation mTranslate; // 位移动画
private Dialog mLoginingDlg; // 显示正在登录的Dialog
private EditText mIdEditText; // 登录ID编辑框
private EditText mPwdEditText; // 登录密码编辑框
private ImageView mMoreUser; // 下拉图标
private Button mLoginButton; // 登录按钮
private ImageView mLoginMoreUserView; // 弹出下拉弹出窗的按钮
private String mIdString;
private String mPwdString;
private ArrayList mUsers; // 用户列表
private ListView mUserIdListView; // 下拉弹出窗显示的ListView对象
private MyAapter mAdapter; // ListView的监听器
private PopupWindow mPop; // 下拉弹出窗
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initView();
setListener();
mLoginLinearLayout.startAnimation(mTranslate); // Y轴水平移动
/* 获取已经保存好的用户密码 */
mUsers = Utils.getUserList(LoginActivity.this);
if (mUsers.size() > 0) {
/* 将列表中的第一个user显示在编辑框 */
mIdEditText.setText(mUsers.get(0).getId());
mPwdEditText.setText(mUsers.get(0).getPwd());
}
LinearLayout parent = (LinearLayout) getLayoutInflater().inflate(
R.layout.userifo_listview, null);
mUserIdListView = (ListView) parent.findViewById(android.R.id.list);
parent.removeView(mUserIdListView); // 必须脱离父子关系,不然会报错
mUserIdListView.setOnItemClickListener(this); // 设置点击事
mAdapter = new MyAapter(mUsers);
mUserIdListView.setAdapter(mAdapter);
}
/* ListView的适配器 */
class MyAapter extends ArrayAdapter {
public MyAapter(ArrayList users) {
super(LoginActivity.this, 0, users);
}
public View getView(final int position, View convertView,
ViewGroup parent) {
if (convertView == null) {
convertView = getLayoutInflater().inflate(
R.layout.listview_item, null);
}
TextView userIdText = (TextView) convertView
.findViewById(R.id.listview_userid);
userIdText.setText(getItem(position).getId());
ImageView deleteUser = (ImageView) convertView
.findViewById(R.id.login_delete_user);
deleteUser.setOnClickListener(new OnClickListener() {
// 点击删除deleteUser时,在mUsers中删除选中的元素
@Override
public void onClick(View v) {
if (getItem(position).getId().equals(mIdString)) {
// 如果要删除的用户Id和Id编辑框当前值相等,则清空
mIdString = "";
mPwdString = "";
mIdEditText.setText(mIdString);
mPwdEditText.setText(mPwdString);
}
mUsers.remove(getItem(position));
mAdapter.notifyDataSetChanged(); // 更新ListView
}
});
return convertView;
}
}
private void setListener() {
mIdEditText.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before,
int count) {
mIdString = s.toString();
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(Editable s) {
}
});
mPwdEditText.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before,
int count) {
mPwdString = s.toString();
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void afterTextChanged(Editable s) {
}
});
mLoginButton.setOnClickListener(this);
mLoginMoreUserView.setOnClickListener(this);
}
private void initView() {
mIdEditText = (EditText) findViewById(R.id.login_edtId);
mPwdEditText = (EditText) findViewById(R.id.login_edtPwd);
mMoreUser = (ImageView) findViewById(R.id.login_more_user);
mLoginButton = (Button) findViewById(R.id.login_btnLogin);
mLoginMoreUserView = (ImageView) findViewById(R.id.login_more_user);
mLoginLinearLayout = (LinearLayout) findViewById(R.id.login_linearLayout);
mUserIdLinearLayout = (LinearLayout) findViewById(R.id.userId_LinearLayout);
mTranslate = AnimationUtils.loadAnimation(this, R.anim.my_translate); // 初始化动画对象
initLoginingDlg();
}
public void initPop() {
int width = mUserIdLinearLayout.getWidth() - 4;
int height = LayoutParams.WRAP_CONTENT;
mPop = new PopupWindow(mUserIdListView, width, height, true);
mPop.setOnDismissListener(this);// 设置弹出窗口消失时监听器
// 注意要加这句代码,点击弹出窗口其它区域才会让窗口消失
mPop.setBackgroundDrawable(new ColorDrawable(0xffffffff));
}
/* 初始化正在登录对话框 */
private void initLoginingDlg() {
mLoginingDlg = new Dialog(this, R.style.loginingDlg);
mLoginingDlg.setContentView(R.layout.logining_dlg);
Window window = mLoginingDlg.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
// 获取和mLoginingDlg关联的当前窗口的属性,从而设置它在屏幕中显示的位置
// 获取屏幕的高宽
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int cxScreen = dm.widthPixels;
int cyScreen = dm.heightPixels;
int height = (int) getResources().getDimension(
R.dimen.loginingdlg_height);// 高42dp
int lrMargin = (int) getResources().getDimension(
R.dimen.loginingdlg_lr_margin); // 左右边沿10dp
int topMargin = (int) getResources().getDimension(
R.dimen.loginingdlg_top_margin); // 上沿20dp
params.y = (-(cyScreen - height) / 2) + topMargin; // -199
/* 对话框默认位置在屏幕中心,所以x,y表示此控件到"屏幕中心"的偏移量 */
params.width = cxScreen;
params.height = height;
// width,height表示mLoginingDlg的实际大小
mLoginingDlg.setCanceledOnTouchOutside(true); // 设置点击Dialog外部任意区域关闭Dialog
}
/* 显示正在登录对话框 */
private void showLoginingDlg() {
if (mLoginingDlg != null)
mLoginingDlg.show();
}
/* 关闭正在登录对话框 */
private void closeLoginingDlg() {
if (mLoginingDlg != null && mLoginingDlg.isShowing())
mLoginingDlg.dismiss();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.login_btnLogin:
// 启动登录
showLoginingDlg(); // 显示"正在登录"对话框,因为此Demo没有登录到web服务器,所以效果可能看不出.可以结合情况使用
Log.i(TAG, mIdString + " " + mPwdString);
if (mIdString == null || mIdString.equals("")) { // 账号为空时
Toast.makeText(LoginActivity.this, "请输入账号", Toast.LENGTH_SHORT)
.show();
} else if (mPwdString == null || mPwdString.equals("")) {// 密码为空时
Toast.makeText(LoginActivity.this, "请输入密码", Toast.LENGTH_SHORT)
.show();
} else {// 账号和密码都不为空时
boolean mIsSave = true;
try {
Log.i(TAG, "保存用户列表");
for (User user : mUsers) { // 判断本地文档是否有此ID用户
if (user.getId().equals(mIdString)) {
mIsSave = false;
break;
}
}
if (mIsSave) { // 将新用户加入users
User user = new User(mIdString, mPwdString);
mUsers.add(user);
}
} catch (Exception e) {
e.printStackTrace();
}
closeLoginingDlg();// 关闭对话框
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
finish();
}
break;
case R.id.login_more_user: // 当点击下拉栏
if (mPop == null) {
initPop();
}
if (!mPop.isShowing() && mUsers.size() > 0) {
// Log.i(TAG, "切换为角向上图标");
mMoreUser.setImageResource(R.drawable.login_more_down); // 切换图标
mPop.showAsDropDown(mUserIdLinearLayout, 2, 1); // 显示弹出窗口
}
break;
default:
break;
}
}
@Override
public void onItemClick(AdapterView> parent, View view, int position,
long id) {
mIdEditText.setText(mUsers.get(position).getId());
mPwdEditText.setText(mUsers.get(position).getPwd());
mPop.dismiss();
}
/* PopupWindow对象dismiss时的事件 */
@Override
public void onDismiss() {
// Log.i(TAG, "切换为角向下图标");
mMoreUser.setImageResource(R.drawable.login_more_up);
}
/* 退出此Activity时保存users */
@Override
public void onPause() {
super.onPause();
try {
Utils.saveUserList(LoginActivity.this, mUsers);
} catch (Exception e) {
e.printStackTrace();
}
}
}
~~~
其他一些布局和资源配置我就不详细列出了,想看的可以下载 [源码](http://download.csdn.net/detail/u012964281/8102005)
';
Android平台 AES加密/解密
最后更新于:2022-04-01 19:47:40
网上有些代码在运行解密时抛出错误:javax.crypto.BadPaddingException: pad block corrupted
以下代码亲测运行无误:
~~~
package com.example.testandroid;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
public static String encrypt(String seed, String cleartext)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result);
}
public static String decrypt(String seed, String encrypted)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] enc = toByte(encrypted);
byte[] result = decrypt(rawKey, enc);
return new String(result);
}
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(seed);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(
new byte[cipher.getBlockSize()]));
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private static byte[] decrypt(byte[] raw, byte[] encrypted)
throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(
new byte[cipher.getBlockSize()]));
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
private static String toHex(String txt) {
return toHex(txt.getBytes());
}
private static String fromHex(String hex) {
return new String(toByte(hex));
}
private static byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2),
16).byteValue();
return result;
}
private static String toHex(byte[] buf) {
if (buf == null)
return "";
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
appendHex(result, buf[i]);
}
return result.toString();
}
private final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String masterPassword = "key";
String originalText = "0123456789";
try {
String encryptingCode = MainActivity.encrypt(masterPassword,
originalText);
Log.e(TAG, "加密后: " + encryptingCode);
String decryptingCode = MainActivity.decrypt(masterPassword,
encryptingCode);
Log.e(TAG, "解密后: " + decryptingCode);
} catch (Exception e) {
e.printStackTrace();
}
}
}
~~~
';
Android Base64 编码/解码
最后更新于:2022-04-01 19:47:37
### Base64编码说明
Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。 如果剩下的字符不足3个字节,则用0填充,输出字符使用'=',因此编码后输出的文本末尾可能会出现1或2个'='。
为了保证所输出的编码位可读字符,Base64制定了一个编码表,以便进行统一转换。编码表的大小为2^6=64,这也是Base64名称的由来。
### Base64编码表
**用法:**
~~~
String token = "hello world"; // 编码前
String base64Token = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);// 编码后
byte[] m = Base64.decode(base64Token,Base64.DEFAULT);// 解码后
~~~
其中第二个参数:
Base64.flag
-
CRLF 这个参数看起来比较眼熟,它就是Win风格的换行符,意思就是使用CR LF这一对作为一行的结尾而不是Unix风格的LF
-
DEFAULT 这个参数是默认,使用默认的方法来加密
-
NO_PADDING 这个参数是略去加密字符串最后的”=”
-
NO_WRAP 这个参数意思是略去所有的换行符(设置后CRLF就没用了)
-
URL_SAFE 这个参数意思是加密时不使用对URL和文件名有特殊意义的字符来作为加密字符,具体就是以-和_取代+和/
';
码值 | 字符 | 码值 | 字符 | 码值 | 字符 | 码值 | 字符 | |||
---|---|---|---|---|---|---|---|---|---|---|
0 | A | 16 | Q | 32 | g | 48 | w | |||
1 | B | 17 | R | 33 | h | 49 | x | |||
2 | C | 18 | S | 34 | i | 50 | y | |||
3 | D | 19 | T | 35 | j | 51 | z | |||
4 | E | 20 | U | 36 | k | 52 | 0 | |||
5 | F | 21 | V | 37 | l | 53 | 1 | |||
6 | G | 22 | W | 38 | m | 54 | 2 | |||
7 | H | 23 | X | 39 | n | 55 | 3 | |||
8 | I | 24 | Y | 40 | o | 56 | 4 | |||
9 | J | 25 | Z | 41 | p | 57 | 5 | |||
10 | K | 26 | a | 42 | q | 58 | 6 | |||
11 | L | 27 | b | 43 | r | 59 | 7 | |||
12 | M | 28 | c | 44 | s | 60 | 8 | |||
13 | N | 29 | d | 45 | t | 61 | 9 | |||
14 | O | 30 | e | 46 | u | 62 | + | |||
15 | P | 31 | f | 47 | v | 63 | / |
android socket 编程实例
最后更新于:2022-04-01 19:47:34
[转载来源](http://blog.csdn.net/wuchuanpingstone/article/details/6617276#comments)
android客户端通过socket与服务器进行通信可以分为以下几步:
应用程序与服务器通信可以采用两种模式:TCP可靠通信 和UDP不可靠通信。
(1)通过IP地址和端口实例化Socket,请求连接服务器:
socket = new Socket(HOST, PORT); //host:为服务器的IP地址 port:为服务器的端口号
(2)获取Socket流以进行读写,并把流包装进BufferWriter或者PrintWriter:
PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())),true);
这里涉及了三个类:socket.getOutputStream得到socket的输出字节流,OutputStreamWriter是字节流向字符流转换的桥梁,BufferWriter是字符流,然后再包装进PrintWriter。
(3)对Socket进行读写
~~~
if (socket.isConnected()) {
if (!socket.isOutputShutdown()) {
out.println(msg);
}
}
~~~
(4)关闭打开的流
` out.close();`
在写代码的过程中一定要注意对socket 输入流 输出流的关闭
下面是一个简单的例子:
main.xml
~~~
~~~
下面是android客户端的源代码:
~~~
package com.example.socketdemo;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@SuppressLint("NewApi")
public class SocketDemo extends Activity implements Runnable {
private TextView tv_msg = null;
private EditText ed_msg = null;
private Button btn_send = null;
// private Button btn_login = null;
private static final String HOST = "192.168.1.223";
private static final int PORT = 9999;
private Socket socket = null;
private BufferedReader in = null;
private PrintWriter out = null;
private String content = "";
/**Called when the activity is first created. */
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@SuppressLint("NewApi")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_socket_demo);
////Android 2.3及以上调用严苛模式
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
.permitAll().build();
StrictMode.setThreadPolicy(policy);
}
tv_msg = (TextView) findViewById(R.id.TextView);
ed_msg = (EditText) findViewById(R.id.EditText01);
// btn_login = (Button) findViewById(R.id.Button01);
btn_send = (Button) findViewById(R.id.Button02);
try {
socket = new Socket(HOST, PORT);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream())), true);
} catch (IOException ex) {
ex.printStackTrace();
ShowDialog("login exception" + ex.getMessage());
}
btn_send.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
String msg = ed_msg.getText().toString();
if (socket.isConnected()) {
if (!socket.isOutputShutdown()) {
out.println(msg);
}
}
}
});
new Thread(SocketDemo.this).start();
}
public void ShowDialog(String msg) {
new AlertDialog.Builder(this).setTitle("notification").setMessage(msg)
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
}).show();
}
public void run() {
try {
while (true) {
if (socket.isConnected()) {
if (!socket.isInputShutdown()) {
if ((content = in.readLine()) != null) {
content += "\n";
mHandler.sendMessage(mHandler.obtainMessage());
} else {
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
tv_msg.setText(tv_msg.getText().toString() + content);
}
};
}
~~~
下面是服务器端的java代码:
~~~
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
private static final int PORT = 9999;
private List mList = new ArrayList();
private ServerSocket server = null;
private ExecutorService mExecutorService = null; //thread pool
public static void main(String[] args) {
new Main();
}
public Main() {
try {
server = new ServerSocket(PORT);
mExecutorService = Executors.newCachedThreadPool(); //create a thread pool
System.out.print("server start ...");
Socket client = null;
while(true) {
client = server.accept();
mList.add(client);
mExecutorService.execute(new Service(client)); //start a new thread to handle the connection
}
}catch (Exception e) {
e.printStackTrace();
}
}
class Service implements Runnable {
private Socket socket;
private BufferedReader in = null;
private String msg = "";
public Service(Socket socket) {
this.socket = socket;
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
msg = "user" +this.socket.getInetAddress() + "come toal:"
+mList.size();
this.sendmsg();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
while(true) {
if((msg = in.readLine())!= null) {
if(msg.equals("exit")) {
System.out.println("ssssssss");
mList.remove(socket);
in.close();
msg = "user:" + socket.getInetAddress()
+ "exit total:" + mList.size();
socket.close();
this.sendmsg();
break;
} else {
msg = socket.getInetAddress() + ":" + msg;
this.sendmsg();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void sendmsg() {
System.out.println(msg);
int num =mList.size();
for (int index = 0; index < num; index ++) {
Socket mSocket = mList.get(index);
PrintWriter pout = null;
try {
pout = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(mSocket.getOutputStream())),true);
pout.println(msg);
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
~~~
注意在AndroidManifest.xml中加入对网络的访问权限
在写代码的过程中一定要注意对套接字和输入/输出流的关闭
';
Android存储和加载本地文件(外部存储设备)
最后更新于:2022-04-01 19:47:32
有时候应用需要将数据写入到设备的外部存储上。列如,需要同其他应用或用户共享音乐、图片或者网络下载资料时,保存在外部设备的数据共享起来要比较方便。而且,外部设备通常具有更大的存储空间。
我们可以通过android.os.Environment.getExternalStorageDirectory()方法获取sdCard的路径。再在此路径下创建一个MyFiles的文件,将数据保存在MyFiles文件夹下。
下面就展示如何在外部存储设备中存储和加载本地文件:
1、创建一个名为 DataStorage的工程
2、准备好布局文件(activity_data_storage.xml)
~~~
~~~
3、在AndroidManifest.xml中加入往sdCard写入文件的权限
4、DataStorageActivity.java
~~~
package com.example.datastorage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class DataStorageActivity extends ActionBarActivity {
private static final String FILENAME = "data.txt";
private static final String TAG = "DataStorageActivity";
private TextView dataView;
private Button saveButton;
private Button loadButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_date_storage);
dataView = (TextView) findViewById(R.id.data_view);
saveButton = (Button) findViewById(R.id.save_button);
loadButton = (Button) findViewById(R.id.load_button);
setListener();
}
private void setListener() {
saveButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
saveData();
} catch (IOException e) {
}
Toast.makeText(DataStorageActivity.this, "保存成功",
Toast.LENGTH_SHORT).show();
}
});
loadButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
loadData();
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
});
}
public void saveData() throws IOException {
File sdCard = Environment.getExternalStorageDirectory();
// 获取外部存储设备(SD卡)的路径
Log.i(TAG, sdCard.getAbsolutePath());
// 查看LogCat,获取的sd卡的绝对路径为 /storage/sdcard
sdCard = new File(sdCard, "/MyFiles");
sdCard.mkdirs();// 创建MyFiles目录(可创建多级目录)
sdCard = new File(sdCard, FILENAME);
FileOutputStream out = new FileOutputStream(sdCard);
Writer writer = new OutputStreamWriter(out);
try {
String str = "来自保存在内部存储设备的数据";
writer.write(str);
} finally {
writer.close();
}
}
public void loadData() throws FileNotFoundException, IOException {
BufferedReader reader = null;
StringBuilder data = new StringBuilder();
try {
File sdCard = Environment.getExternalStorageDirectory();
sdCard = new File(sdCard, "/MyFiles/" + FILENAME);
FileInputStream in = new FileInputStream(sdCard);
reader = new BufferedReader(new InputStreamReader(in));
String line = new String();
while ((line = reader.readLine()) != null) {
data.append(line);
}
dataView.setText(data);
} catch (FileNotFoundException e) {
dataView.setText("没有发现保存的数据");
} finally {
reader.close();
}
}
}
~~~
运行程序,点击保存数据按钮,Toast显示保存成功字样后。再点击加载数据按钮:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396c45d11.jpg)
可以发现保存在内部存储设备的设备被加载后在TextView显示。再看文件具体位置:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396c6aae4.jpg)
`#DONE#`
';
Android存储和加载本地文件(内部存储设备)
最后更新于:2022-04-01 19:47:29
Android设备上的所有应用都有一个放置在沙盘中的文件目录,将文件保存到沙盒中可以阻止其他应用的访问。
沙盒目录的全路径为:/data/data/<包名> 用File Explorer查看:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396c35105.jpg)
如上图可见,每个应用都在/data/data下有一个以此应用包名命名的文件目录。
而本文就是介绍将文件保存在/data/data/<包名>/files/ 目录下
下面就展示如何在内部存储设备中存储和加载本地文件:
1、创建一个名为 DataStorage的工程
2、准备好布局文件(activity_data_storage.xml)
~~~
~~~
3、DataStorageActivity.java
~~~
package com.example.datastorage;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class DataStorageActivity extends ActionBarActivity {
private static final String FILENAME = "data.txt";
private static final String TAG = "DataStorageActivity";
private TextView dataView;
private Button saveButton;
private Button loadButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_date_storage);
dataView = (TextView) findViewById(R.id.data_view);
saveButton = (Button) findViewById(R.id.save_button);
loadButton = (Button) findViewById(R.id.load_button);
setListener();
}
private void setListener() {
saveButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
saveData();
} catch (IOException e) {
}
Toast.makeText(DataStorageActivity.this, "保存成功",
Toast.LENGTH_SHORT).show();
}
});
loadButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
loadData();
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
});
}
public void saveData() throws IOException {
OutputStream out = this.openFileOutput(FILENAME, Context.MODE_PRIVATE);
/* 参数一: 文件名。
* 如果文件不存在,Android会自动创建它。创建的文件保存在/data/data//files目录下
* 参数二: 文件操作模式参数。代表该文件是私有数据,只能被应用本身访问。
* */
Writer writer = new OutputStreamWriter(out);
try {
String str = "来自保存在内部存储设备的数据";
writer.write(str);
} finally {
writer.close();
}
}
public void loadData() throws FileNotFoundException, IOException {
BufferedReader reader = null;
StringBuilder data = new StringBuilder();
try {
InputStream in = this.openFileInput(FILENAME);
Log.i(TAG, in.toString());
reader = new BufferedReader(new InputStreamReader(in));
String line = new String();
while ((line = reader.readLine()) != null) {
data.append(line);
}
dataView.setText(data);
} catch (FileNotFoundException e) {
dataView.setText("没有发现保存的数据");
} finally {
reader.close();
}
}
}
~~~
运行程序,点击保存数据按钮,Toast显示保存成功字样后。再点击加载数据按钮:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396c45d11.jpg)
可以发现保存在内部存储设备的设备被加载后在TextView显示。再看文件具体位置:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396c5532c.jpg)
`#DONE#`
';
Android VideoView实现视频播放
最后更新于:2022-04-01 19:47:26
xml文件
~~~
~~~
java文件
~~~
package com.example.playvideo;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.MediaController;
import android.widget.VideoView;
public class PlayVideo extends ActionBarActivity {
private VideoView videoView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_video);
videoView = (VideoView) findViewById(R.id.video_view);
MediaController mc = new MediaController(this);
videoView.setMediaController(mc);
//videoView.setVideoPath("/sdcard/forTest.mp4");//sdcard上的视频
videoView.setVideoPath("android.resource://com.example.showvideo/"+ R.raw.test);
videoView.requestFocus();
videoView.start();
}
}
~~~
运行效果如下图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396be8f37.jpg)
备注:
遇到过一个叫Error (1,-2147483648)的错误,程序直接崩溃:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396c0d9bc.jpg)
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396c23870.jpg)
我查了一下,原来它代表 未知错误
真正的原因在于:
MP4有多种编码格式,例如H.264,H.263等,而Android版本较低的机器只支持部分编码。
一旦遭遇不被支持的编码格式,MediaPlayer可能就会抛出上面的错误信息。
于是,我把被测试的mpg格式的视频换成mp4格式,结果就OK了。
';
Android ListFragment
最后更新于:2022-04-01 19:47:24
Android是在Android 3.0(API level 11)开始引入Fragment的(为了兼容较低版本的设备使用支持库类)。可以把Fragment看成Activity中的模块,这个模块有自己的布局,有自己的生命周期(由托管activity调用其周期方法),单独处理自己的输入,在Activity运行的时候可以加载或者移除Fragment模块。
ListFragment是Fragment的子类,内置列表显示支持功能。ListFragment通过内置的ListView显示绑定在其上面的数据。下面就通过例子了解具体的用法:
1、模型层 Day类和DayLab类
通过DayLab对象的getDays()方法获取用来绑定的数据。
Day.java
~~~
package com.example.showdays;
import java.util.ArrayList;
public class Day {
private String mTitle;
public String getTitle() {
return mTitle;
}
public void setTitle(String mTitle) {
this.mTitle = mTitle;
}
}
~~~
DayLab.java
~~~
package com.example.showdays;
import java.util.ArrayList;
public class DayLab {
private ArrayList mDays;
public DayLab() {
mDays = new ArrayList();
for (int i = 1; i <= 10; i++) {
Day day = new Day();
day.setTitle("Title #" + i);
mDays.add(day);
}
}
public ArrayList getDays() {
return mDays;
}
}
~~~
2、创建DayListFragment类
继承ListFragment类。并给其内置的ListView设置Adapter
DayListFragment.java
~~~
package com.example.showdays;
import java.util.ArrayList;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.widget.ArrayAdapter;
public class DayListFragment extends ListFragment {
private ArrayList days;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DayLab dayLab = new DayLab();
this.days = dayLab.getDays();
ArrayAdapter adapter = new ArrayAdapter(getActivity(),
android.R.layout.simple_list_item_1, days);
// 第一个参数: Context对象,使用第二个参数的资源ID需要该Context对象
// 第二个参数: 资源ID,可定位ArrayAdapter用来创建View对象的布局,这里的实参是 Android SDK提供的预定义布局资源
// 第三个参数: 数据集
setListAdapter(adapter);// 给DayListFragment内置的ListView设置adapter
}
}
~~~
3、创建DayActivity类
继承FragmentActivity类。托管DayListFragment对象
其布局如下:
activity_day.xml
~~~
~~~
在类中引用此布局文件,并将DayListFragment对象放置在id为fragmentContainer的FrameLayout容器视图中。
DayActivity.java
~~~
package com.example.showdays;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
public class DayActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_day);
FragmentManager fm = getSupportFragmentManager();
// 继承支持库类FragmentActivity获取FragmentManager对象的方法
// 若继承Activity使用getFragmentManager()
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
if (fragment == null) { // 查看fragment事务队列中是否存在此事物
fragment = new DayListFragment();
fm.beginTransaction().add(R.id.fragmentContainer, fragment)
.commit();
// 提交事务(资源ID作为唯一标识符且通知视图位置)
}
}
}
~~~
至此程序就可以正常运行了,运行效果如下图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396bafd18.jpg)
可以看到,已经呈现出列表形式,但是要显示的内容却不是我想要的。
那是因为 Android SDK提供的预定义布局资源(android.R.layout.simple_list_item_1)布局如下:
~~~
~~~
这样在ListView被实例化后与adapter会话时,ArrayAdapter.getView()方法会调用Day对象的toString()方法,然后将返回值传递给TextView。
所以覆盖Day对象toString()方法:
Day.java
~~~
package com.example.showdays;
import java.util.ArrayList;
public class Day {
private String mTitle;
public String getTitle() {
return mTitle;
}
public void setTitle(String mTitle) {
this.mTitle = mTitle;
}
@Override
public String toString() {
return mTitle;
}
}
~~~
此时再运行程序,运行效果如下图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396bc39f2.jpg)
4、自定义列表项
上面使用的是Android SDK提供的预定义布局资源,直接把数据放置在TextView组件中来显示。
下面我就自定义一个列表项布局,将要显示的信息用Button组件来显示,布局如下:
my_list_item.xml
~~~
~~~
因为ListView更新视图的时候都要调用ArrayAdapter.getView()方法来获取View。所以要想把自己定义的列表项加入到ListView中,可以重写getView()方法,覆盖它并返回一个自己定义的View。做出改变如下:
DayListFragment.java
~~~
package com.example.showdays;
import java.util.ArrayList;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
public class DayListFragment extends ListFragment {
private ArrayList days;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DayLab dayLab = new DayLab();
this.days = dayLab.getDays();
DayAdapter adapter = new DayAdapter(days);
setListAdapter(adapter);
}
private class DayAdapter extends ArrayAdapter {
public DayAdapter(ArrayList days) {
super(getActivity(), 0, days);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) { // 此列表项是否存在,不存在则创建一个
convertView = getActivity().getLayoutInflater().inflate(
R.layout.my_list_item, null);
}
Day day = getItem(position); // 获取当前位置的Day对象
Button button = (Button) convertView.findViewById(R.id.button);
button.setText(day.toString());
return convertView;
}
}
}
~~~
最后运行一下,运行效果如下图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396bd70f4.jpg)
`#DONE#`
';
Android Fragment 基本介绍
最后更新于:2022-04-01 19:47:20
[转载来源](http://www.cnblogs.com/mengdd/archive/2013/01/08/2851368.html)
### Fragment
Android是在Android 3.0 (API level 11)开始引入Fragment的。
可以把Fragment想成Activity中的模块,这个模块有自己的布局,有自己的生命周期,单独处理自己的输入,在Activity运行的时候可以加载或者移除Fragment模块。
可以把Fragment设计成可以在多个Activity中复用的模块。
当开发的应用程序同时适用于平板电脑和手机时,可以利用Fragment实现灵活的布局,改善用户体验。
如图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396b29d4d.png)
### Fragment的生命周期
因为Fragment必须嵌入在Acitivity中使用,所以Fragment的生命周期和它所在的Activity是密切相关的。
如果Activity是暂停状态,其中所有的Fragment都是暂停状态;如果Activity是stopped状态,这个Activity中所有的Fragment都不能被启动;如果Activity被销毁,那么它其中的所有Fragment都会被销毁。
但是,当Activity在活动状态,可以独立控制Fragment的状态,比如加上或者移除Fragment。
当这样进行fragment transaction(转换)的时候,可以把fragment放入Activity的**back stack**中,这样用户就可以进行返回操作。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396b3fc97.png)
### Fragment的使用相关
使用Fragment时,需要继承Fragment或者Fragment的子类(DialogFragment, ListFragment, PreferenceFragment, WebViewFragment),所以Fragment的代码看起来和Activity的类似。
**使用Support Library**
Support Library是一个提供了API库函数的JAR文件,这样就可以在旧版本的Android上使用一些新版本的APIs。
比如android-support-v4.jar.它的完整路径是:
/extras/android/support/v4/android-support-v4.jar.
它就提供了Fragment的APIs,使得在Android 1.6 (API level 4)以上的系统都可以使用Fragment。
为了确定没有在旧版本系统上使用新版本的APIs,需要如下导入语句:
~~~
import android.support.v4.app.Fragment;
~~~
~~~
import android.support.v4.app.FragmentManager;
~~~
同时应该将上述的包拷入libs项目下的libs文件夹,然后在项目的Properties中添加:右键单击项目,选Properties,左边选Java Build Path,然后Add External JARs…,添加android-support-v4.jar.
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396b5f3df.png)
**当创建包含Fragment的Activity时,如果用的是Support Library,那么继承的就应该是FragmentActivity而不是Activity。**
**必须实现的三个回调函数**
onCreate()
系统在创建Fragment的时候调用这个方法,这里应该初始化相关的组件,一些即便是被暂停或者被停止时依然需要保留的东西。
onCreateView()
当第一次绘制Fragment的UI时系统调用这个方法,必须返回一个View,如果Fragment不提供UI也可以返回null。
注意,如果继承自ListFragment,onCreateView()默认的实现会返回一个ListView,所以不用自己实现。
onPause()
当用户离开Fragment时第一个调用这个方法,需要提交一些变化,因为用户很可能不再返回来。
**实现Fragment的UI**
提供Fragment的UI,必须实现onCreateView()方法。
假设Fragment的布局设置写在example_fragment.xml资源文件中,那么onCreateView()方法可以如下写:
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
~~~
public static class ExampleFragment extends Fragment
{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
// Inflate the layout for this fragment
return inflater.inflate(R.layout.example_fragment, container, false);
}
}
~~~
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
onCreateView()中container参数代表该Fragment在Activity中的父控件;savedInstanceState提供了上一个实例的数据。
inflate()方法的三个参数:
第一个是resource ID,指明了当前的Fragment对应的资源文件;
第二个参数是父容器控件;
第三个布尔值参数表明是否连接该布局和其父容器控件,在这里的情况设置为false,因为系统已经插入了这个布局到父控件,设置为true将会产生多余的一个View Group。
### 把Fragment加入Activity
当Fragment被加入Activity中时,它会处在对应的View Group中。
Fragment有两种加载方式:一种是在Activity的layout中使用标签声明;另一种方法是在代码中把它加入到一个指定的ViewGroup中。
另外,Fragment它可以并不是Activity布局中的任何一部分,它可以是一个不可见的部分。这部分内容先略过。
**加载方式1:通过Activity的布局文件将Fragment加入Activity**
在Activity的布局文件中,将Fragment作为一个子标签加入即可。
如:
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
~~~
~~~
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
其中android:name属性填上你自己创建的fragment的完整类名。
**当系统创建这个Activity的布局文件时,系统会实例化每一个fragment,并且调用它们的onCreateView()方法,来获得相应fragment的布局,并将返回值插入fragment标签所在的地方。**
有三种方法为Fragment提供ID:
android:id属性:唯一的id
android:tag属性:唯一的字符串
如果上面两个都没提供,系统使用容器view的ID。
**加载方式2:通过编程的方式将Fragment加入到一个ViewGroup中**
当Activity处于Running状态下的时候,可以在Activity的布局中动态地加入Fragment,只需要指定加入这个Fragment的父View Group即可。
首先,需要一个FragmentTransaction实例:
~~~
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
~~~
**(注,如果import android.support.v4.app.FragmentManager;那么使用的是:FragmentManager fragmentManager = getSupportFragmentManager();)**
之后,用add()方法加上Fragment的对象:
~~~
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
~~~
其中第一个参数是这个fragment的容器,即父控件组。
最后需要调用commit()方法使得FragmentTransaction实例的改变生效。
### 实例
练习的例子:
写一个类继承自Fragment类,并且写好其布局文件(本例中是两个TextView),在Fragment类的onCreateView()方法中加入该布局。
之后用两种方法在Activity中加入这个fragment:
第一种是在Activity的布局文件中加入标签;
第二种是在Activity的代码中使用FragmentTransaction的add()方法加入fragment。
**贴出代码:**
自己定义的fragment类:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396b79c3e.gif)
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
~~~
package com.example.learningfragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class ExampleFragment extends Fragment
{
//三个一般必须重载的方法
@Override
public void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
System.out.println("ExampleFragment--onCreate");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
System.out.println("ExampleFragment--onCreateView");
return inflater.inflate(R.layout.example_fragment_layout, container, false);
}
@Override
public void onPause()
{
// TODO Auto-generated method stub
super.onPause();
System.out.println("ExampleFragment--onPause");
}
~~~
~~~
@Override
public void onResume()
{
// TODO Auto-generated method stub
super.onResume();
System.out.println("ExampleFragment--onResume");
}
@Override
public void onStop()
{
// TODO Auto-generated method stub
super.onStop();
System.out.println("ExampleFragment--onStop");
}
}
~~~
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
fragment的布局文件:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396b79c3e.gif)
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
~~~
~~~
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
主Activity:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396b79c3e.gif)
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
~~~
package com.example.learningfragment;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
public class LearnFragment extends FragmentActivity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_learn_fragment);
//在程序中加入Fragment
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.linear, fragment);
fragmentTransaction.commit();
}
}
~~~
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
Activity的布局文件:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396b79c3e.gif)
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
~~~
~~~
[![复制代码](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-30_5632e1b8d3b57.gif)]( "复制代码")
运行结果如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396b96d5e.png)
可以看到第二种方式加入fragment的时候,指定了父容器(一个线性布局)的id,其中已经有一个Button 3,所以fragment加在其后。
### 参考资源
Fragment类文档:
[http://developer.android.com/reference/android/app/Fragment.html](http://developer.android.com/reference/android/app/Fragment.html)
Training:Building a Dynamic UI with Fragments
[http://developer.android.com/training/basics/fragments/index.html](http://developer.android.com/training/basics/fragments/index.html)
Fragments Develop Guide:
[http://developer.android.com/guide/components/fragments.html](http://developer.android.com/guide/components/fragments.html)
';
activity间的数据传递
最后更新于:2022-04-01 19:47:18
在Activity_A中启动Activity_B
实现Activity_A和Activity_B间的对话
一、信息仅从Activity_A传递到Activity_B
Activity_A.java :
~~~
Intent intent = new Intent(Activity_A.this,Activity_B.class);
intent.putExtra(key,value);
startActivity(intent);
~~~
将value信息,以键-值的形式,附加在Intent对象上发送过去,value可以是任意数据。
Activity_B.java :
~~~
getIntent().get类型Extra(key,defaultValue);
//类型表示接收信息的类型,比如getStringExtra(...)等
~~~
通过此行代码返回传递过来的值,明显第二个参数为默认值。
二、Activity_B同时返回信息到Activity_A
Activity_A.java :
~~~
Intent intent = new Intent(Activity_A.this,Activity_B.class);
intent.putExtra(key,value);
startActivityForResult(intent,requestCode);
~~~
可以看到,启动Activity_B的方法更改成startActivityForResult(...)。
requestCode表示请求代码,当Activity_A启动多个不同类型的子activity时,用于区分各消息反馈。
在Activity_A中还需要重写onActivityResult(...)方法,来获取Activity_B回传的值。
~~~
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data != null) {
value = data.get类型Extra(key, defaultValue); // value表示回传的值
}
}
~~~
Activity_B.java :
~~~
getIntent().get类型Extra(key,defaultValue);
Intent data = new Intent();
data.putExtra(key,value);
setResult(resultCode,data);
~~~
可以看出,子activity通过setResult(...)方法将信息返回给父activity。
';
JavaScript与Java通信
最后更新于:2022-04-01 19:47:16
1、WebView中JavaScript调用Android程序中Java:
使用WebView类中的addJavascriptInterface()方法,可以使用它扩展嵌入式浏览器内的DOM(文档对象模型),并定义JavaScript代码可以访问的新对象。JavaScript代码调用该对象的方法时,实际上它会调用Android程序中的方法。
2、在Android程序中调用JavaScript方法:
调用loadUrl()方法,将URL以javascript:*要执行的代码 *的形式传递给它。浏览器会在当前页面执行给定的JavaScript表达式,而不是转到新的页面。
实例:
构建一个Android程序,布局如下(res/layout/activity_local_browser.xml)
~~~
~~~
可以看出,布局的上半部分是WeView控件,下部分是来自Android用户界面的TextView和Button。
下面需要完成将被加载到WebView中的index.html文件(assets/index.html),代码如下
~~~
LocalBrower
Settings
TextView
Call JavaScript from Android
~~~
完成这些准备后,就可以把目光放在程序的LocalBrowser类上了,代码如下:
~~~
package com.example.localbrowser;
import android.support.v7.app.ActionBarActivity;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.JavascriptInterface;
import android.webkit.JsResult;
import android.webkit.WebView;
import android.webkit.WebChromeClient;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
@SuppressLint("JavascriptInterface")
public class LocalBrowser extends ActionBarActivity {
private WebView webview;
private Button button;
private TextView textview;
private final Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_local_browser);
webview = (WebView) findViewById(R.id.web_view);
button = (Button) findViewById(R.id.button);
textview = (TextView) findViewById(R.id.text_view);
button.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
//单击按钮时,通过WebView.loadUrl()方法调用index.html中定义的callJS()函数
webview.loadUrl("javascript:callJS('Hello from Android')");
}
});
//打开JavaScript(默认是关闭的)
webview.getSettings().setJavaScriptEnabled(true);
//往WebView中注入Java对象,从而使得在index.tml中能使用此对象的方法
webview.addJavascriptInterface(new AndroidBridge(), "android");
webview.setWebChromeClient(new WebChromeClient() {
// 覆盖默认的window.alert()的展示界面
public boolean onJsAlert(final WebView view, final String url,
final String message, JsResult result) {
Toast.makeText(LocalBrowser.this, message, 3000).show();
result.confirm();
return true;
}
});
webview.loadUrl("file:///android_asset/index.html");//加载本地网页,注意有3个斜杠
}
public class AndroidBridge {
@JavascriptInterface
// 要通过JavaScript调用的Java代码片段
public void callAndroid(final String arg) {
handler.post(new Runnable() {
public void run() {
textview.setText(arg);
}
});
}
}
}
~~~
整个过程很简单,但有点要注意的是:
在AdroidBridge类中
4.2之前向WebView注入的对象所暴露的接口callAndroid没有注释语句@JavascriptInterface,而4.2及以后的则多了注释语句@JavascriptInterface。如果去掉这行代码,当程序调用此方法时,将不能成功,而logcat会报如下输出:
E/Web Console: Uncaught TypeError: Object [object Object] has no method 'callAndroid'
程序界面:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396b045ad.jpg)
当单击按钮和链接时,它会在两个环境间进行调用,下面是运行效果图
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-17_56c4396b15320.jpg)
';
WebView
~~~ 可以看到,callJS()函数是将会在Java代码中被调用JavaScript函数,它接收一个参数并赋值给replaceme标签。往下的两条链接分别是调用window.alert()函数(显示短消息)和window.android对象的callAndroid()方法(在Java代码中定义好的)。 同时不要忘记在res/values/strings.xml文件中完成字符串的赋值: ~~~
Android颜色对照表
最后更新于:2022-04-01 19:47:13
**颜色代码大全**
AA 指定透明度。 00 是完全透明。 FF 是完全不透明。超出取值范围的值将被恢复为默认值。
浅绿色:浅绿色aqua
碧绿色:碧绿色aquamarine
天蓝色:天蓝色azure(汗。。。)
米色:米色beige
桔黄色:桔黄色bisque
黑色:黑色black
白杏色:白杏色blanchedalmond
蓝色:蓝色blue
蓝紫色:蓝紫色blueviolet
褐色:褐色brown
实木色:实木色burlywood
刺桧蓝色:刺桧蓝色cadetblue
亮黄绿色:亮黄绿色chartreuse
巧克力色:巧克力色chocolate
珊瑚色:珊瑚色coral
矢车菊色:矢车菊色cornflowerblue
谷丝色:谷丝色cornsilk
深红色:深红色crimson
蓝绿色:蓝绿色cyan
深蓝色:深蓝色darkblue
深青色:深青色darkcyan
深金杆色:深金杆色darkgoldenrod
深灰色:深灰色darkgray
深绿色:深绿色darkgreen
深黄褐色:深黄褐色darkkhaki
深洋红色:深洋红色darkmagenta
深橄榄绿色:深橄榄绿色darkolivegreen
深橙色:深橙色darkorange
深紫色:深紫色darkorchid
深红色:深红色darkred
深肉色:深肉色darksalmon
深海绿色:深海绿色darkseagreen
深暗灰蓝色:深暗灰蓝色darkslateblue
深暗蓝灰色:深暗蓝灰色darkslategray
深青绿色:深青绿色darkturquoise
深紫色:深紫色darkviolet
深粉色:深粉色deeppink
深天蓝色:深天蓝色deepskyblue
暗灰色:暗灰色dimgray
遮板蓝色:遮板蓝色dodgerblue
砖色:砖色firebrick
花白色:花白色floralwhite
葱绿色:葱绿色forestgreen
紫红色:紫红色fushcia
庚斯博罗灰色:庚斯博罗灰色gainsboro
幽灵白色:幽灵白色ghostwhite
金黄色:金黄色gold
金杆黄色:金杆黄色goldenrod
灰色:灰色gray
绿色:绿色green
绿黄色:绿黄色greenyellow
蜜汁色:蜜汁色honeydew
亮粉色:亮粉色hotpink
印第安红色:印第安红色indianred
靛青色:靛青色indigo
象牙色:象牙色ivory
黄褐色:黄褐色khaki
淡紫色:淡紫色lavender
浅紫红色:浅紫红色lavenderblush
草绿色:草绿色lawngreen
柠檬纱色:柠檬纱色lemonchiffon
浅蓝色:浅蓝色lightblue
浅珊瑚色:浅珊瑚色 lightcoral
浅金杆黄色:浅金杆黄色lightgoldenrodyellow
转载来自:[点击打开链接](http://blog.sina.com.cn/s/blog_7ed4baf90100sbn6.html)
';
颜色代码表2:
古典白色 |
前言
最后更新于:2022-04-01 19:47:11
> 原文出处:[Android学习笔记](http://blog.csdn.net/column/details/androidtome.html)
作者:[u012964281](http://blog.csdn.net/u012964281)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# Android学习笔记
> 关于Android学习过程中的一些记录和总结
';