今天看啥  ›  专栏  ›  zchanglin

Android学习日志

zchanglin  · 掘金  ·  · 2021-03-01 18:19
阅读 4

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 文件重新导包

  • getFilesDir() 得到的 file 对象的路径是 data/data / 应用包名 / files

    • 存放在这个路径下的文件,只要你不删,它就一直在
  • getCacheDir() 得到的 file 对象的路径是 data/data 应用包名 / cache

    • 存放在这个路径下的文件,当内存不足时,有可能被删除
  • 系统管理应用界面的清除缓存,会清除 cache 文件夹下的东西,清除数据,会清除整个包名目录下的东西


  • 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))


import java.io.File;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.StatFs;
import android.text.format.Formatter;
import android.widget.TextView;

public class MainActivity extends Activity {
	private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv);
        if(existSDCard()){
        	String available = formatSize(getAvailable());
        	String allSize = this.formatSize(getAllSize());
        	tv.setText("总空间:"+allSize+"  可用空间"+available);
        }
    }
    
    //获取可用容量
    private long getAvailable(){
    	File path = Environment.getExternalStorageDirectory();
    	StatFs sf = new StatFs(path.getPath());
        //获取单个数据块的大小(Byte)
        long blockSize = sf.getBlockSizeLong();
        //获取所有数据块数
        long availbleBlocks = sf.getAvailableBlocksLong();
    	return blockSize* availbleBlocks;
    }
    
    //获取总容量
    private long getAllSize(){
    	File path = Environment.getExternalStorageDirectory();
    	StatFs sf = new StatFs(path.getPath());
        //获取单个数据块的大小(Byte)
        long blockSize = sf.getBlockSizeLong();
        //获取所有数据块数
        long allBlocks = sf.getBlockCountLong();
        return blockSize*allBlocks;
    }
    
    //格式化函数
    private String formatSize(long size) {
        return Formatter.formatFileSize(this, size);
    }
    
    //是否挂载SD卡
    private boolean existSDCard() {
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            return true;
        } else return false;
    }
}
复制代码

注意:在使用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:

import android.test.AndroidTestCase;

public class Test extends AndroidTestCase {
	public void test(){
		//TODO 测试的代码或者方法
	}
}
复制代码

并且在中做出如下配置:

<instrumentation android:
	    android:targetPackage="要测试的应用包名">
</instrumentation>
<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
    	<!-- 只有这一个属性就是android.test.runner -->
        <uses-library android:/>
</application>
复制代码
  • 轻量级关系型数据库

  • 创建数据库需要使用的 api:SQLiteOpenHelper

    • 数据库被创建时会调用:onCreate 方法
    • 数据库升级时会调用:onUpgrade 方法
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
        
    public class MyOpenHelper extends SQLiteOpenHelper {
        
    	public MyOpenHelper(Context context) {
    		//参数说明:上下文、数据库名称、游标工厂(默认使用NULL)、版本(用于升级)
    		super(context, "test.db", null, 1);
    	}
        
    	@Override
    	public void onCreate(SQLiteDatabase db) {
    		//创建表
    		db.execSQL("create table person(_id integer primary key autoincrement, name char(10), phone char(20), salary integer(10))");
    	}
        
    	//数据库升级的时候调用
    	@Override
    	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    		System.out.println("数据库升级...");
    	}
    }
    复制代码

创建数据库

//创建OpenHelper对象
MyOpenHelper oh = new MyOpenHelper(getContext(), "test.db", null, 1);
//获得数据库对象,如果数据库不存在,先创建数据库,后获得,如果存在,则直接获得
SQLiteDatabase db = oh.getWritableDatabase();
复制代码
  • getWritableDatabase():打开可读写的数据库
  • getReadableDatabase():在磁盘空间不足时打开只读数据库,否则打开可读写数据库

数据库的增删改查

数据库的增删改查可以自己使用 SQL 语句,然后db.execSQL("SQL语句")即可完成!

注意:setup 主要实现测试前的初始化工作,而 teardown 则主要实现测试完成后的垃圾回收等工作。

@Override
protected void setUp() throws Exception {
	super.setUp();
	//获取虚拟上下文对象
	oh = new MyOpenHelper(getContext(), "test.db", null, 1);
}
@Override
protected void tearDown() throws Exception {
	super.tearDown();
    //关闭数据库
	db.close();
}
复制代码

使用 API 实现增删改查

//使用游标来逐个访问每条数据
public void select(){
		Cursor cursor = db.rawQuery("select * from 表名", null);
		//把指针移动至下一行
		while(cursor.moveToNext()){
			//先通过列名,获取列索引,然后再获取该列的内容
			String str = cursor.getString(cursor.getColumnIndex("字段名1"));
			String str2 = cursor.getString(cursor.getColumnIndex("字段名2"));
			int str3 = cursor.getInt(cursor.getColumnIndex("字段名3"));
			System.out.println(str+str2+str3);
		}
}
public void insertApi(){
		ContentValues values = new ContentValues();
		values.put("字段1", "value");
    	values.put("字段2", "value");
    	values.put("字段3", "value");
		//返回值-1,插入失败
		long l = db.insert("表名", null, values);
}
	
public void deleteApi(){
        //因为后面要传入的是对象数组
		int i = db.delete("表名", "_id = ?", new String[]{"4"});
}
	
public void updateApi(){
		ContentValues values = new ContentValues();
		values.put("字段名", "value");
		int i = db.update("表名", values, "_id = ?", new String[]{"1"});
}
复制代码

事务

保证多条 SQL 语句要么同时成功,要么同时失败,失败的话会发生回滚!

