Android系统四层架构
Android四大组件
常用布局管理器及继承关系
- 常用布局管理器
- LinearLayout(线性布局)
- RelativeLayout(相对布局)
- TableLayout(相对布局)
- AbsoluteLayout(绝对布局)
- TableLayout(表格布局)
- GridLayout(网格布局)
Activity生命周期
Handler
Handler类的作用包括:在新启动的线程中发送消息和在主线程中获取和处理消息
ListView的使用
菜单使用时需回调的方法
编程
1、布局管理器的课上讲解练习
线性布局
- LinearLayout:线性布局,子控件的排列顺序是在水平方向或竖直方向一个挨着一个排列
- orientation:指定排列方向
- vertical:在竖直方向排列
- horizontal:水平方向排列
- weightSum:指定总比重
- layout_weight:指定子控件的占的比重
<?xml version="1.0" encoding="utf-8"?>
<!--LinearLayout:线性布局,子控件的排列顺序是在水平方向或竖直方向一个挨着一个排列
orientation:指定排列方向,vertical:在竖直方向排列 horizontal:水平方向排列
weightSum:指定总比重
layout_weight:指定子控件的占的比重-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="20">
<Button
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="2"/>
<Button
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="3"/>
</LinearLayout>
相对布局
- RelativeLayout:相对布局,子控件的位置是由父控件或兄弟控件的相对位置决定
- 根据父控件的相对位置的属性:
- layout_centerInParent:在容器的正中间
- layout_centerVertical:在容器的竖直方向的中间
- layout_centerHorizontal:在水平方向的中间
- layout_alignParentLeft:和父控件的左边框对齐
- layout_alignParentRight:和父控件的右边框对齐
- layout_alignParentTop:和父控件的上边框对齐
- layout_alignParentBottom:和父控件的下边框对齐
- 根据兄弟控件的相对位置的属性:
- layout_toLeftOf:在某个兄弟控件的左边
- layout_toRightOf:右边
- layout_above:上边
- layout_below:下边
- layout_alignLeft:和兄弟控件左边框对齐
- layout_alignRight:有边框对齐
- layout_alignTop:上边框对齐
- layout_alignBottom:下边框对齐
<?xml version="1.0" encoding="utf-8"?>
<!--RelativeLayout:相对布局,子控件的位置是由父控件或兄弟控件的相对位置决定
根据父控件的相对位置的属性:
layout_centerInParent:在容器的正中间
layout_centerVertical:在容器的竖直方向的中间
layout_centerHorizontal:在水平方向的中间
layout_alignParentLeft:和父控件的左边框对齐
layout_alignParentRight:和父控件的右边框对齐
layout_alignParentTop:和父控件的上边框对齐
layout_alignParentBottom:和父控件的下边框对齐
根据兄弟控件的相对位置的属性:
layout_toLeftOf:在某个兄弟控件的左边
layout_toRightOf:右边
layout_above:上边
layout_below:下边
layout_alignLeft:和兄弟控件左边框对齐
layout_alignRight:有边框对齐
layout_alignTop:上边框对齐
layout_alignBottom:下边框对齐
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="中"/>
<Button
android:layout_above="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/btn"
android:text="北"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/btn"
android:layout_alignLeft="@+id/btn"
android:text="南"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/btn"
android:layout_alignTop="@+id/btn"
android:text="西"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/btn"
android:layout_alignTop="@+id/btn"
android:text="东"/>
</RelativeLayout>
绝对布局
- **AbsoluteLayout:(不建议使用,适配效果差)绝对布局,控件的位置是根据绝对坐标点决定,坐标原点是左上角
- layout_x:指定控件x轴坐标
- layout_y:指定y轴坐标
<?xml version="1.0" encoding="utf-8"?>
<!--AbsoluteLayout:(不建议使用,适配效果差)绝对布局,控件的位置是根据绝对坐标点决定,坐标原点是左上角
layout_x:指定控件x轴坐标
layout_y:指定y轴坐标-->
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_x="100dp"
android:layout_y="100dp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</AbsoluteLayout>
网格布局
- GridLayout:网格布局
- rowCount:指定一共有多少行
- columnCount:指定一共有多少列
- layout_row:指定控件在多少行
- layout_column:指定在多少列
- layout_columnSpan:横跨多少列,需要同时设置layout_gravity="fill"
- layout_rowSpan:竖跨多少行,需要同时设置layout_gravity="fill"
<?xml version="1.0" encoding="utf-8"?>
<!--GridLayout:网格布局
rowCount:指定一共有多少行
columnCount:指定一共有多少列
layout_row:指定控件在多少行
layout_column:指定在多少列
layout_columnSpan:横跨多少列,需要同时设置layout_gravity="fill"
layout_rowSpan:竖跨多少行,需要同时设置layout_gravity="fill"-->
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent"
android:rowCount="5"
android:columnCount="5">
<Button />
<Button
android:layout_row="1"
android:layout_column="2"
android:layout_columnSpan="2"
android:layout_rowSpan="2"
android:layout_gravity="fill"/>
<Button/> <Button/> <Button/>
<Button/> <Button/> <Button/> <Button/> <Button/>
<Button/>
</GridLayout>
表格布局
- 表格布局,有多少行就添加几个TableRow
<?xml version="1.0" encoding="utf-8"?>
<!--TableLayout:表格布局,有多少行就添加几个TableRow-->
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TableRow>
<Button /> <Button/> <Button/>
</TableRow>
<TableRow>
<Button/> <Button/> <Button/> <Button/>
</TableRow>
<TableRow>
<Button/> <Button/>
</TableRow>
</TableLayout>
帧布局
- layout_gravity:控制控件的对齐位置
<?xml version="1.0" encoding="utf-8"?>
<!--FrameLayout:帧布局,每一个子控件是一帧
layout_gravity:控制控件的对齐位置-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
2、通过ContentReceiver的使用
BroadcaseReceiver
BroadcastReceiver是广播接收器,用于接收系统和应用中的广播
BroadcastReceiver是一种对广播进行过滤接收并响应的组件
自身并不提供用户图形界面
本质上就是一个全局监听器,用于监听系统全局的广播消息
实现广播和接收Intent的步骤
注册BroadCaseReceiver广播接收器
上面的方式已经过时,不安全,现在都是动态注册
-
动态注册
3、使用IO流进行文件读写
IO读写
package com.example.asyncdemo;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/*//访问外部存储中公有目录下的文件,都需要添加读或写文件的权限
// 获取外部存储根目录
Environment.getExternalStorageDirectory(); //
//获取9大公有目录
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
*//*获取的外部存储中私有目录 不需要权限*//*
getExternalCacheDir(); //android/data/com.example.asyncdemo/cache
getExternalFilesDir("a.txt"); //android/data/com.example.asyncdemo/file/a.txt
*//*获取内部存储中的私有目录,不需要权限
MODE_PRIVATE:覆盖的模式,写入文件的内容会替换之前的内容
MODE_APPEND:追加的模式*//*
getDir("a.txt",MODE_APPEND); //data/data/com.example.asyncdemo/a.txt
getCacheDir(); //data/data/com.example.asyncdemo/cache
getFilesDir(); //data/data/com.example.asyncdemo/files
try {
InputStream is = openFileInput("a.txt"); //data/data/com.example.asyncdemo/file/a.txt 文件对应的输入流
OutputStream os = openFileOutput("a.txt",MODE_PRIVATE);
} catch (FileNotFoundException e) {
e.printStackTrace();
}*/
public class MainActivity extends AppCompatActivity {
ProgressBar pb;
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pb = findViewById(R.id.pb);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
//如果没有写文件的权限,进行申请
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},101);
}else { //如果有权限,直接写文件
writeE();
}
/*读取内部存储中/files目录下的文件,不需要读写的权限*/
// try { //写文件
// OutputStream outputStream = openFileOutput("a.txt",MODE_APPEND);
// outputStream.write("你好".getBytes());
// outputStream.close();
//
// /*读取文件*/
// InputStream is = openFileInput("a.txt");
// byte[] bytes = new byte[1024];
// while (is.read(bytes) != -1){
// Toast.makeText(getApplicationContext(),bytes.toString(),Toast.LENGTH_SHORT).show();
// }
//
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// } catch (IOException e) {
// e.printStackTrace();
// }
//
//
// MyTask myTask = new MyTask();
// myTask.execute(); //执行异步任务
}
/*动态权限申请结果的回调*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 101 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ //表示用户选择了运行授予权限
writeE();
}else {
finish();
}
}
/*该方法用于读取共有目录的文件*/
public void writeE(){
String path = Environment.getExternalStorageDirectory()+ "/my/a.txt";
File file = new File(path);
if(!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write("你很好".getBytes());
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class MyTask extends AsyncTask<Void,Integer,String>{
/*耗时操作执行之前调用,进行一些初始化操作*/
@Override
protected void onPreExecute() {
super.onPreExecute();
}
/*此方法用于执行耗时操作,此方法运行在子线程中*/
@SuppressLint("WrongThread")
@Override
protected String doInBackground(Void... voids) {
for(int a = 0;a<100;a++){
try {
Thread.sleep(100);
onProgressUpdate(a+1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "下载任务执行完成";
}
/*该方法进行实时更新UI,此方法在主进线中执行*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
pb.setProgress(values[0]);
}
/*耗时操作执行完成后回调该方法,主线程中执行*/
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
/*耗时操作执行完之后设置进度条不可见*/
pb.setVisibility(View.GONE);
Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
}
}
}
4、SQLiteOpenHelper的使用
SQLite
数据库文件存储在内部文件,data/该应用文件/databases中
代码示例
- 这种方式尽量不用
package com.qst.sqlitedemo;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.util.Log;
import android.widget.SimpleCursorAdapter;
public class MainActivity extends AppCompatActivity {
SQLiteDatabase db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//
PenpleDao dao = new PenpleDao();
dao.insert(this);
//打开一个数据库对象,如果不存在则新建该数据库,该数据库默认保存在内部存储
db = openOrCreateDatabase("my.db",MODE_PRIVATE,null);
try{
//execSQL执行sql语句,创建一张表
// db.execSQL("CREATE table student(id integer primary key,age integer,name text)");
//向表中插入一条数据
db.execSQL("INSERT into student(id,age,name)values(1,25,'zhangsan')");
//通过insert方法插入一条数据,insert返回值是插入行的rowid,如果返回-1表示插入失败
ContentValues cv = new ContentValues();
cv.put("id",3);
cv.put("age",20);
cv.put("name","lisi");
db.insert("student",null,cv);
}catch(Exception e){
e.printStackTrace();
}
//删除数据操作
// db.execSQL("delete from student where id=1");
// db.delete("student","name=?",new String[]{"lisi"}); //返回值表示多少行数据被影响了
//
// //修改数据
// db.execSQL("update student set age=10 where id=2");
//
// ContentValues cv2 = new ContentValues();
// cv2.put("name","wangwu");
// db.update("student",cv2,"name=?",new String[]{"lisi"}); //返回值表示多少行数据被影响了
//
// //查询,返回结果是一个cursor游标对象,该对象类似于JDBC ResultSet
Cursor cursor = db.query(true,"student",new String[]{"age,name"},
"id<?",new String[]{"10"},null,null,null,null);
while (cursor.moveToNext()){
Log.d("sql",cursor.getInt(0)+"");
Log.d("sql",cursor.getString(1));
}
db.close();
}
}
SQLiteOpenHelper
MyDBHelper.java 代码示例
package com.qst.sqlitedemo;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.Nullable;
/*用于获取数据库连接*/
public class MyDBHelper extends SQLiteOpenHelper {
public MyDBHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
/*创建数据库时回调该方法,一般在该方法里执行表的创建*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table people(_id integer primary key autoincrement,name text,age integer,phone text)");
db.execSQL("create table student(_id integer primary key autoincrement,name text,age integer,phone text)");
}
/*数据库版本更新时执行该方法*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
PeopleDao实例代码
package com.qst.sqlitedemo;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
/*封装对people表的增删改查的操作*/
public class PenpleDao {
public void insert(Context context){
//实例化MyDBHelper对象,第二个参数:数据库的名字
MyDBHelper myDBHelper = new MyDBHelper(context,"test.db",null,1);
//通过myDBHelper获取一个数据库对象(数据库连接)
SQLiteDatabase db = myDBHelper.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("age",20);
cv.put("name","lisi");
db.insert("people","",cv);
db.close();
myDBHelper.close();
}
}