Android学习日志
常见布局
相对布局 RelativeLayout
-
组件默认左对齐、顶部对齐
-
设置组件在指定组件的右边
android:layout_toRightOf="@id/tv1"
-
设置在指定组件的下边
android:layout_below="@id/tv1"
-
设置右对齐父元素
android:layout_alignParentRight="true"
-
设置与指定组件右对齐
android:layout_alignRight="@id/tv1"
线性布局LinearLayout
-
指定各个节点的排列方向
-
设置右对齐
android:layout_gravity="right"
-
当竖直布局时,只能左右对齐和水平居中,顶部底部对齐竖直居中无效
-
当水平布局时,只能顶部底部对齐和竖直居中
-
使用match_parent时注意不要把其他组件顶出去
-
线性布局非常重要的一个属性:权重
android:layout_weight="1"
-
权重:按比例分配屏幕的剩余宽度或者高度
帧布局FrameLayout
-
默认组件都是左对齐和顶部对齐,每个组件相当于一个div
-
可以设置上下左右对齐,水平竖直居中,设置方式与线性布局一样
android:layout_gravity="bottom"
-
不能相对于其他组件布局
表格布局TableLayout
-
每个
<TableRow/>
节点是一行,它的每个子节点是一列 -
表格布局中的节点可以不设置宽高,因为设置了也无效
- 根节点
<TableLayout/>
的子节点宽为匹配父元素,高为包裹内容 <TableRow/>
节点的子节点宽为包裹内容,高为包裹内容- 以上默认属性无法修改
- 根节点
-
根节点中可以设置以下属性,表示让第1列拉伸填满屏幕宽度的剩余空间
android:stretchColumns="1"
绝对布局AbsoluteLayout
- 直接指定组件的x、y坐标
android:layout_x="144dp"
android:layout_y="154dp"
注意:直接复制项目需要改动的地方:项目名字、应用包名、R文件重新导包
路径API说明
-
getFilesDir()得到的file对象的路径是data/data/应用包名/files
- 存放在这个路径下的文件,只要你不删,它就一直在
-
getCacheDir()得到的file对象的路径是data/data应用包名/cache
- 存放在这个路径下的文件,当内存不足时,有可能被删除
-
系统管理应用界面的清除缓存,会清除cache文件夹下的东西,清除数据,会清除整个包名目录下的东西
sd卡的路径
-
2.2之前,sd卡路径:sdcard
-
4.3之前,sd卡路径:mnt/sdcard
-
4.3开始,sd卡路径:storage/sdcard
-
最简单的打开sd卡的方式
File file = new File("sdcard/test.txt");
-
使用api获得sd卡的真实路径,部分手机品牌会更改sd卡的路径
Environment.getExternalStorageDirectory()
-
判断sd卡是否准备就绪
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
获取SD容量
1import java.io.File;
2
3import android.app.Activity;
4import android.os.Bundle;
5import android.os.Environment;
6import android.os.StatFs;
7import android.text.format.Formatter;
8import android.widget.TextView;
9
10public class MainActivity extends Activity {
11 private TextView tv;
12 @Override
13 protected void onCreate(Bundle savedInstanceState) {
14 super.onCreate(savedInstanceState);
15 setContentView(R.layout.activity_main);
16 tv = (TextView) findViewById(R.id.tv);
17 if(existSDCard()){
18 String available = formatSize(getAvailable());
19 String allSize = this.formatSize(getAllSize());
20 tv.setText("总空间:"+allSize+" 可用空间"+available);
21 }
22 }
23
24 //获取可用容量
25 private long getAvailable(){
26 File path = Environment.getExternalStorageDirectory();
27 StatFs sf = new StatFs(path.getPath());
28 //获取单个数据块的大小(Byte)
29 long blockSize = sf.getBlockSizeLong();
30 //获取所有数据块数
31 long availbleBlocks = sf.getAvailableBlocksLong();
32 return blockSize* availbleBlocks;
33 }
34
35 //获取总容量
36 private long getAllSize(){
37 File path = Environment.getExternalStorageDirectory();
38 StatFs sf = new StatFs(path.getPath());
39 //获取单个数据块的大小(Byte)
40 long blockSize = sf.getBlockSizeLong();
41 //获取所有数据块数
42 long allBlocks = sf.getBlockCountLong();
43 return blockSize*allBlocks;
44 }
45
46 //格式化函数
47 private String formatSize(long size) {
48 return Formatter.formatFileSize(this, size);
49 }
50
51 //是否挂载SD卡
52 private boolean existSDCard() {
53 if (Environment.getExternalStorageState().equals(
54 Environment.MEDIA_MOUNTED)) {
55 return true;
56 } else return false;
57 }
58}
注意:在使用getBlockCountLong()
等API的时候主要SDK的版本,SDK17是不支持getBlockCountLong()
的,低版本的SDK仍然使用getBlockCount()
等不带Long
的
测试
测试分类
-
黑盒测试
- 测试逻辑业务
-
白盒测试
- 测试逻辑方法
-
根据测试粒度
- 方法测试:function test
- 单元测试:unit test
- 集成测试:integration test
- 系统测试:system test
-
根据测试暴力程度
- 冒烟测试:smoke test(例如monkey 1000:在屏幕上随机点击1000次)
- 压力测试:pressure test
Android中的单元测试
首先要使用一个类继承自AndroidTestCast:
1import android.test.AndroidTestCase;
2
3public class Test extends AndroidTestCase {
4 public void test(){
5 //TODO 测试的代码或者方法
6 }
7}
并且在中做出如下配置:
1<instrumentation android:name="android.test.InstrumentationTestRunner"
2 android:targetPackage="要测试的应用包名">
3</instrumentation>
4<application
5 android:allowBackup="true"
6 android:icon="@drawable/ic_launcher"
7 android:label="@string/app_name"
8 android:theme="@style/AppTheme" >
9 <!-- 只有这一个属性就是android.test.runner -->
10 <uses-library android:name="android.test.runner"/>
11</application>
SQLite
-
轻量级关系型数据库
-
创建数据库需要使用的api:SQLiteOpenHelper
- 数据库被创建时会调用:onCreate方法
- 数据库升级时会调用:onUpgrade方法
1import android.content.Context; 2import android.database.sqlite.SQLiteDatabase; 3import android.database.sqlite.SQLiteOpenHelper; 4 5public class MyOpenHelper extends SQLiteOpenHelper { 6 7 public MyOpenHelper(Context context) { 8 //参数说明:上下文、数据库名称、游标工厂(默认使用NULL)、版本(用于升级) 9 super(context, "test.db", null, 1); 10 } 11 12 @Override 13 public void onCreate(SQLiteDatabase db) { 14 //创建表 15 db.execSQL("create table person(_id integer primary key autoincrement, name char(10), phone char(20), salary integer(10))"); 16 } 17 18 //数据库升级的时候调用 19 @Override 20 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 21 System.out.println("数据库升级..."); 22 } 23}
创建数据库
1//创建OpenHelper对象
2MyOpenHelper oh = new MyOpenHelper(getContext(), "test.db", null, 1);
3//获得数据库对象,如果数据库不存在,先创建数据库,后获得,如果存在,则直接获得
4SQLiteDatabase db = oh.getWritableDatabase();
- getWritableDatabase():打开可读写的数据库
- getReadableDatabase():在磁盘空间不足时打开只读数据库,否则打开可读写数据库
数据库的增删改查
数据库的增删改查可以自己使用SQL语句,然后db.execSQL("SQL语句")
即可完成!
注意:setup主要实现测试前的初始化工作,而teardown则主要实现测试完成后的垃圾回收等工作。
1@Override
2protected void setUp() throws Exception {
3 super.setUp();
4 //获取虚拟上下文对象
5 oh = new MyOpenHelper(getContext(), "test.db", null, 1);
6}
7@Override
8protected void tearDown() throws Exception {
9 super.tearDown();
10 //关闭数据库
11 db.close();
12}
使用API实现增删改查
1//使用游标来逐个访问每条数据
2public void select(){
3 Cursor cursor = db.rawQuery("select * from 表名", null);
4 //把指针移动至下一行
5 while(cursor.moveToNext()){
6 //先通过列名,获取列索引,然后再获取该列的内容
7 String str = cursor.getString(cursor.getColumnIndex("字段名1"));
8 String str2 = cursor.getString(cursor.getColumnIndex("字段名2"));
9 int str3 = cursor.getInt(cursor.getColumnIndex("字段名3"));
10 System.out.println(str+str2+str3);
11 }
12}
13public void insertApi(){
14 ContentValues values = new ContentValues();
15 values.put("字段1", "value");
16 values.put("字段2", "value");
17 values.put("字段3", "value");
18 //返回值-1,插入失败
19 long l = db.insert("表名", null, values);
20}
21
22public void deleteApi(){
23 //因为后面要传入的是对象数组
24 int i = db.delete("表名", "_id = ?", new String[]{"4"});
25}
26
27public void updateApi(){
28 ContentValues values = new ContentValues();
29 values.put("字段名", "value");
30 int i = db.update("表名", values, "_id = ?", new String[]{"1"});
31}
事务
保证多条SQL语句要么同时成功,要么同时失败,失败的话会发生回滚!
1public void transaction(){
2 try{
3 //开启事务
4 db.beginTransaction();
5
6 //TODO 修改表数据...
7
8 //设置事务执行成功,提交时如果这行代码没有执行过,就会回滚
9 db.setTransactionSuccessful();
10 }
11 catch (Exception e) {
12 //这里的异常必须要捕获
13 e.printStackTrace();
14 }
15 finally{
16 //关闭事务,提交数据
17 db.endTransaction();
18 }
19}
ListView
-
用于显示列表
-
MVC结构
- M:model模型层,要显示的数据 ————people集合
- V:view视图层,用户看到的界面 ————ListView
- c:control控制层,操作数据如何显示 ————adapter对象
-
每一个条目都是一个View对象
BaseAdapter
-
必须实现的两个方法
1// 适配器类 2class MyAdapter extends BaseAdapter { 3 // 系统调用:获取模型层数据的数量 4 @Override 5 public int getCount() { 6 return list.size(); 7 } 8 9 // 系统调用:获取要显示至ListView的View对象 10 // convertView:系统之前缓存的条目 11 @Override 12 public View getView(int position, View convertView, ViewGroup parent) { 13 People p = list.get(position); 14 View view = null; 15 if (convertView == null) { 16 // 把布局文件填充为view对象(看源码,这就是三种方式) 17 // LayoutInflater layIn = LayoutInflater.from(MainActivity.this); 18 // LayoutInflater layIn = (LayoutInflater)getSystemService("layout_inflater"); 19 // view = layIn.inflate(R.layout.item_listview,null); 20 view = View.inflate(getApplicationContext(),R.layout.item_listview, null); 21 22 } else { 23 view = convertView; 24 TextView tv_name = (TextView) view.findViewById(R.id.tv_name); 25 TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone); 26 TextView tv_sa = (TextView) view.findViewById(R.id.tv_sa); 27 28 tv_name.setText(p.getName()); 29 tv_phone.setText(p.getPhone()); 30 tv_sa.setText(p.getSalary()); 31 } 32 return view; 33 } 34}
屏幕上能显示多少个条目,getView方法就会被调用多少次,屏幕向下滑动时,getView会继续被调用,创建更多的View对象显示至屏幕
条目的缓存
当条目划出屏幕时,系统会把该条目缓存至内存,当该条目再次进入屏幕,系统在重新调用getView时会把缓存的条目作为convertView参数传入,但是传入的条目不一定是之前被缓存的该条目,即系统有可能在调用getView方法获取第一个条目时,传入任意一个条目的缓存!
ArrayAdapter
在条目中显示一个字符串
1String[] objects = new String[]{"AAA","BBB","CCC"}; 2ListView lv = (ListView) findViewById(R.id.lv); 3//arg1:指定要填充的布局文件 4//arg2:指定文本显示至哪一个文本框内 5lv.setAdapter(new ArrayAdapter<String>(this, R.layout.item_array, R.id.tv_name, objects));
SimpleAdapter
可在条目中显示多种数据
要显示的数据封装在List中,集合的每一个元素存放的是一个条目会显示的数据,因为可能会有多种数据,而集合的泛型只能指定一种数据,所以把数据先存放在Map中,在把Map放入List中
1protected void onCreate(Bundle savedInstanceState) { 2 super.onCreate(savedInstanceState); 3 setContentView(R.layout.activity_main); 4 lv = (ListView)findViewById(R.id.lv); 5 6 //把每个条目需要处理的所有数据封装至map中,在把map封装至list中 7 //这样就保证了list每个元素都包含了一个条目需要的所有数据 8 List<Map<String, Object>> data = new ArrayList<Map<String,Object>>(); 9 Map<String, Object> m1 = new HashMap<String, Object>(); 10 m1.put("name","AAA"); 11 m1.put("Photo",R.drawable.a); 12 data.add(m1); 13 14 Map<String, Object> m2 = new HashMap<String, Object>(); 15 m2.put("name","BBB"); 16 m2.put("Photo",R.drawable.b); 17 data.add(m2); 18 19 Map<String, Object> m3 = new HashMap<String, Object>(); 20 m3.put("name","CCC"); 21 m3.put("Photo",R.drawable.c); 22 data.add(m3); 23 24 lv.setAdapter(new SimpleAdapter(MainActivity.this, data, R.layout.item_listview, 25 new String[]{"name","Photo"} , 26 new int[]{R.id.name, R.id.iv}));
Android项目的目录结构
1、 Activity:应用被打开时显示的界面 2、 src:项目代码 3、R.java:项目中所有资源文件的资源id 4、 Android.jar:Android的jar包,导入此包方可使用Android的api 5、libs:导入第三方jar包 6、assets:存放资源文件,比方说mp3、视频文件 7、bin:存放编译打包后的文件 8、 res:存放资源文件,存放在此文件夹下的所有资源文件都会生成资源id 9、 drawable:存放图片资源 10、 layout:存放布局文件,把布局文件通过资源id指定给activity,界面就会显示出该布局文件定义的布局 11、 menu:定义菜单的样式 12、Strings.xml:存放字符串资源,每个资源都会有一个资源
JVM 和DVM(Dalvik)的区别
Android的新虚拟机ART: Dalvik:应用每次运行的时候,字节码都需要通过及时编译转换为机器码,这会拖慢应用的启动速度 ART:应用在第一次安装的时候,字节码会预先编译成机器码,使其成为真的本地应用,应用的启动和执行速度都会显著提升
adb指令:
adb install D:\weibo.apk 安装apk adb uninstall 包名 卸载apk adb kill-server 杀死adb进程 adb start-server 开启adb进程 adb devices 列举与开发环境连接的Android设备列表 adb shell 进入Android命令行执行Linux指令
一个windows指令:netstat -ano 查看端口号的占用情况(adb进程在5037端口,如果端口被占用则会导致adb启动失败)
调用系统的拨号Activity
1//点击按钮拨打电话
2 public void call(View v) {
3 //先创建一个意图对象
4 Intent intent = new Intent();
5 //设置动作,打电话
6 intent.setAction(Intent.ACTION_CALL);
7 intent.setData(Uri.parse("tel:" + phone));
8 //把意图告诉系统
9 startActivity(intent);
10
11 }
短信发送的API
1 public void send(View v) {
2 String mmsg = "Text";
3 String phone = "15291418231";
4 // 直接使用发送信息的API
5 SmsManager sm = SmsManager.getDefault();
6 // 将长短信截为短的短信
7 ArrayList<String> message = sm.divideMessage(mmsg);
8 // 截断后分条发送
9 for (String mmsg_str : message) {
10 // 目标号码、
11 // 短信中心号码(null使用默认)
12 // 短信文本
13 // 发送成功或失败的广播
14 // 对方接收成功的广播
15 sm.sendTextMessage(phone, null, mmsg_str, null, null);
16 }
17 }
点击事件的四种写法
- 定义一个MyListener实现onClickListener接口
- 定义一个匿名内部类实现onClickListener接口
- 让当前activity实现onClickListener接口
- 给Button节点设置onClick属性,然后在activity中定义跟该属性值同名的方法
px与dp:dp跟密度有关,px是像素,使用dp更容易做屏幕适配