public void transaction(){
		try{
			//开启事务
			db.beginTransaction();
			
			//TODO 修改表数据...
	
			//设置事务执行成功,提交时如果这行代码没有执行过,就会回滚
			db.setTransactionSuccessful();
		}
		catch (Exception e) {
             //这里的异常必须要捕获
			e.printStackTrace();
		}
		finally{
			//关闭事务,提交数据
			db.endTransaction();
		}
}
复制代码
  • 用于显示列表

  • MVC 结构

    • M:model 模型层,要显示的数据 ————people 集合
    • V:view 视图层,用户看到的界面 ————ListView
    • c:control 控制层,操作数据如何显示 ————adapter 对象
  • 每一个条目都是一个 View 对象

    BaseAdapter

  • 必须实现的两个方法

    // 适配器类
    class MyAdapter extends BaseAdapter {
    		// 系统调用:获取模型层数据的数量
    		@Override
    		public int getCount() {
    			return list.size();
    		}
        
    		// 系统调用:获取要显示至ListView的View对象
    		// convertView:系统之前缓存的条目
    		@Override
    		public View getView(int position, View convertView, ViewGroup parent) {
    			People p = list.get(position);
    			View view = null;
    			if (convertView == null) {
    				// 把布局文件填充为view对象(看源码,这就是三种方式)
    				// LayoutInflater layIn = LayoutInflater.from(MainActivity.this);
    				// LayoutInflater layIn = (LayoutInflater)getSystemService("layout_inflater");
    				// view = layIn.inflate(R.layout.item_listview,null);
    				view = View.inflate(getApplicationContext(),R.layout.item_listview, null);
        				
    			} else {
    				view = convertView;
    				TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
    				TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone);
    				TextView tv_sa = (TextView) view.findViewById(R.id.tv_sa);
        
    				tv_name.setText(p.getName());
    				tv_phone.setText(p.getPhone());
    				tv_sa.setText(p.getSalary());
    			}
    			return view;
    		}
    }
    复制代码

屏幕上能显示多少个条目,getView 方法就会被调用多少次,屏幕向下滑动时,getView 会继续被调用,创建更多的 View 对象显示至屏幕

条目的缓存

当条目划出屏幕时,系统会把该条目缓存至内存,当该条目再次进入屏幕,系统在重新调用 getView 时会把缓存的条目作为 convertView 参数传入,但是传入的条目不一定是之前被缓存的该条目,即系统有可能在调用 getView 方法获取第一个条目时,传入任意一个条目的缓存!

ArrayAdapter

在条目中显示一个字符串

String[] objects = new String[]{"AAA","BBB","CCC"};
ListView lv = (ListView) findViewById(R.id.lv);
//arg1:指定要填充的布局文件
//arg2:指定文本显示至哪一个文本框内
lv.setAdapter(new ArrayAdapter<String>(this, R.layout.item_array, R.id.tv_name, objects));
复制代码

SimpleAdapter

可在条目中显示多种数据

要显示的数据封装在 List 中,集合的每一个元素存放的是一个条目会显示的数据,因为可能会有多种数据,而集合的泛型只能指定一种数据,所以把数据先存放在 Map 中,在把 Map 放入 List 中

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView)findViewById(R.id.lv);

        //把每个条目需要处理的所有数据封装至map中,在把map封装至list中
      	//这样就保证了list每个元素都包含了一个条目需要的所有数据
        List<Map<String, Object>> data = new ArrayList<Map<String,Object>>(); 
        Map<String, Object> m1 = new HashMap<String, Object>();
        m1.put("name","AAA");
        m1.put("Photo",R.drawable.a);
        data.add(m1);
        
        Map<String, Object> m2 = new HashMap<String, Object>();
        m2.put("name","BBB");
        m2.put("Photo",R.drawable.b);
        data.add(m2);
        
        Map<String, Object> m3 = new HashMap<String, Object>();
        m3.put("name","CCC");
        m3.put("Photo",R.drawable.c);
        data.add(m3);
        
        lv.setAdapter(new SimpleAdapter(MainActivity.this, data, R.layout.item_listview,
        		new String[]{"name","Photo"} ,
        		new int[]{R.id.name, R.id.iv}));
复制代码

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:存放字符串资源,每个资源都会有一个资源

Android 的新虚拟机 ART:
Dalvik:应用每次运行的时候,字节码都需要通过及时编译转换为机器码,这会拖慢应用的启动速度
ART:应用在第一次安装的时候,字节码会预先编译成机器码,使其成为真的本地应用,应用的启动和执行速度都会显著提升

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 启动失败)

//点击按钮拨打电话
    public void call(View v) {
         //先创建一个意图对象
         Intent intent = new Intent();
         //设置动作,打电话
         intent.setAction(Intent.ACTION_CALL);
         intent.setData(Uri.parse("tel:" + phone));
         //把意图告诉系统
         startActivity(intent);

    }
复制代码
public void send(View v) {
     String mmsg = "Text";
     String phone = "15291418231";
     // 直接使用发送信息的API
     SmsManager sm = SmsManager.getDefault();
     // 将长短信截为短的短信
     ArrayList<String> message = sm.divideMessage(mmsg);
     // 截断后分条发送
     for (String mmsg_str : message) {
         // 目标号码、
         // 短信中心号码(null使用默认)
         // 短信文本
         // 发送成功或失败的广播
         // 对方接收成功的广播
         sm.sendTextMessage(phone, null, mmsg_str, null, null);
     }
}
复制代码
  • 定义一个 MyListener 实现 onClickListener 接口
  • 定义一个匿名内部类实现 onClickListener 接口
  • 让当前 activity 实现 onClickListener 接口
  • 给 Button 节点设置 onClick 属性,然后在 activity 中定义跟该属性值同名的方法

px 与 dp:dp 跟密度有关,px 是像素,使用 dp 更容易做屏幕适配




原文地址:访问原文地址
快照地址: 访问文章快照