安卓(Android)是一种基于Linux内核(不包含GNU组件)的自由及开放源代码的移动操作系统。主要应用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。Android操作系统最初由安迪·鲁宾开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。随后Google以Apache开源许可证的授权方式,发布了Android的源代码。

1.Android基本

1.1.下载Android Studio和SDK

Install Android Studio | Android Developers (google.cn)

1.2.观察APP运行日志

Log.e表示错误信息

Log.w表示警告信息

Log.i表示一般消息

Log.d表示调试信息

Log.v表示冗余信息

1.3.工程目录结构

1.3.1.gradle

.gradle和.idea Android Studio自动生成的文件,不用了解,一般不要改动。

app 代码、资源等内容(常用)

build 编译文件夹,一般不用

gradle gradle wrapper的配置文件,Android Studio默认没有启动gradle wrapper的方式,如果需要打开,可以点击Android Studio导航栏 –> File –> Settings –> Build,Execution,Deployment –> Gradle,进行配置更改。

.gitignore 将指定的目录或文件排除在版本控制之外

build.gradle 项目全局的gradle构建脚本,通常这个文件中的内容是不需要修改的

gradle.properties 全局的gradle配置文件,在这里配置的属性将会影响到项目中所有的gradle编译脚本

gradlew和gradlew.bat 在命令行界面中执行gradle命令,其中gradlew是在Linux或Mac系统中使用的,gradlew.bat是在Windows系统中使用的。

HelloWorld.iml iml文件是所有IntelliJ IDEA项目都会自动生成的一个文件(Android Studio是基于IntelliJ IDEA开发的),用于标识这是一个IntelliJ IDEA项目,我们不需要修改这个文件中的任何内容

local.properties 指定本机中的Android SDK路径,通常内容都是自动生成的,我们并不需要修改。除非你本机中的Android SDK位置发生了变化,那么就将这个文件中的路径改成新的位置即可。

settings.gradle 用于指定项目中所有引入的模块。需要我们手动去修改这个文件的场景可能比较少

1.3.2.app

build 编译时自动生成的文件,一般不用

libs 第三方jar包,jar包都放在libs目录下

AndroidTest 用来编写Android Test测试用例的,可以对项目进行一些自动化测试

java java目录是放置我们所有java代码的地方,展开该目录,你将看到我们刚才创建的HelloWorldActivity文件就在里面

res 所有资源文件都在这里 图片放在drawable目录下 布局放在layout目录下,(相当于html中的body)应用图标在mipmap目录下 字符串放在values目录下

AndroidManifest.xml 整个Android项目的配置文件,你在程序中定义的所以四大组件都需要在这个文件里注册,另外还可以在这个文件中给应用程序添加权限声明。

test 此处是用来编写Unit Test测试用例的,是对项目进行自动化测试的另一种方式。

.gitignore 这个文件用于将app模块内的指定的目录或文件排除在版本控制之外,作用和外层的.gitignore文件类似。

app.iml IntelliJ IDEA项目自动生成的文件,我们不需要关心或修改这个文件中的内容。

build.gradle 这是app模块的gradle构建脚本,这个文件中会指定很多项目构建相关的配置。

proguard-rules.pro 这个文件用于指定项目代码的混淆规则,当代码开发完成后打成安装包文件,如果不希望代码被别人破解

1.4.创建新的App页面

1.创建一个layout布局文件

xxx.xml

2.创建一个Activity Java类

使用startActivity切换页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.tv1);
tv.setText("你好,RICHU");
Log.d("ning","create");
Button btn = findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, MainActivity_2.class);
startActivity(intent);
}
});
}
}

2.Andriod控件

2.1.设置文本的内容

1.在XML文件中通过属性andriod:text

1
2
3
4
<!--res/values-->
<resources>
<string name="mytext" translatable="false">HAHAHA</string>
</resources>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--res/layoutres-->
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="@+id/mytest"
android:layout_height="match_parent">

<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:text="@string/mytext"

tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>

2.在Java代码中调用文本视图对象的setText方法设置文本

1
2
3
4
<!--res/values-->
<resources>
<string name="mytext" translatable="false">HAHAHA</string>
</resources>
1
2
3
4
5
6
7
8
9
10
//java/com/example/application1/MainActivity_2.java
public class MainActivity_2 extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
TextView tv = findViewById(R.id.tv);
tv.setText(R.string.mytext);
}
}

2.2.设置文本的大小

px 像素

dpi 指屏幕每英寸有多少个像素点

dip 长度单位,同一个单位在不同设备上有不同的显示效果,简称dp,与设备无关只与屏幕的尺寸有关

sp 专门用来设置字体大小,在系统中可以设置调整字体大小

px = dpi*dip/160

相同尺寸的手机,即使分辨率不同,同dp的组件占用屏幕比例也相同

1
2
//默认使用sp
tv.setTextSize(30);

2.3.设置文本颜色

使用自带的颜色,在Color类里面选择

tv.setTextColor(Color.BLACK);

使用自定义的颜色类似于argb() 前两位是透明度 ff为不透明,不设置透明度就直接透明了

1
tv.setTextColor(0xff00ff00);

在布局文件中设置 后两位是透明度ff为透明

八位编码#FFEEDDCC中,FF表示透明度,EE表示红色的浓度,DD表示 绿色的浓度,CC表示蓝色的浓度。透明度为FF表示完全不透明,为00表示完全透明。

1
2
3
4
5
6
7
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/mytext"
android:textColor="#8ec5fc"
tools:ignore="MissingConstraints" />

2.4.设置视图的宽高

视图宽高通过属性Android:layout_width表达,视图高度通过属性android:layout_height表达

match_parent 表示与上级视图保持一致

wrap_content 表示与内容自适应

以dp为单位的具体尺寸

1
2
3
4
5
6
7
8
9
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="17sp"
android:text="@string/mytext"
android:background="#00ffff"
android:layout_marginTop="5dp"
/>
1
2
3
4
5
6
7
8
9
10
11
public class MainActivity_2 extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
TextView tv = findViewById(R.id.tv);
ViewGroup.LayoutParams params = tv.getLayoutParams();
params.width = diptopx.dip(this,300);
tv.setLayoutParams(params);
}
}

工具类将dip转为px

1
2
3
4
5
6
7
8
public class diptopx {
public static int dip(Context context, float dip){
//获取当前手机的像素密度
float scale = context.getResources().getDisplayMetrics().density;
//四舍五入
return (int)(dip * scale +0.5f);
}
}

2.5.设置视图的间距

margin 当前视图和周围平级视图之间的距离

padding 当前视图与内部下级视图之间的距离

与前端的margin和padding类似有四个方向,layout_marginTop….

2.6.设置视图的对齐方式

layout_gravity 当前视图相对于上级视图的对齐方式

gravity下级视图相对于当前视图的对齐方式

left right top bottom

用|连接表示合并方向left|top 为左上方

3.布局

3.1LinearLayout线性布局

orientation

  • horizontal 内部视图在水平方向从左到右排列
  • vertical 内部视图在水平方向从上到下排列

如果不指定orientation属性,则LinearLayout默认水平方向排列

线性布局的权重概念,值得是线性布局的下级视图各自拥有多大比例的宽高

权重属性名叫layout_weight.但该属性不在LinearLayout节点设置,而在线性布局的直接下级视图设置,表示该下级视图占据的宽高比例。

  • layout_width=0 layout_weight表示水平方向的宽高比例
  • layout_height=0 layout_weight表示垂直方向的宽高比例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="横排第一个"
android:textSize="17sp"
android:textColor="#000000"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="横排第一个"
android:textSize="17sp"
android:textColor="#000000"/>
</LinearLayout>

3.2RelativeLayout相对布局

相对布局的下级视图位置由其他视图决定。用于确定下级视图位置的参照物分两种:

  • 与该视图自身平级的视图
  • 该视图的上级视图

如果不设置下级视图的参照物,那么下级视图默认显示在RelativeLayout内部的左上角

与父容器相关的定位:

android:layout_alignParentTop 控件顶部与父布局的顶部对齐。
android:layout_alignParentBottom 控件底部与父布局的底部对齐。
android:layout_alignParentLeft 控件左边与父布局的左边对齐。
android:layout_alignParentRight 控件右边与父布局的右边对齐。
android:layout_alignParentStart 将控件的起始边(根据布局方向,可能是左边或右边)与父容器的起始边对齐。
android:layout_alignParentEnd 将控件的结束边(根据布局方向,可能是右边或左边)与父容器的结束边对齐。
android:layout_centerHorizontal 将控件水平居中对齐。
android:layout_centerVertical 将控件垂直居中对齐。
android:layout_centerInParent 水平垂直都居中

相对定位属性:

android:layout_above 控件放置在指定控件的上方。
android:layout_below 控件放置在指定控件的下方。
android:layout_toLeftOf 控件放置在指定控件的左边。
android:layout_toRightOf 控件放置在指定控件的右边。
android:layout_alignTop 控件的顶部与指定控件的顶部对齐。
android:layout_alignBottom 控件的底部与指定控件的底部对齐。
android:layout_alignLeft 控件的左边与指定控件的左边对齐。
android:layout_alignRight 控件的右边与指定控件的右边对齐。
android:layout_alignStart 将控件的起始边(根据布局方向,可能是左边或右边)与指定控件的起始边对齐。
android:layout_alignEnd 将控件的结束边(根据布局方向,可能是右边或左边)与指定控件的结束边对齐。
android:layout_alignBaseline 控件的基线与指定控件的基线对齐。

3.3GridLayout网格布局

支持多行多列的布局

默认从左往右从上到下

  • columnCount属性,指定了网格的列数
  • rowCount属性,指定了网格的行数

设置视图的对齐方式

  • 采用layout_gravity属性,它指定了当前视图相对于上级视图的对齐方式
  • 采用gravity属性,它指定了下级视图相对于当前视图的对齐方式

layout_gravity和gravity取值为left,top,right,bottom ,center

left|top为左上

1
2
3
4
5
6
<GridLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_columnWeight="1"
android:layout_rowWeight="1"/>

设置权重layout_columnWeight,layout_rowWeight

3.4滚动视图ScrollView

ScrollView,它是垂直方向的滚动视图;垂直方向滚动时,layout_width属性值设置为match_parent,layout_height属性值设置为wrap_content

HorizontalScrollView,它是水平方向的滚动视图水平方向滚动时,layout_width属性值设置为wrap_content,layout_height属性设置为match_parent

3.5.Button

由TextView派生

  • 有默认背景
  • 文本居中对齐

相对于TextView新增的属性

android:textAllCaps=”true”

  • true将小写转为大写
  • false则不会转换

android:onClick=”doclick”

绑定事件(不常用)

3.6点击事件

setOnClickListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MainActivity_2 extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);

TextView tv = findViewById(R.id.tv);
Button bt = findViewById(R.id.bt);
bt.setOnClickListener(new MyOnclickListener(tv));
}

static class MyOnclickListener implements View.OnClickListener{
private final TextView tv;
public MyOnclickListener(TextView tv) {
this.tv = tv;
}
@Override
public void onClick(View v) {
String s = String.format("%s点击%s", SimpleUtil.dateformat(),((Button)v));
tv.setText(s);
}
}

创建公用OnClickListener

1
2
3
4
5
6
7
8
public class MainActivity_2 extends AppCompatActivity implements View.OnClickListener{
@override
public void Onclick(View v){
if (v.getId()==R.id.bt){

}
}
}

3.7长按点击事件

true则为开启事件冒泡,false则不开启

1
2
3
4
5
bt.setOnLongClickListener(v->{
String s = String.format("%s点击%s", SimpleUtil.dateformat(),((Button)v));
tv.setText(s);
return true;
});

3.8禁用恢复按钮

是否允许点击enable

1
2
bt1.setEnabled(true);
bt1.setEnabled(false);

true启用,false禁用

3.9ImageView

图像视图ImageView

图像视图展示的图片通常位于res/drawable***目录,设置图像视图的显示图片有两种方式:

在XML文件中,通过属性android:src设置图片资源,属性值格式形如“@drawable/不含扩展名的图片名称”。

在Java代码中,调用setlmageResource方法设置图片资源,方法参数格式形如“R.drawable.不含扩展名的图片名称”。

1
2
3
4
5
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="5dp"
android:src="@drawable/test"/>

scaleType属性

matrix:使用matrix方式进行缩放。

fitXY:横向、纵向独立缩放,以适应该ImageView。

fitStart:保持纵横比缩放图片,并且将图片放在ImageView的左上角。

fitCenter:保持纵横比缩放图片,缩放完成后将图片放在ImageView的中央。

fitEnd:保持纵横比缩放图片,缩放完成后将图片放在ImageView的右下角。

center:把图片放在ImageView的中央,但是不进行任何缩放。

centerCrop:保持纵横比缩放图片,以使图片能完全覆盖ImageView。

centerInside:保持纵横比缩放图片,以使得ImageView能完全显示该图片。(用于缩小图片)

1
2
3
4
5
ImageView imageView = findViewById(R.id.image);
//设置图片源
imageView.setImageResource(R.drawable.test);
//设置缩放对齐方式
imageView.setScaleType(ImageView.ScaleType.FIT_XY);

3.10ImageButton

同时显示文本和图像

通过Button上的drawable属性设置周围文本的图标

drawableTop:指定文字上方的图片

drawableBottom:指定文字下方的图片

drawableLeft:指定文字左方的图片

drawableRight:指定文字右方的图片

drawablePadding:指定图片与文字的间距

3.11计算器项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
public class CaculateActivity extends AppCompatActivity implements View.OnClickListener {

private TextView tv_result;
private String firstNum = "";
private String operator="";
private String secondNum = "";

private String showText = "";
private String result = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_caculate);
//获取结果文本视图
tv_result = findViewById(R.id.tv_result);
//给每个按钮绑定事件
findViewById(R.id.btn_cancel).setOnClickListener(this);
findViewById(R.id.btn_div).setOnClickListener(this);
findViewById(R.id.btn_mul).setOnClickListener(this);
findViewById(R.id.btn_clear).setOnClickListener(this);
findViewById(R.id.btn_seven).setOnClickListener(this);
findViewById(R.id.btn_eight).setOnClickListener(this);
findViewById(R.id.btn_nine).setOnClickListener(this);
findViewById(R.id.btn_plus).setOnClickListener(this);
findViewById(R.id.btn_four).setOnClickListener(this);
findViewById(R.id.btn_five).setOnClickListener(this);
findViewById(R.id.btn_six).setOnClickListener(this);
findViewById(R.id.btn_mius).setOnClickListener(this);
findViewById(R.id.btn_one).setOnClickListener(this);
findViewById(R.id.btn_two).setOnClickListener(this);
findViewById(R.id.btn_three).setOnClickListener(this);
findViewById(R.id.btn_sqrt).setOnClickListener(this);
findViewById(R.id.btn_divone).setOnClickListener(this);
findViewById(R.id.btn_zero).setOnClickListener(this);
findViewById(R.id.btn_point).setOnClickListener(this);
findViewById(R.id.btn_equal).setOnClickListener(this);
}

@Override
public void onClick(View v) {
String inputtext;
if (v.getId() == R.id.btn_sqrt){
inputtext = "√";
}else{
inputtext = ((TextView) v).getText().toString();
}
if (v.getId() == R.id.btn_clear) {
clear();
}else if(v.getId() == R.id.btn_cancel) {
cancel();
}else if(v.getId() == R.id.btn_plus) {
operator = inputtext;
refreshText(showText+inputtext);
}else if (v.getId() == R.id.btn_mius) {
operator = inputtext;
refreshText(showText+inputtext);
}else if (v.getId() == R.id.btn_mul) {
operator = inputtext;
refreshText(showText+inputtext);
}else if (v.getId() == R.id.btn_div) {
operator = inputtext;
refreshText(showText+inputtext);
}else if (v.getId() == R.id.btn_equal) {
double caculate_result = Caculate();
refreshOpreator(String.valueOf(caculate_result));
refreshText(showText+"="+caculate_result);
}else if (v.getId() == R.id.btn_sqrt) {
double sqrt_result = Math.sqrt(Double.parseDouble(firstNum));
refreshOpreator(String.valueOf(sqrt_result));
refreshText(showText + "^=" + sqrt_result);
}else if (v.getId()==R.id.btn_divone) {
double sqrt_result = 1/Double.parseDouble(firstNum);
refreshOpreator(String.valueOf(sqrt_result));
refreshText(showText + "/=" + sqrt_result);
}
else{
if (!result.isEmpty()){
clear();
}
if (operator.isEmpty()) {
firstNum = firstNum + inputtext;
} else {
secondNum = secondNum + inputtext;
}
//整数不需要前面的0
if (showText.equals("0") && !inputtext.equals(".")) {
refreshText(inputtext);
} else {
refreshText(showText + inputtext);
}
}
}
private void cancel(){
if (showText.length()>1){
refreshText(showText.substring(0,showText.length()-1));
}else{
refreshText("0");
}
}
private void refreshText(String text){
showText = text;
tv_result.setText(showText);
}
private void clear() {
refreshOpreator("");
refreshText("0");
}
private void refreshOpreator(String newresult){
result = newresult;
firstNum = result;
secondNum = "";
operator = "";
}
private double Caculate(){
switch (operator){
case "+":
return Double.parseDouble(firstNum) + Double.parseDouble(secondNum);
case "-":
return Double.parseDouble(firstNum) - Double.parseDouble(secondNum);
case "X":
return Double.parseDouble(firstNum) * Double.parseDouble(secondNum);
case "÷":
return Double.parseDouble(firstNum) / Double.parseDouble(secondNum);
}
return 0;
}
}

4.Activity

4.1启停activity

启动新Activity

startActivity(new Intent(原页面.this,目标页面.class))

返回

finnish()//结束当前的活动页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn).setOnClickListener(this);
}

@Override
public void onClick(View v) {
startActivity(new Intent(this,MainActivity2.class));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MainActivity2 extends AppCompatActivity implements View.OnClickListener {

@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
findViewById(R.id.btn1).setOnClickListener(this);
}

@Override
public void onClick(View v) {
if (v.getId() == R.id.btn1){
finish();
}
}
}

4.2Activity生命周期

onCreate:表示Activity正在被创建。生命周期的第一个方法,当打开一个activity时首先回调这个方法。在这个方法中一般做一些初始化工作,例如加载界面布局资源(setContentView)、数据初始化(findviewbyid)

onRestart : 表示Activity正在被重新启动。当前activity从不可见变为可见状态时这个方法就会回调。一般是用户行为导致,比如用户摁home键回到桌面,当用户再次回到本activity时,当前activity 走 onRestart->onStart->onResume

onStart :表示activity正在被启动。activity可见,未出现在前台。可以理解为activity已经显示出来了,但是我们看不到,不能与之交互。

onResume :表示activity已经可见,并且出现在前台,可以与用户进行交互。例如activity上有Button,此时我们就可以进行点击了。

onPause:表示activity正在停止,接着很快执行onStop。注意极端情况下,本Activity跳转其他activity后快速的回到当前activity时,当前activity的生命周期:onPause->onResume
但是这个“快速回到”要很快,一般情况下都是onPause->onStop->onRestart->onStart->onResume。这里不能做太耗时操作,可以做一些数据存储,动画停止工作。

onStop:表示activity即将停止,可以做一些回收工作。但是不能太耗时。

onDestroy :activity即将被销毁 ,activity生命周期最后一个回调,这里可以做一些回收工作,资源释放。

onNewIntent:重用已有的活动实例

activity.png

4.3Activity启动模式

1.在配置文件中设置启动模式

1
2
3
4
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="standard">

standard

默认启动模式

启动Activity的会依照顺序依次被压入Task栈内

singleTop

如果栈顶为新建的Activity(目标Activity),那么就不会重复创建新的Activity

适合开启渠道多,多应用开启调用的Activity,通过这种设置可以避免创建过的Activity被重复创建,多数通过动态设置使用

singleTask

栈内复用

程序的主界面,耗费系统资源的Activity

全局唯一模式

singleInstance

单独为它创建一个任务栈,新的Task只会有它一个实例。如果已经创建过,则不会创建新的Activity,而是将之前的Activity唤醒

2.在代码中动态设置

启动标志的取值说明如下:
Intent.FLAG_ACTIVITY_NEW_TASK:开辟一个新的任务栈
Intent.FLAG_ACTIVITY_SINGLE_TOP:当栈顶为待跳转的活动实例之时,则重用栈顶的实例
Intent.FLAG_ACTIVITY_CLEAR_TOP:当栈中存在待跳转的活动实例时,则重新创建一个新实例,并清除原实例上方的所有实例
Intent.FLAG_ACTIVITY_NO_HISTORY:栈中不保存新启动的活动实例
Intent.FLAG_ACTIVITY_CLEAR_TASK:跳转到新页面时,栈中的原有实例都被清空

1
2
3
4
5
public void onClick(View v) {
Intent intent = new Intent(this,MainActivity2.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}

登录后不再返回登录界面

1
2
3
Intent intent = new Intent(this,MainActivity2.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

4.4传递数据

4.4.1显示Intent和隐式Intent

Intent是各个组件之间信息沟通的桥梁,用于各组件的通信

显示Intent,直接指定来源活动和目标活动,属于精确匹配

  • 在Intent的构造函数中指定
  • 调用意图对象的SetClass方法指定
  • 调用意图对象的SetComponent方法指定
1
2
3
4
5
6
7
8
//第一种
Intent intent = new Intent(this,MainActivity2.class);
//第二种,调用意图对象的setClass方法指定
Intent intent = new Intent();
intent.setClass(this, MainActivity2.class);
//第三种,调用意图对象的SetComponet方法指定
ComponentName componentName = new ComponentName(this, MainActivity2.class);
intent.setComponent(componentName);

隐式Intent,没有明确指定要跳转的目标活动,只要给出一个动作字符串让系统自动匹配,属于模糊匹配

1
2
3
4
5
6
7
String phoneNumber ="1234";
Intent intent = new Intent();
//设置意图动作
intent.setAction(Intent.ACTION_DIAL);
Uri uri = Uri.parse("tel:"+phoneNumber);
intent.setData(uri);
startActivity(intent);

启动自定义的程序

1
2
3
4
Intent intent = new Intent();
intent.setAction("android.intent.action.RICHU");
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);

在要启动的程序中配置

1
2
3
4
5
<intent-filter>
<action android:name="android.intent.action.RICHU" />

<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

常见系统动作的取值说明

intent 类的系统动作常量名 系统动作的常量值 说明
ACTION_MAIN android.intent.action.MAIN App启动时的入口
ACTION_VIEW android.intent.action.VIEW 向用户显示数据
ACTION_SEND android.intent.action.SEND 分享内容
ACTION_CALL android.intent.action.CALL 直接拨号
ACITON_DIAL android.intent.action.DIAL 准备拨号
ACTION_SENDTO android.intent.action.SENDTO 发送短信
ACTION_ANSWER android.intent.action.ANSWER 接听电话

4.4.2向下一个Activity发送数据

使用Bundle对象存储待传递的数据信息

发送

1
2
3
4
5
6
7
8
Intent intent = new Intent(this, MainActivity2.class);
//创建一个新包裹
Bundle bundle = new Bundle();
Button send = findViewById(R.id.btn);
bundle.putString("request_time", MyDate.gettime());
bundle.putString("request_content",send.getText().toString());
intent.putExtras(bundle);
startActivity(intent);

接收

1
2
3
4
5
6
TextView  show = findViewById(R.id.show);
Bundle bundle = getIntent().getExtras();
String request_time = bundle.getString("request_time");
String request_content = bundle.getString("request_content");
String test = String.format("hahah\nha%s\n%s",request_time,request_content);
show.setText(test);

4.4.3向上一个Activity返回数据

1.上一个页面打包好请求数据后,调用StartActivityForResult方法执行跳转动作

2.下一个页面接收并解析请求数据,进行相应处理

3.下一个页面返回上一个页面时,打包应答数据并调用SetResult方法返回数据包裹

4.上一个页面重写方法OnActivityResult,解析获得下一个页面的返回数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class MainActivity3 extends AppCompatActivity implements View.OnClickListener {

private String myrequest = "this is a test";
private String getrequest;
private ActivityResultLauncher<Intent> register;

@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);

TextView begin = findViewById(R.id.begin);
begin.setText(myrequest);

findViewById(R.id.mybtn).setOnClickListener(this);

register = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), o -> {
if (o != null){
Intent intent = o.getData();
if (intent != null && o.getResultCode() == Activity.RESULT_OK){
Bundle bundle = intent.getExtras();
String request_time = bundle.getString("response");
String request_content = bundle.getString("content");
getrequest = String.format("hahah\n回复%s\n%s",request_time,request_content);
TextView end = findViewById(R.id.end);
end.setText(getrequest);
}
}
});
}

@Override
public void onClick(View v) {
Intent intent = new Intent(this, MainActivity4.class);
Bundle bundle = new Bundle();
bundle.putString("request_time", MyDate.gettime());
bundle.putString("request_content",myrequest);
intent.putExtras(bundle);

register.launch(intent);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MainActivity4 extends AppCompatActivity implements View.OnClickListener {

@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);

TextView get = findViewById(R.id.get);
Bundle bundle = getIntent().getExtras();
String request_time = bundle.getString("request_time");
String request_content = bundle.getString("request_content");
String test = String.format("hahah\n接收%s\n%s",request_time,request_content);
get.setText(test);
findViewById(R.id.set).setOnClickListener(this);
}

@Override
public void onClick(View v) {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("response", MyDate.gettime());
//待返回的数据
bundle.putString("content","chli");
intent.putExtras(bundle);
setResult(Activity.RESULT_OK,intent);
finish();
}
}

4.4.4利用资源配置文件配置字符串

1
String test = getString(R.string.test);

4.4.5利用元数据传递配置信息

1
<meta-data android:name="test" android:value="test"></meta-data>

5.Drawable

drawable-ldpi存放低分辨率的图片,基本没有这样的手机了

drawable-mdpi存放中等分辨率的图片,使用较少

drawable-hdpi存放高分辨率的图片 4-5英寸

drawable-xhdpi存放高分辨率的图片 5-5.5英寸

drawable-xxhdpi存放高分辨率的图片 6-6.5英寸

drawable-xxxhdpi存放高分辨率的图片 >7英寸

5.1形状图形

Shape图形又称形状图形,它用来描述常见的几何形状,包括矩形,圆角矩形,圆形,椭圆等等

rectangle矩形

oval椭圆

line直线

ring圆环

1
2
3
4
5
6
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#66ffaa"/>
<stroke android:width="1dp" android:color="#66ffaa"/>

</shape>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MainActivity6 extends AppCompatActivity implements View.OnClickListener {

private View v_content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main6);
v_content = findViewById(R.id.content);
findViewById(R.id.rectangle).setOnClickListener(this);
findViewById(R.id.oval).setOnClickListener(this);
v_content.setBackgroundResource(R.drawable.react_drawable);
}

@Override
public void onClick(View v) {
if (v.getId() == R.id.rectangle){
v_content.setBackgroundResource(R.drawable.react_drawable);
}else if(v.getId() == R.id.oval){
v_content.setBackgroundResource(R.drawable.react1_drawable);
}
}
}

size(尺寸)

size时shape的下级节点,描述形状图形的宽高尺寸,若无size,则与宿主图形一样(View)

  • height
  • width

stroke(描边)

  • color颜色
  • dashGap像素;类型
  • dashWidth虚线的宽度
  • width厚度

dashWidth和dashGap为0为实线

solid填充

color:颜色

padding

上下左右

gradient渐变

  • angle 整形渐变的起始角度
  • type 渐变的类型
类型 说明
linear 线性渐变
radial 放射渐变
sweep 滚动渐变

5.2九宫格图片

test.9.png

上下左右边缘线

黑线区域以内的图像会拉伸,黑线以外的图像保持原状

内部文字只能放在黑线区域内

5.3状态列表图形

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/test" android:state_pressed="true"/>
<item android:drawable="@drawable/tests"/>
</selector>

state

状态类型的属性名称 说明 适用的组件
state_pressed 是否按下 按钮Button
state_checked 是否勾选 复选框CheckBox、单选按钮RadioButton
state_focused 是否获取焦点 文本编辑EditText
state_selected 是否选中 各组件通用

5.4复选框CheckBOx

1
2
3
4
<CheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="系统checkbox"/>

checked属性

设置是否选择,可以设置默认选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MainActivity7 extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main7);
CheckBox cb = findViewById(R.id.select);
cb.setOnCheckedChangeListener(this);
}

//CompoundButton
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
String desc = String.format("您%s这个Checkbox",isChecked?"勾选":"取消");
buttonView.setText(desc);
}
}

视图—》文本视图 —》按钮 —》复合按钮 ===》复选框、单选按钮、开关按钮

XML中CompoundButton

  • checked;指定按钮的勾选状态
  • button:指定左侧勾选图标的图像资源

Java代码中CompoundButton

  • setChecked设置按钮的勾选状态
  • setButtonDrawable设置左侧勾选图标的图像资源
  • setOnCheckedChangeListener设置勾选状态变化的监听器
  • isChecked判断按钮是否勾选

5.5Switch

Switch是开关按钮,它在选中和取消选中时可展现的界面元素比复选框丰富

textOn:打开时的文本

textOff:设置左侧关闭的文本

track:设置开关轨道的背景

thumb:设置开关表示的图标

1
2
3
4
5
6
7
<Switch
android:id="@+id/select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center"
android:text="switch"
/>
1
2
3
4
5
6
7
8
9
10
11
<!-- 使用checkBox实现Switch开关按钮 -->   
<CheckBox
android:id="@+id/select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center"
android:text="switch"
//设置按钮为空
android:button="@null"
android:background="@drawable/ic_launcher_background"
/>

5.6单选按钮

一组中只能选择一个

RadioButton,放入一个RadioGroup组内部

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提示信息"
android:layout_gravity="center"/>
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/male"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="man"/>

<RadioButton
android:id="@+id/female"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="woman"/>
</RadioGroup>

监听单选按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MainActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {

private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RadioGroup radioGroup = findViewById(R.id.gender);
tv = findViewById(R.id.show);
radioGroup.setOnCheckedChangeListener(this);
}

@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId == R.id.male){
tv.setText("male");
}else if (checkedId == R.id.female){
tv.setText("female");
}
}
}

5.7文本输入框

1
2
3
4
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="输入登录信息"/>

去掉默认边框

1
2
3
4
5
6
7
<EditText
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="输入密码信息"
android:background="@null"
android:hint="自定义"
android:inputType="text"/>

inputType:指定输入的文本类型

允许输入的最大长度

hint:指定提示文本的内容

textColorHint指示文本的颜色

输入类型 说明
text 文本
textPassword 文本密码
number 整数型
numberSigned 带符号的数字
numberDecimal 带小数点的数字
numberpassword 数字密码
datetime 时间日期格式
date 日期格式
time 事件格式

5.8焦点变更监听器

Toast.makeText(this,”请输入8位密码”,Toast.LENGTH_SHORT).show();

//弹出提示

LENGTH_SHORT

LENGTH_LONG 持续时间更长

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MainActivity2 extends AppCompatActivity implements View.OnFocusChangeListener {
private EditText et_message;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
et_message = findViewById(R.id.message);
EditText et_password = findViewById(R.id.password);
et_password.setOnFocusChangeListener(this);
}

@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus){
String password = et_message.getText().toString();
if (TextUtils.isEmpty(password) || password.length() < 8){
et_message.requestFocus();
Toast.makeText(this,"请输入8位用户名",Toast.LENGTH_SHORT).show();
}
}
}
}

5.9文本变化监听器

调用编辑框对象的addTextChangelistener方法即可注册文本监听器

文本监听器的接口名位TextWatcher,该接口提供了3个监控方法

  • beforeTextChanged
  • OnTextChanged
  • afterTextChanged

隐藏输入框

1
2
3
4
5
6
7
8
public class hidenView {
public static void HidenInput(Activity ac, View v){
//从系统获取输入法管理器
InputMethodManager imm = (InputMethodManager) ac.getSystemService(Context.INPUT_METHOD_SERVICE);
//关闭输入法 得到窗口管理器
imm.hideSoftInputFromWindow(v.getWindowToken(),0);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class MainActivity2 extends AppCompatActivity{
private EditText et_password;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);

et_password = findViewById(R.id.password);
et_password.addTextChangedListener(new HidenTextWatcher(et_password,11));
}

//定义,当达到指定值后隐藏文本框
private class HidenTextWatcher implements TextWatcher{
private EditText et;
private int maxLength;
public HidenTextWatcher(EditText et,int maxlength){
this.et = et;
this.maxLength = maxlength;
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {

}

@Override
public void afterTextChanged(Editable s) {
String test = s.toString();
if (test.length() == 11 && maxLength==11){
hidenView.HidenInput(MainActivity2.this,et_password);
}
}
}
}

5.10提醒对话框

AlertDialog

借助建造器AlertDialog.Builder才能完成设置

调用建造器的create方法生产对话框实例,再调用对话框实例的show方法,在页面提示对话框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class MainActivity3 extends AppCompatActivity implements View.OnClickListener {

private TextView tv_dialog;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
findViewById(R.id.btn_diolog).setOnClickListener(this);
tv_dialog = findViewById(R.id.diolog_view);
}

@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("尊敬的用户");
builder.setMessage("是否卸载");
builder.setPositiveButton("取消",((dialog, which) -> {
tv_dialog.setText("没有卸载");
}));
builder.setNegativeButton("确认",((dialog, which) -> {
tv_dialog.setText("卸载成功");
}));
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
}

setIcon:设置图标

setTitle设置对话框的标题文本

setMessage设置对话框的文本

setPositiveButton设置肯定按钮

setNegativeButton 设置否定按钮

setNeutralButton设置中性按钮

5.11日期选择器

1
2
3
4
5
6
<DatePicker
android:id="@+id/dp_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:calendarViewShown="false"
android:datePickerMode="spinner" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
findViewById(R.id.dp_btn).setOnClickListener(this);
dp = findViewById(R.id.dp_date);
dp_show =findViewById(R.id.dp_dateshow);
}

@Override
public void onClick(View v) {
if (v.getId() == R.id.dp_btn){
String format = String.format("日期是%d年%d月%d日",dp.getYear(),dp.getMonth()+1,dp.getDayOfMonth());
dp_show.setText(format);
}
}

新建一个Calender实例获取时间

1
2
3
4
Calendar calendar = Calendar.getInstance();
calendar.get(Calendar.YEAR);
calendar.get(Calendar.MONTH);
calendar.get(Calendar.DAY_OF_MONTH);

DatePickDiolog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    @Override
public void onClick(View v) {
if (v.getId() == R.id.dp_btn){
/* String format = String.format("日期是%d年%d月%d日",dp.getYear(),dp.getMonth()+1,dp.getDayOfMonth());
dp_show.setText(format);*/
/* Calendar calendar = Calendar.getInstance();
calendar.get(Calendar.YEAR);
calendar.get(Calendar.MONTH);
calendar.get(Calendar.DAY_OF_MONTH);*/
DatePickerDialog dialog = new DatePickerDialog(this,this,2090,5,11);
dialog.show();
}
}

@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
String format = String.format("日期是%d年%d月%d日",year,month+1,dayOfMonth);
dp_show.setText(format);
}

5.12时间选择器

TimePickDialog

TimePicker类似DatePicker,可以选择具体的小时

TimePickDiolog的用法类似DatePickDiolog

1
2
3
TimePickDialog dialog = new TimePickDialog(this,[android.R.style.Theme.Holo_Light_dialog    ],this,calendar.get(Calendar.HOUR_OF_DAY),calender.get(Calendar.MINUTE),true)
//android.R.style.Theme.Holo_Light_dialog 设置为横向选择样式
//true 为是否开启24小时制

5.13小案例

页面布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<RadioGroup
android:id="@+id/rg_login"
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">

<RadioButton
android:id="@+id/rg_withpwd"
android:checked="true"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/login_withpwd"
android:textSize="@dimen/item_line_height"
/>

<RadioButton
android:id="@+id/withoutpwd"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/login_withoutpwd"
android:textSize="@dimen/item_line_height"/>
</RadioGroup>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">

<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/phonenumber"
android:gravity="center"
android:textColor="@color/black"
android:textSize="@dimen/item_line_height"/>

<EditText
android:id="@+id/et_phone"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
android:background="@drawable/shape"
android:hint="@string/inputnumber"
android:maxLength="11"
android:textColor="@color/black"
android:inputType="number"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">

<TextView
android:id="@+id/tv_password"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/loginpassword"
android:gravity="center"
android:textColor="@color/black"
android:textSize="@dimen/item_line_height"/>



<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">

<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
android:background="@drawable/shape"
android:hint="@string/inputpassword"
android:maxLength="6"
android:textColor="@color/black"
android:inputType="numberPassword"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>

<Button
android:id="@+id/btn_password"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/forgetpassword"
android:layout_alignParentEnd="true"
android:background="@color/gray"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
</RelativeLayout>

</LinearLayout>
<CheckBox
android:id="@+id/check_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/remenumber"/>

<Button
android:id="@+id/beginlogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login"
android:textSize="@dimen/item_line_height"
android:textColor="@color/gray"/>
</LinearLayout>

代码逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
public class MainActivity5 extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener, View.OnClickListener {

private TextView tv_password;
private EditText et_password;
private Button btn_password;

private CheckBox checkBox;

private RadioButton withpwd;
RadioButton withoutpwd;
private ActivityResultLauncher<Intent> register;
private EditText et_phone;
private Button btn_login;

private String clearPassword="111111";
private String verifycode;

@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main5);
RadioGroup group= findViewById(R.id.rg_login);
group.setOnCheckedChangeListener(this);
tv_password = findViewById(R.id.tv_password);
et_password = findViewById(R.id.et_password);
btn_password = findViewById(R.id.btn_password);
btn_password.setOnClickListener(this);
checkBox = findViewById(R.id.check_number);
et_phone = findViewById(R.id.et_phone);
withpwd = findViewById(R.id.rg_withpwd);
withoutpwd = findViewById(R.id.withoutpwd);
btn_login = findViewById(R.id.beginlogin);
btn_login.setOnClickListener(this);
et_phone.addTextChangedListener(new HideTextWatcher(et_phone,11));
et_password.addTextChangedListener(new HideTextWatcher(et_password,6));
register = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult o) {
Intent intent = o.getData();
if (intent != null && o.getResultCode()== Activity.RESULT_OK){
String newpassword = intent.getStringExtra("password");
clearPassword = newpassword;
}
}
});
}

@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId==R.id.rg_withpwd){
tv_password.setText(getString(R.string.loginpassword));
et_password.setHint(getString(R.string.inputpassword));
btn_password.setText(getString(R.string.forgetpassword));
checkBox.setVisibility(View.VISIBLE);
}else if (checkedId == R.id.withoutpwd){
tv_password.setText(getString(R.string.verifycode));
et_password.setHint(getString(R.string.input_verifycode));
btn_password.setText(getString(R.string.getverifycode));
checkBox.setVisibility(View.GONE);
}
}

@Override
public void onClick(View v) {
String phone = et_phone.getText().toString();
if (v.getId() == R.id.btn_password){
if (withpwd.isChecked()){
Intent intent = new Intent(this, MainActivity7.class);
intent.putExtra("phone",phone);
register.launch(intent);
}else if (withoutpwd.isChecked()){
verifycode = String.format("%06d",new Random().nextInt(999999));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("验证码");
builder.setMessage(verifycode);
builder.setPositiveButton("确认",null);
AlertDialog dialog = builder.create();
dialog.show();
}
}else if (v.getId() == R.id.beginlogin){
String password = et_password.getText().toString();
if (withpwd.isChecked()){
if (!password.equals(clearPassword)){
Toast.makeText(this,"请输入正确的密码",Toast.LENGTH_SHORT).show();
return;
}
LoinSuccess();
}else if (withoutpwd.isChecked()){
if (!password.equals(verifycode)){
Toast.makeText(this,"请输入正确的验证码",Toast.LENGTH_SHORT).show();
return;
}
LoinSuccess();
}
}
}

private void LoinSuccess() {
String mes = "登录成功";
AlertDialog.Builder builder= new AlertDialog.Builder(this);
builder.setTitle("登录成功");
builder.setMessage(mes);
builder.setPositiveButton("确认返回",((dialog, which) -> {
finish();
}));
builder.setNegativeButton("我再看看",null);
Dialog dialog = builder.create();
dialog.show();
}

private class HideTextWatcher implements TextWatcher {
private EditText et;

private int maxlength;

public HideTextWatcher(EditText et_phone,int maxlength){
this.et = et_phone;
this.maxlength = maxlength;
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {

}

@Override
public void afterTextChanged(Editable s) {
if (s.toString().length() == maxlength ){
hidenView.HidenInput(MainActivity5.this,et);
}
}
}
}

找回密码布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">

<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/newpassword"
android:gravity="center"
android:textColor="@color/black"
android:textSize="@dimen/item_line_height"/>

<EditText
android:id="@+id/new_password"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
android:background="@drawable/shape"
android:hint="@string/inputnewpassword"
android:maxLength="6"
android:textColor="@color/black"
android:inputType="number"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">

<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/newpasswordagain"
android:gravity="center"
android:textColor="@color/black"
android:textSize="@dimen/item_line_height"/>

<EditText
android:id="@+id/new_passwordagain"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
android:background="@drawable/shape"
android:hint="@string/inputnewpasswordagain"
android:maxLength="6"
android:textColor="@color/black"
android:inputType="number"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">

<TextView
android:id="@+id/tv_newverify"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/verifycode"
android:gravity="center"
android:textColor="@color/black"
android:textSize="@dimen/item_line_height"/>



<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">

<EditText
android:id="@+id/et_newverify"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
android:background="@drawable/shape"
android:hint="@string/input_verifycode"
android:maxLength="6"
android:textColor="@color/black"
android:inputType="numberPassword"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>

<Button
android:id="@+id/btn_newverify"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/getverifycode"
android:layout_alignParentEnd="true"
android:background="@color/gray"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
</RelativeLayout>

</LinearLayout>
<Button
android:id="@+id/beginnewpassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/click"
android:textColor="@color/gray"
android:textSize="@dimen/item_line_height"/>



</LinearLayout>

找回密码逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class MainActivity7 extends AppCompatActivity implements View.OnClickListener {

private String phone;
private String verifycode;
private EditText tv_newpassword;
private EditText tv_newpasswordagin;
private EditText et_newverify;

@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main7);
phone = getIntent().getStringExtra("phone");
findViewById(R.id.btn_newverify).setOnClickListener(this);
findViewById(R.id.beginnewpassword).setOnClickListener(this);
tv_newpassword = findViewById(R.id.new_password);
tv_newpasswordagin = findViewById(R.id.new_passwordagain);
et_newverify = findViewById(R.id.et_newverify);
}

@Override
public void onClick(View v) {
if (v.getId()==R.id.btn_newverify){
verifycode = String.format("%06d",new Random().nextInt(999999));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("验证码");
builder.setMessage(verifycode);
builder.setPositiveButton("确认",null);
AlertDialog dialog = builder.create();
dialog.show();
}else if (v.getId() == R.id.beginnewpassword){
String passwd = tv_newpassword.getText().toString();
String newpasswd = tv_newpasswordagin.getText().toString();
String mycode = et_newverify.getText().toString();
if (passwd.length()<6 || newpasswd.length()<6){
Toast.makeText(this,"密码长度不合适",Toast.LENGTH_SHORT).show();
return;
}
if (!passwd.equals(newpasswd)){
Toast.makeText(this,"两次密码不一致",Toast.LENGTH_SHORT).show();
return;
}
if (!mycode.equals(verifycode)){
Toast.makeText(this,"验证码错误",Toast.LENGTH_SHORT).show();
return;
}
Intent intent = new Intent();
intent.putExtra("password",passwd);
setResult(Activity.RESULT_OK,intent);
finish();
}
}
}

6.SharePreferences

SharePreferences是Android的一个轻量级存储工具,采用键值对的方式

/data/data/应用包名/shared_prefs/文件名.xml

App个性化配置信息,用户使用App的行为信息、临时需要保存的片段信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private EditText et_name;
private EditText et_age;
private EditText et_height;
private EditText et_weight;
private SharedPreferences preferences;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_name = findViewById(R.id.et_name);
et_age = findViewById(R.id.et_age);
et_height = findViewById(R.id.et_height);
et_weight = findViewById(R.id.et_weight);

findViewById(R.id.submit).setOnClickListener(this);

preferences = getSharedPreferences("config", Context.MODE_PRIVATE);
}

@Override
public void onClick(View v) {
String name = et_name.getText().toString();
String age = et_age.getText().toString();
String height = et_height.getText().toString();
String weight = et_weight.getText().toString();
SharedPreferences.Editor ediit = preferences.edit();
ediit.putString("name",name);
ediit.putInt("age",Integer.parseInt(age));
ediit.putFloat("height",Float.parseFloat(height));
ediit.putFloat("weight",Float.parseFloat(weight));
ediit.apply();
}
}

data/data/包名/shared_prefs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void reload() {
//取数据
String name = preferences.getString("name",null);
if (name!=null){
et_name.setText(name);
}
int age = preferences.getInt("age",0);
if (age !=0){
et_age.setText(String.valueOf(age));
}
float height = preferences.getFloat("height",0f);
if (height !=0f){
et_height.setText(String.valueOf(height));
}
float weight = preferences.getFloat("weight",0f);
if (height !=0f){
et_weight.setText(String.valueOf(weight));
}
}

7.SQLLite

7.1.管理类

openDatabase:打开指定路径的数据库

isopen:判断数据库是否打开

close:关闭数据库

getVersion:获取数据库的般般号

setVersion:设置数据库的版本号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MainActivity8 extends AppCompatActivity implements View.OnClickListener {

private String mDatabase;
private TextView tv_database;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main8);
findViewById(R.id.createdatabase).setOnClickListener(this);
findViewById(R.id.deletedatabase).setOnClickListener(this);
//数据库的路径
mDatabase = getFilesDir() + "/test.db";
tv_database = findViewById(R.id.tv_view);
}

@Override
public void onClick(View v) {
if (v.getId() == R.id.createdatabase){
SQLiteDatabase sqLiteDatabase = openOrCreateDatabase(mDatabase, Context.MODE_PRIVATE, null);
String desc = String.format("%s创建%s",sqLiteDatabase.getPath(), "成功");
tv_database.setText(desc);
}else if(v.getId() == R.id.deletedatabase) {
boolean result = deleteDatabase(mDatabase);
String desc = String.format("%s删除%s",mDatabase,result ? "成功" : "失败");
tv_database.setText(desc);
}
}
}

SQLiteOPenHeaper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class UserDBHelper extends SQLiteOpenHelper {

private static final String DB_NAME = "user.db";
private static final int DB_VSERSION = 1;

private static final String TABLE_NAME = "user_info";
public static UserDBHelper mhelper = null;
private SQLiteDatabase mRdb = null;

private SQLiteDatabase mWdb = null;
private UserDBHelper(Context context){
super(context,DB_NAME,null,DB_VSERSION);
}

//利用单例模式获取数据库帮助器的唯一实例
public static UserDBHelper getInstance(Context context){
if (mhelper == null){
mhelper = new UserDBHelper(context);
}
return mhelper;
}
//打开数据库的读连接
public SQLiteDatabase openReadLink(){
if (mRdb == null || mRdb.isOpen()){
mRdb = mhelper.getReadableDatabase();
}
return mRdb;
}
//打开数据库的写连接
public SQLiteDatabase openWriteLink(){
if (mWdb == null || mWdb.isOpen()){
mWdb = mhelper.getWritableDatabase();
}
return mWdb;
}
//关闭数据库
public void closeLink(){
if (mRdb != null && mRdb.isOpen()){
mRdb.close();
mRdb = null;
}
if (mWdb != null && mWdb.isOpen()){
mWdb.close();
mWdb = null;
}
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "create table if not exists user_info(_id integer primary key autoincrement not null,"
+"name varchar not null"+")";
db.execSQL(sql);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

}

}

增删改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//增
public long insert(user user){
ContentValues values = new ContentValues();
values.put("name",user.name);
return mWdb.insert(TABLE_NAME,null,values);
}
//删
public long delete(String name){
return mWdb.delete(TABLE_NAME,"name=?",new String[]{name});
}
//改
public long update(user user){
ContentValues contentValues = new ContentValues();
contentValues.put("name",user.name);
return mWdb.update(TABLE_NAME,contentValues,"name=?",new String[]{user.name});
}

7.2.事务类

beginTransaction:开始事务

setTransactionSuccessful:设置事务成功的标志

endTransaction:结束事务

7.3.数据库版本更新

1
2
3
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

}

7.4.外部存储和内部存储,外部SD卡

读写文件操作类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class FileUtil {
public static void saveText(String path,String txt) throws IOException {
BufferedWriter os = null;
try{
os = new BufferedWriter(new FileWriter(path));
} catch (IOException e) {
e.printStackTrace();
}finally {
if (os!=null){
os.close();
}
}
}

public static String openText(String path) throws IOException {
BufferedReader rs = null;
StringBuffer sb = new StringBuffer();
try{
rs = new BufferedReader(new FileReader(path));
String line = null;
while((line=rs.readLine())!=null){
sb.append(line);
}
}catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if (rs != null) {
rs.close();
}
}
return rs.toString();
}
}

获取外部存储的私有空间

1
String path  = directory+ File.separatorChar+fileName;

获取外部存储公共空间

1
2
//获取外部公共空间
directory=Environment.getExternalStorageState();

内部存储私有空间

1
2
//内部存储私有空间
directory = getFilesDir().toString();

7.5.在存储卡读写图片文件

Bitmap

BitmaoFactory读写各种来源的图片

decodeResource 从资源文件中读取图片信息

decodeFile 指定路径的图片获取到bitmap对象

decodeStream从输入流获取位图数据

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void onClick(View v) {
if (v.getId() == R.id. create){
String fileName = System.currentTimeMillis() + ".png";
path = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString()+ File.separatorChar+fileName;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.gl);
FileUtil.saveImage(path,bitmap);
}else if (v.getId() == R.id.delete){
Bitmap bitmap = FileUtil.openImage(path);
im.setImageBitmap(bitmap);
}
}

文件操作FileUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static void saveImage(String path, Bitmap bitmap) {
FileOutputStream fos = null;
try{
fos = new FileOutputStream(path);
bitmap.compress(Bitmap.CompressFormat.PNG,100,fos);
}catch (Exception e){
e.printStackTrace();
}finally {
if (fos != null){
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

public static Bitmap openImage(String path) {
Bitmap bitmap = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(path);
bitmap = decodeStream(fis);
}catch (Exception e){
e.printStackTrace();
}finally {
if (fis != null){
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return bitmap;
}

8.Application

8.1Application

Application是Android的一大组件,在App运行过程中有且仅有一个Application对象贯穿整个生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Application extends android.app.Application {
@Override
public void onCreate() {
super.onCreate();
Log.d("richu","onCreate");
}

//Andriod设备上不会执行
@Override
public void onTerminate() {
super.onTerminate();
Log.d("richu","onTerminate");
}

//配置改变时
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.d("richu","onConfigure");
}
}

8.2全局变量

适用:会频繁读取的信息,用户名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Application extends android.app.Application {
private static Application application;

@Override
public void onCreate() {
super.onCreate();
}

public static Application getinstance(){
return application;
}

//Andriod设备上不会执行
@Override
public void onTerminate() {
super.onTerminate();
application = this;
Log.d("richu","onTerminate");
}

public HashMap<String,String> infoMap= new HashMap<>();

//配置改变时
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.d("richu","onConfigure");
}
}

1
2
application.infoMap.put("name","richu");
name = application.infoMap.get("name");

9.Room

1
2
3
implementation ("androidx.room:room-runtime:2.2.6")
implementation ("androidx.room:room-ktx:2.2.6")
testImplementation ("androidx.room:room-testing:2.2.6")

数据实体 Entry 表示应用的数据库中的表。数据实体用于更新表中的行所存储的数据以及创建新行供插入。

数据访问对象 (DAO) 提供应用在数据库中检索、更新、插入和删除数据所用的方法。

数据库类持有数据库,并且是应用数据库底层连接的主要访问点。数据库类为应用提供与该数据库关联的 DAO 的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Dao
public interface BookDao {
@Insert()
void insert(Books... books);

@Delete
void delete(Books... books);

@Update
int update(Books... books);

@Query("select * from Books")
List<Books> queryAll();

@Query("select * from Books where id= :id")
Books queryById(int id);
}

1
2
3
4
5
//entities表示有哪些表  version为版本号 exportSchema表示是否导出数据库的json串
@Database(entities = {Books.class},version = 1,exportSchema = false)
public abstract class BookDatabase extends RoomDatabase {

}

在自定义的Appication创建BookDatabase实例

1
2
3
4
5
bookDatabase = Room.databaseBuilder(this,BookDatabase.class,"book").
//允许迁移数据库
addMigrations().
//允许主线程中操作数据库
allowMainThreadQueries().build();

10.ContentProvider

10.1.service端代码 编写

为App存储内部数据提供统一的外部接口,让不同的外部应用之间得以共享数据

1
2
3
4
5
<provider
android:name=".provider.UserinfoProvider"
android:authorities="com.richuff.server.provider.UserinfoProvider"
android:enabled="true"
android:exported="true" />

uri 通用资源标识符 代表数据操作的地址 每一个ContentProvider都有一个唯一的值。ContentProvider使用的Uri的语法结构如下

在 Android 中,ContentProvider使用的Uri(统一资源标识符)有一个标准的语法结构。一般形式为content://authority/path

conten://是一个固定的前缀,用于表明这是一个内容提供者(ContentProvider)相关的Uri,所有通过ContentResolver访问ContentProviderUri都以这个前缀开头。

关键部分之一,它用于唯一标识一个ContentProvider。通常,authority是一个字符串,格式类似于一个包名或者反向域名(例如com.example.app.provider)。在AndroidManifest.xml文件中,当注册ContentProvider时,android:authorities属性的值就是这个authority。

1
2
3
4
<provider
android:name=".MyContentProvider"
android:authorities="com.example.myapp.provider"
android:exported="true"/>

应用通过这个authority来定位要访问的ContentProvider。不同的ContentProvider应该有不同的authority,以避免冲突。

path是可选的,它用于在一个ContentProvider内部进一步划分资源或者数据集合。例如,一个ContentProvider可能管理用户信息和订单信息,那么可以通过不同的path来区分访问用户信息(如content://com.example.myapp.provider/users)和访问订单信息(如content://com.example.myapp.provider/orders)。

path可以包含多层路径,例如content://com.example.myapp.provider/users/profile,可以用于更精细地划分数据访问路径,这里可能表示访问用户信息中的用户资料部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class UserinfoProvider extends ContentProvider {
private UserinfoDatabase userinfoDatabase;
public UserinfoProvider() {

}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Not yet implemented");
}

@Override
public String getType(Uri uri) {
throw new UnsupportedOperationException("Not yet implemented");
}

//添加
@Override
public Uri insert(Uri uri, ContentValues values) {
if ("/user".equals(uri.getPath())) {
SQLiteDatabase db = userinfoDatabase.getWritableDatabase();
db.insert(UserinfoDatabase.TABLE_NAME, null, values);
}
return uri;
}

@Override
public boolean onCreate() {
userinfoDatabase = UserinfoDatabase.getInstance(getContext());
return true;
}

//查询
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = userinfoDatabase.getReadableDatabase();
return db.query(UserinfoDatabase.TABLE_NAME, projection, selection, selectionArgs, null, null, null);
}

@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
throw new UnsupportedOperationException("Not yet implemented");
}
}

10.2.client端代码

通过ContentResolver访问数据

1
2
3
4
5
ContentValues values = new ContentValues();
values.put(UserinfoContent.PASSWORD,"test");
values.put(UserinfoContent.PHONE,"11111");
values.put(UserinfoContent.ISCHECKED,1);
getContentResolver().insert(UserinfoContent.CONTENT_URI,values);

使用getContentResolver()获取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public void onClick(View v) {
if (v.getId() == R.id.read){
//查询
Cursor cursor = getContentResolver().query(UserinfoContent.CONTENT_URI,null,null,null,null,null);
if (cursor != null){
while (cursor.moveToNext()){
//接收数据
User info = new User();
info.id = cursor.getInt(cursor.getColumnIndex(UserinfoContent._ID));
info.name = cursor.getString(cursor.getColumnIndex(UserinfoContent.NAME));

info.height = cursor.getLong(cursor.getColumnIndex(UserinfoContent.HEIGHT));
info.age = cursor.getInt(cursor.getColumnIndex(UserinfoContent.AGE));
info.weight = cursor.getFloat(cursor.getColumnIndex(UserinfoContent.WEIGHT));

Log.d("mytag", info.toString());
}
//关闭cursor
cursor.close();
}

}else if (v.getId() == R.id.write){
//保存
ContentValues values = new ContentValues();
values.put(UserinfoContent.NAME,name.getText().toString());
values.put(UserinfoContent.AGE,age.getText().toString());
values.put(UserinfoContent.HEIGHT,height.getText().toString());
values.put(UserinfoContent.WEIGHT,weight.getText().toString());
//插入数据
getContentResolver().insert(UserinfoContent.CONTENT_URI,values);
//弹窗提示
Toastshow.show(this,"保存成功");
}
}

在android11之后要提前声明
AndroidManifest.xml文件中声明需要访问的其他软件包

1
2
3
<queries>
<provider android:authorities="com.richuff.server.provider.UserinfoProvider"/>
</queries>

提供contentprovider的uri

1
2
3
4
5
6
7
8
9
10
11
public class UserinfoContent {
public static final String AUTHORITIES = "com.richuff.server.provider.UserinfoProvider"; //服务端的具体类名
//访问内容器的URI
public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITIES+"/user");

public static final String HEIGHT = "height";
public static final String NAME = "name";

public static final String WEIGHT = "weight";
public static final String AGE = "age";
}

10.3.进行数据的删除

在provider中修改delete函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
//删除多行
if (URI_MATCHER.match(uri) == USERS){
SQLiteDatabase db = UserinfoDatabase.mhelper.getWritableDatabase();
count = db.delete(UserinfoDatabase.TABLE_NAME,selection,selectionArgs);
db.close();
}
//删除单行
else if (URI_MATCHER.match(uri) == USER) {
String id = uri.getLastPathSegment();
SQLiteDatabase db = UserinfoDatabase.mhelper.getWritableDatabase();
count = db.delete(UserinfoDatabase.TABLE_NAME,"_id=?",new String[]{id});
db.close();
}
return count;
}

使用provider

1
2
3
4
5
6
7
8
9
10
11
//删除多行
int count = getContentResolver().delete(UserinfoContent.CONTENT_URI, "name=?", new String[]{"rc"});
if (count > 0){
Toastshow.show(this,"删除成功");
}
//删除单行
Uri uri = ContentUris.withAppendedId(UserinfoContent.CONTENT_URI,2); //在uri后面附加一个/2
int count = getContentResolver().delete(uri, "name=?", new String[]{"rc"});
if (count > 0){
Toastshow.show(this,"删除成功");
}

在provider中选择路径匹配

1
2
3
4
5
6
7
8
9
private static final UriMatcher URI_MATCHER= new UriMatcher(UriMatcher.NO_MATCH);

private static final int USERS = 1;
private static final int USER = 2;

static {
URI_MATCHER.addURI(UserinfoContent.AUTHORITIES,"/user",USERS);
URI_MATCHER.addURI(UserinfoContent.AUTHORITIES,"/user/#",USER);
}

11.运行时动态申请权限

提前声明需要的权限

1
2
3
4
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.SEND_SMS"/>

1.检查App是否开启了某个权限

1
2
3
4
5
6
7
8
9
10
11
12
13
private static final String[] PERMISSION_CONTACTS = new String[]{
Manifest.permission.READ_CONTACTS,
Manifest.permission.WRITE_CONTACTS
};

private static final String[] PERMISSION_SMS = new String[]{
Manifest.permission.READ_SMS,
Manifest.permission.SEND_SMS
};
private static final int REQUEST_CODE_CONTACTS = 1;
private static final int REQUEST_CODE_SMS = 2;


2.请求系统弹窗,以便用户选择是否开启权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static boolean checkPermission(Activity act,String[] permissions,int requestCode){
int check = PackageManager.PERMISSION_GRANTED;
for (String permission:permissions){
check = ContextCompat.checkSelfPermission(act,permission);
if (check != PackageManager.PERMISSION_GRANTED){
break;
}
}
if (check != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(act, permissions, requestCode);
return false;
}

return true;
}

3.判断用户的权限选择结果

1
2
3
4
5
6
7
8
9
10
11
public static boolean checkGrant(int[] grantResults) {
if (grantResults != null) {
for (int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
return false;
}

跳转到setting页面

1
2
3
4
5
6
7
public void goToSetting(){
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package",getPackageName(),null));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}

12.通讯录

12.1.添加联系人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private void addContact(ContentResolver contentResolver, Contact contact) {
ContentValues values = new ContentValues();

Uri uri = contentResolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
long rawContentID = ContentUris.parseId(uri);
ContentValues name = new ContentValues();
//关联联系人编号
name.put(ContactsContract.Data.RAW_CONTACT_ID,rawContentID);
//姓名的数据类型
name.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
//添加的类型 1表示家庭 2表示工作
name.put(ContactsContract.Data.DATA2,contact.name);
//联系人的姓名
contentResolver.insert(ContactsContract.Data.CONTENT_URI,name);

ContentValues phone = new ContentValues();
//关联联系人编号
phone.put(ContactsContract.Data.RAW_CONTACT_ID,rawContentID);
//姓名的数据类型
phone.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
//添加的类型 1表示家庭 2表示工作
phone.put(ContactsContract.Data.DATA1,contact.phone);
phone.put(ContactsContract.Data.DATA2,ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
//联系人的手机号
contentResolver.insert(ContactsContract.Data.CONTENT_URI,phone);

ContentValues email = new ContentValues();
//关联联系人编号
email.put(ContactsContract.Data.RAW_CONTACT_ID,rawContentID);
//姓名的数据类型
email.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
//添加的类型 1表示家庭 2表示工作
email.put(ContactsContract.Data.DATA1,contact.email);
email.put(ContactsContract.Data.DATA2,ContactsContract.CommonDataKinds.Email.TYPE_WORK);
//联系人的手机号
contentResolver.insert(ContactsContract.Data.CONTENT_URI,email);
}

12.2.批量添加联系人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public void addFullContacts(ContentResolver contentResolver, Contact contact){
ContentProviderOperation op_main = ContentProviderOperation.
newInsert(ContactsContract.Data.CONTENT_URI).withValue(ContactsContract.RawContacts.ACCOUNT_NAME,null).
build();

ContentProviderOperation op_name = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).
//将第0个操作的id,即raw_contracts的id作为data表中的raw_contract_id
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID,0).
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.Data.DATA2,contact.name)
.build();

ContentProviderOperation op_phone = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).
//将第0个操作的id,即raw_contracts的id作为data表中的raw_contract_id
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID,0).
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.Data.DATA1,contact.phone)
.withValue(ContactsContract.Data.DATA2,ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
.build();

ContentProviderOperation op_email = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).
//将第0个操作的id,即raw_contracts的id作为data表中的raw_contract_id
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID,0).
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.Data.DATA1,contact.email)
.withValue(ContactsContract.Data.DATA2,ContactsContract.CommonDataKinds.Email.TYPE_WORK)
.build();

ArrayList<ContentProviderOperation> operations = new ArrayList<>();
operations.add(op_main);
operations.add(op_name);
operations.add(op_phone);
operations.add(op_email);

try {
contentResolver.applyBatch(ContactsContract.AUTHORITY,operations);
} catch (OperationApplicationException e) {
throw new RuntimeException(e);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}

12.3.查询联系人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@SuppressLint("Range")
public void readPhoneContacts(ContentResolver resolver){
Cursor cursor = resolver.query(ContactsContract.RawContacts.CONTENT_URI, new String[]{ContactsContract.RawContacts._ID}, null, null,null);
while (cursor.moveToNext()){
int rawContactId = cursor.getInt(0);
Uri uri = Uri.parse("content://com.android.contacts/contacts/" + rawContactId + "/dataa");
Cursor query = resolver.query(uri, new String[]{ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2}, null, null, null);

Contact contact = new Contact();
while (query.moveToNext()){
String data1 = query.getString(query.getColumnIndex(ContactsContract.Data.DATA1));
String mimeType = query.getString(query.getColumnIndex(ContactsContract.Data.MIMETYPE));
//查询姓名
if (mimeType.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)){
contact.name = data1;
}//查询电话号
else if (mimeType.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)){
contact.phone = data1;
}//查询邮箱
else if (mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)){
contact.email = data1;
}
}
query.close();
}
cursor.close();
}

13.短信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class MainActivity4 extends AppCompatActivity {

public SmsGetObserver mObserver;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main4);

//给指定的uri注册内容监听器,一旦发生数据变化,就触发观察器的onChange方法
Uri uri = Uri.parse("content://sms");
mObserver = new SmsGetObserver(this);
//notifyForDescendants为false表示uri的精确匹配,为true表示可以匹配其派生路径
getContentResolver().registerContentObserver(uri,true,mObserver);
}

@Override
protected void onDestroy() {
super.onDestroy();
getContentResolver().unregisterContentObserver(mObserver);
}

private static class SmsGetObserver extends ContentObserver {

private final Context mContext;
public SmsGetObserver(Context context) {
super(new Handler(Looper.getMainLooper()));
this.mContext = context;
}

@SuppressLint("Range")
@Override
public void onChange(boolean selfChange, @Nullable Uri uri) {
super.onChange(selfChange, uri);

if (uri == null) {
return;
}
if (uri.toString().contains("content://sms/raw") || uri.toString().equals("content://sms")){
return;
}
Cursor cursor = mContext.getContentResolver().query(uri, new String[]{"address", "body", "date"}, null, null, "date DESC");
if (cursor.moveToNext()){
String sender = cursor.getString(cursor.getColumnIndex("address"));
String content = cursor.getString(cursor.getColumnIndex("body"));

Log.d("mytag", String.format("sender:%s,content:%s",sender,content));
}
cursor.close();
}
}
}

选择图片并且显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class MainActivity5 extends AppCompatActivity implements View.OnClickListener {
private ImageView view;
private ActivityResultLauncher<Intent> register;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main5);

view = findViewById(R.id.e_image);
view.setOnClickListener(this);

//跳转到系统相册,并且返回
register = registerForActivityResult(new StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult o) {
if (o.getResultCode() == RESULT_OK){
Intent intent = o.getData();
//选择图片的路径
Uri uri = intent.getData();
if (uri != null){
view.setImageURI(uri);
Log.d("mytag", uri.toString());
}
}
}
});
}

@Override
public void onClick(View v) {
if (v.getId() == R.id.e_image){
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

intent.setType("image/*");
register.launch(intent);
}
}
}

跳转到短信,并且发送短信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class MainActivity5 extends AppCompatActivity implements View.OnClickListener {
private EditText title;
private EditText phone;
private ImageView view;
private EditText content;
private ActivityResultLauncher<Intent> register;

private Uri uri;

@Override
protected void onCreate(Bundle savedInstanceState) {
view = findViewById(R.id.e_image);
view.setOnClickListener(this);
findViewById(R.id.e_send).setOnClickListener(this);

phone = findViewById(R.id.e_phone);
title = findViewById(R.id.e_title);
content = findViewById(R.id.e_content);

@Override
public void onClick(View v) {
if (v.getId() == R.id.e_send) {
sendMsm(phone.getText().toString(),
title.getText().toString(),
content.getText().toString());
}
}

private void sendMsm(String phone, String title, String message) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

//intent的接收者将被准许读取携带的uri数据
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra("address",phone);
intent.putExtra("title",title);
intent.putExtra("sms_body",message);
//图片 文件流
intent.putExtra(Intent.EXTRA_STREAM,uri);
//附件的类型
intent.setType("image/*");
//系统会弹出窗口让你选择
startActivity(intent);
}
}

通过MediaStore查询图片

PermissionUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class PermissionUtil {
public static boolean checkPermission(Activity act,String[] permissions,int requestCode){
int check = PackageManager.PERMISSION_GRANTED;
for (String permission:permissions){
check = ContextCompat.checkSelfPermission(act,permission);
if (check != PackageManager.PERMISSION_GRANTED){
break;
}
}
if (check != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(act, permissions, requestCode);
return false;
}

return true;
}

public static boolean checkGrant(int[] grantResults) {
if (grantResults != null) {
for (int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
return false;
}
}

Activity

权限

1
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class MainActivity6 extends AppCompatActivity {

private static final String[] PERMISSION = new String[]{
Manifest.permission.READ_MEDIA_IMAGES
};

private static final int REQUEST_CODE_ALL = 1;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_ALL && PermissionUtil.checkGrant(grantResults)){
LoadImageList();
ShowImageGrid();
}
}

private ArrayList<ImageInfo> mImageInfoList = new ArrayList<>();
private GridLayout gl;

@Override
protected void onCreate(Bundle savedInstanceState) {
gl = findViewById(R.id.e_grid);
//手动让MediaStore扫描入库
MediaScannerConnection.scanFile(this,new String[]{
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString()
},null,null);

//引发系统弹窗
if (PermissionUtil.checkPermission(this,PERMISSION,REQUEST_CODE_ALL)){
LoadImageList();
ShowImageGrid();
}
}

private void ShowImageGrid(){
gl.removeAllViews();

for (ImageInfo imageInfo:mImageInfoList){
ImageView iv = new ImageView(this);
Bitmap bitmap = BitmapFactory.decodeFile(imageInfo.getPath());
iv.setImageBitmap(bitmap);
iv.setScaleType(ImageView.ScaleType.FIT_CENTER);
int px = diptopx.dip(this,110);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(px,px);
iv.setLayoutParams(layoutParams);
int padding = diptopx.dip(this,5);
iv.setPadding(padding,padding,padding,padding);
iv.setOnClickListener(v->{

});

gl.addView(iv);
}
}

@SuppressLint("Range")
private void LoadImageList() {
String[] column = new String[]{
MediaStore.Images.Media._ID,
MediaStore.Images.Media.TITLE,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.DATA,
};

Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
column,
"_size < 307200", null, "_size DESC"
);
int count = 0;
while (cursor.moveToNext() && count < 6){
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID));
String title = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.TITLE));
long size = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.SIZE));
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));

ImageInfo imageInfo = new ImageInfo(id, title, size, path);
count++;
mImageInfoList.add(imageInfo);
}
}
}

14FileProvider

权限

1
2
3
4
5
6
7
<provider
android:authorities="@string/file_provider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/>
</provider>

xml/file_paths.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_storage_pictures"
path="DCIM/Camera" />
<external-path
name="external_storage_pictures"
path="Pictures" />
</paths>

检测文件是否是指定fileProvider路径下的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static boolean CheckFileUri(Context ctx,String path){
File file = new File(path);
if (!file.exists() || !file.isFile() || file.length() <= 0){
return false;
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
//通过FileProvider获得文件路径
FileProvider.getUriForFile(ctx,ctx.getString(R.string.file_provider),file);
}
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}

15.安装应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class MainActivity7 extends AppCompatActivity implements View.OnClickListener {

//兼容安卓11之前的版本
private static final String[] PERMISSION = new String[]{
Manifest.permission.READ_MEDIA_IMAGES
};

private static final int REQUEST_CODE_ALL = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
findViewById(R.id.down).setOnClickListener(this);
}

@Override
public void onClick(View v) {
if(v.getId() == R.id.down){
//版本兼容
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
CheckAndinstallApk();
}else{
if (PermissionUtil.checkPermission(this,PERMISSION,REQUEST_CODE_ALL)){
installApk();
}
}
}
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_ALL && PermissionUtil.checkGrant(grantResults)){
installApk();
}
}

private void CheckAndinstallApk() {
//检查是否有MANAGE_EXTERNAL_STORAGE权限,没有则跳转到设置页面
if (!Environment.isExternalStorageEmulated()){
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.fromParts("package",getPackageName(),null));
}else {
installApk();
}
}

public void installApk(){
String apkPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/.apk";
Log.d("mytag", "apkPath"+apkPath);
PackageManager pm = getPackageManager();
PackageInfo pi;
try {
pi = pm.getPackageInfo(apkPath, PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
if (pi == null){
Toastshow.show(this,"文件已损坏");
}
Uri uri = Uri.parse(apkPath);

uri = FileProvider.getUriForFile(this,getString(R.string.file_provider),new File(apkPath));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//设置uri的类型为apk文件
intent.setDataAndType(uri,"application/vnd.android.package-archive");
//启动
startActivity(intent);
}
}

需要的权限

1
2
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

16.高级控件

16.1.Spinner

1
2
3
4
5
<Spinner
android:id="@+id/drop_down"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:spinnerMode="dropdown"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private Spinner sp;

private final static String[] startArray = {"水星","火星","金星","木星","木星"};

@SuppressLint("ResourceType")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_test);

sp = findViewById(R.id.drop_down);
sp.setAdapter(new ArrayAdapter<String>(this,R.id.drop_down,startArray));
//默认选择第一项
sp.setSelection(0);
sp.setOnItemClickListener(this);
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toastshow.show(this,"选择"+startArray[position]);
}

16.2.SimpleAdapter

构建每一个条目的基本构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<ImageView
android:src="@drawable/na"
android:id="@+id/tv_im"
android:layout_width="0dp"
android:layout_height="50dp"
android:scaleType="centerCrop"
android:layout_weight="1"/>

<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="图1"
android:textSize="10sp"
android:layout_weight="3"
android:gravity="center"
android:textColor="#ff0000"/>

</LinearLayout>

基本代码逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private int[] iconArray = {R.drawable.na,R.drawable.na,R.drawable.na,R.drawable.na,R.drawable.na};
private String[] nameArray = {"图一","图二","图三","图四","图五"};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main2);

List<Map<String, Object>> list = new ArrayList<>();
for (int i=0;i<iconArray.length;i++){
Map<String,Object> item = new HashMap<>();

item.put("name",nameArray[i]);
item.put("icon",iconArray[i]);
list.add(item);
}
SimpleAdapter startAdapter = new SimpleAdapter(this,list,R.layout.item_simple,
new String[]{"name","icon"},new int[]{R.id.tv_name,R.id.tv_im});
Spinner sp_icon = findViewById(R.id.tv_sp);
sp_icon.setAdapter(startAdapter);
sp_icon.setSelection(0);
sp_icon.setOnItemSelectedListener(this);
}

16.3BaseAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class MyAdapter extends BaseAdapter {
private int[] imageArr;

private String[] nameArr;

private Context mContext;

public MyAdapter(Context context,int[] imageArr,String[] nameArr){
this.mContext = context;
this.imageArr = imageArr;
this.nameArr = nameArr;
}

@Override
public int getCount() {
return imageArr.length;
}

@Override
public Object getItem(int position) {
return imageArr[position];
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = LayoutInflater.from(mContext).inflate(R.layout.item_list, null);
ImageView image = view.findViewById(R.id.ts_im);
TextView maintext = view.findViewById(R.id.ts_name);
TextView childtext = view.findViewById(R.id.ts_chd);

String name = nameArr[position];
int imgsrc = imageArr[position];

image.setImageResource(imgsrc);
maintext.setText(name);
childtext.setText(name+"id");

return view;
}
}

使用

1
2
3
4
5
6
lv =  findViewById(R.id.ts_lv);

MyAdapter startAdapter = new MyAdapter(this,iconArray,nameArray);

lv.setAdapter(startAdapter);
lv.setSelection(0);

进行优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class MyAdapter extends BaseAdapter {
private int[] imageArr;

private String[] nameArr;
private Context mContext;
public MyAdapter(Context context,int[] imageArr,String[] nameArr){
this.mContext = context;
this.imageArr = imageArr;
this.nameArr = nameArr;
}

@Override
public int getCount() {
return imageArr.length;
}

@Override
public Object getItem(int position) {
return imageArr[position];
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = new ViewHolder();
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list, null);
holder.iv_icon = convertView.findViewById(R.id.ts_im);
holder.tv_name = convertView.findViewById(R.id.ts_name);
holder.tv_child = convertView.findViewById(R.id.ts_chd);
//将视图持有者保存到转换视图中
convertView.setTag(holder);
}else{
holder = (ViewHolder)convertView.getTag();
}

String name = nameArr[position];
int imgsrc = imageArr[position];

holder.iv_icon.setImageResource(imgsrc);
holder.tv_name.setText(name);
holder.tv_child.setText(name+"id");

return convertView;
}

public final class ViewHolder{
public ImageView iv_icon;
public TextView tv_name;
public TextView tv_child;
}
}

16.4.ListView

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private int[] iconArray = {R.drawable.na,R.drawable.na,R.drawable.na,R.drawable.na,R.drawable.na};
private String[] nameArray = {"图一","图二","图三","图四","图五"};

@Override
protected void onCreate(Bundle savedInstanceState) {
ListView listView = findViewById(R.id.tv_lv);
List<Map<String, Object>> list = new ArrayList<>();
for (int i=0;i<iconArray.length;i++){
Map<String,Object> item = new HashMap<>();

item.put("name",nameArray[i]);
item.put("icon",iconArray[i]);
list.add(item);
}
SimpleAdapter startAdapter = new SimpleAdapter(this,list,R.layout.item_simple,
new String[]{"name","icon"},new int[]{R.id.tv_name,R.id.tv_im});

listView.setAdapter(startAdapter);
listView.setOnItemClickListener(this);
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toastshow.show(this,"点击了"+nameArray[position]);
}

更改样式

1
2
3
4
5
6
7
8
<ListView
android:id="@+id/tv_lv"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:divider="@null"
android:dividerHeight="0dp"
android:listSelector="@color/transparent"
/> <!-- //dividerHeight="0dp":分割线高度为0 listSelector="@color/transparent":分割线颜色为透明-->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void onClick(View v) {
if (v.getId() == R.id.cb_bg){
if (ck_bg.isChecked()){
listView.setSelector(R.drawable.listselector);
}else{
listView.setSelector(R.drawable.ic_launcher_foreground);
}
}else if (v.getId() == R.id.cb_fg){
if (ck_fg.isChecked()){
Drawable drawable = getResources().getDrawable(R.color.black,getTheme());
listView.setDivider(drawable);
listView.setDividerHeight(diptopx.dip(this,1));
}else{
listView.setDivider(null);
listView.setDividerHeight(0);
}
}
}

解决条目时间冲突问题

1
2
3
4
5
6
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:descendantFocusability="blocksDescendants">
<!--阻止下级控件获取焦点,避免堵塞-->

使用代码控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class MyAdapter extends BaseAdapter {
private int[] imageArr;

private String[] nameArr;
private Context mContext;
public MyAdapter(Context context, int[] imageArr, String[] nameArr){
this.mContext = context;
this.imageArr = imageArr;
this.nameArr = nameArr;
}

@Override
public int getCount() {
return imageArr.length;
}

@Override
public Object getItem(int position) {
return imageArr[position];
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = new ViewHolder();
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_withbutton, null);
holder.tv_ll = convertView.findViewById(R.id.ll_ts);
holder.iv_icon = convertView.findViewById(R.id.ts_im);
holder.tv_name = convertView.findViewById(R.id.ts_name);
holder.tv_child = convertView.findViewById(R.id.ts_chd);
holder.ts_bt = convertView.findViewById(R.id.ts_bt);
//将视图持有者保存到转换视图中
convertView.setTag(holder);
}else{
holder = (ViewHolder)convertView.getTag();
}

String name = nameArr[position];
int imgsrc = imageArr[position];

holder.iv_icon.setImageResource(imgsrc);


holder.tv_ll.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); //重点


holder.tv_name.setText(name);
holder.tv_child.setText(name+"id");
holder.ts_bt.setOnClickListener((v)->{
Toast.makeText(mContext,name,Toast.LENGTH_SHORT).show();
});

return convertView;
}

public final class ViewHolder{
public LinearLayout tv_ll;
public ImageView iv_icon;
public TextView tv_name;
public TextView tv_child;
public Button ts_bt;
}
}

16.5GridView

更改样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<GridView
android:id="@+id/gv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numColumns="2"/>


<GridView
android:id="@+id/gv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numColumns="2"
android:verticalSpacing="3dp" //垂直间距为3dp
android:background="#ff00ff"
android:columnWidth="100dp" //列宽为100dp
android:stretchMode="none" //拉伸模式
/>

使用代码控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class MyPAdapter extends BaseAdapter {
private int[] imageArr;

private String[] nameArr;
private Context mContext;
public MyPAdapter(Context context, int[] imageArr, String[] nameArr){
this.mContext = context;
this.imageArr = imageArr;
this.nameArr = nameArr;
}

@Override
public int getCount() {
return imageArr.length;
}

@Override
public Object getItem(int position) {
return imageArr[position];
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = new ViewHolder();
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.grid_list, null);
holder.iv_icon = convertView.findViewById(R.id.ts_im);
holder.tv_name = convertView.findViewById(R.id.ts_name);
holder.tv_child = convertView.findViewById(R.id.ts_chd);
//将视图持有者保存到转换视图中
convertView.setTag(holder);
}else{
holder = (ViewHolder)convertView.getTag();
}

String name = nameArr[position];
int imgsrc = imageArr[position];

holder.iv_icon.setImageResource(imgsrc);
holder.tv_name.setText(name);
holder.tv_child.setText(name+"id");

return convertView;
}

public final class ViewHolder{
public ImageView iv_icon;
public TextView tv_name;
public TextView tv_child;
}
}

16.6ViewPager

  1. 实现引导页:很多应用在首次启动时会展示一系列介绍性或欢迎界面,ViewPager 可以实现连续的翻页效果,让用户轻松浏览并快速进入主界面。
  2. 图片轮播或浏览:在需要展示多张图片的场景下,如产品详情页或照片查看器,ViewPager 可以实现图片的左右滑动切换,提供良好的用户体验。
  3. 多页面内容展示:用于展示多个页面的内容,比如在复杂的设置选项中,将不同设置类别或子菜单组织为可滑动的列表,每个页面对应一个设置类别。
  4. 结合 TabLayout 使用:与 TabLayout 结合是 ViewPager 的经典应用场景,通过顶部标签栏与底部内容区域联动,实现类似浏览器标签页的功能,每个标签对应一个 ViewPager 页面,便于用户在多个内容片段之间快速切换。
  5. 屏幕滑动导航:在一些新闻类、博客阅读类应用中,ViewPager 可以实现不同文章之间的平滑过渡和预加载,提供沉浸式阅读体验。
1
2
3
4
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

16.7PagerTitleStrip

1. 创建Fragment

创建多个Fragment,每个Fragment代表一个页面。例如:

1
2
3
4
5
6
public class FirstFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_first, container, false);
}
}

2. 创建适配器

创建一个继承自FragmentPagerAdapterFragmentStatePagerAdapter的适配器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MyPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> fragmentList = new ArrayList<>();
private final List<String> titleList = new ArrayList<>();

public MyPagerAdapter(FragmentManager fm) {
super(fm);
}

public void addFragment(Fragment fragment, String title) {
fragmentList.add(fragment);
titleList.add(title);
}

@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}

@Override
public int getCount() {
return fragmentList.size();
}

@Override
public CharSequence getPageTitle(int position) {
return titleList.get(position);
}
}

3. 在Activity中设置ViewPager和PagerTitleStrip

在Activity的布局文件中添加ViewPager和PagerTitleStrip:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent">

<androidx.viewpager.widget.PagerTitleStrip
android:id="@+id/pagerTitleStrip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingTop="20dp"
android:paddingBottom="20dp"
android:textSize="18sp"/>

</androidx.viewpager.widget.ViewPager>

在Activity中初始化ViewPager和适配器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MainActivity extends FragmentActivity {
private ViewPager viewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

viewPager = findViewById(R.id.viewPager);

MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new FirstFragment(), "第一页");
adapter.addFragment(new SecondFragment(), "第二页");
adapter.addFragment(new ThirdFragment(), "第三页");

viewPager.setAdapter(adapter);
}
}

16.8简单的启动引导页

每一个页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="imageview"
android:scaleType="fitXY"/>

<RadioGroup
android:id="@+id/rg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:paddingBottom="20dp"
android:orientation="horizontal" />

<Button
android:id="@+id/btn"
android:textColor="#ff3300"
android:layout_centerInParent="true"
android:visibility="gone"
android:text="立即开始"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

</RelativeLayout>

ViewPager

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<androidx.viewpager.widget.ViewPager
android:id="@+id/vp"
android:layout_width="match_parent"
android:layout_height="370dp"/>
</LinearLayout>

设置adapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class LauchSimpleAdapter extends PagerAdapter {
private List<View> mView = new ArrayList<>();
public LauchSimpleAdapter(Context mcontext,int[] launchImage) {
for (int i=0;i<launchImage.length;i++){
View inflate = LayoutInflater.from(mcontext).inflate(R.layout.activity_main, null);
ImageView iv = inflate.findViewById(R.id.iv);
RadioGroup rg = inflate.findViewById(R.id.rg);

Button btn = inflate.findViewById(R.id.btn);

iv.setImageResource(launchImage[i]);
for (int j = 0; j < launchImage.length; j++) {
RadioButton rb = new RadioButton(mcontext);
rb.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
rb.setPadding(10,10,10,10);
rg.addView(rb);
}
((RadioButton) rg.getChildAt(i)).setChecked(true);
if (i == launchImage.length-1){
btn.setVisibility(View.VISIBLE);
btn.setOnClickListener( v ->{
Toast.makeText(mcontext,"welcome", Toast.LENGTH_SHORT).show();
});
}
mView.add(inflate);
}
}

@Override
public int getCount() {
return mView.size();
}

@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
View item = mView.get(position);
container.addView(item);
return item;
}

@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView(mView.get(position));
}
}

创建界面

1
2
3
4
5
6
7
8
9
10
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);

ViewPager vp = findViewById(R.id.vp);
LauchSimpleAdapter adapter = new LauchSimpleAdapter(this,launchImage);
vp.setAdapter(adapter);
}

17.Fragment

1.创建fragment

静态注册

1
2
3
4
5
<fragment
android:id="@+id/fg"
android:name="com.richuff.server.fragment.BlankFragment" //自己创建的fragment的路径
android:layout_width="match_parent"
android:layout_height="match_parent"/>

动态注册

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, MyFragment.class, null)
.commit();
}
}
}

2.改进启动引导页

在Activity里声明

1
2
3
4
5
ViewPager vp =  findViewById(R.id.vp);
//launchImage 为R.drawable.? 的int id
LauchImproveAdapter adapter = new LauchImproveAdapter(getSupportFragmentManager(),launchImage);
//添加适配器
vp.setAdapter(adapter);

适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class LauchImproveAdapter extends FragmentPagerAdapter {

private int[] mImageArray;

public LauchImproveAdapter(@NonNull FragmentManager fm,int[] launchImage) {
super(fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.mImageArray = launchImage;
}

@NonNull
@Override
public Fragment getItem(int position) {
return TestFragment.newInstance(mImageArray.length,position,mImageArray[position]);
}

@Override
public int getCount() {
return mImageArray.length;
}
}

Fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class TestFragment extends Fragment {

//创建实例,添加参数
public static TestFragment newInstance(int count,int position, int imageId) {
TestFragment fragment = new TestFragment();
Bundle args = new Bundle();
args.putInt("position", position);
args.putInt("imageId", imageId);
args.putInt("count",count);
fragment.setArguments(args);
return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Bundle bundle = getArguments();
Context mcontext = getContext();
View minflate = LayoutInflater.from(mcontext).inflate(R.layout.view_page, null);

if (bundle != null){
int position = bundle.getInt("position");
int imageId = bundle.getInt("imageId");
int count = bundle.getInt("count");

ImageView iv = minflate.findViewById(R.id.iv);
RadioGroup rg = minflate.findViewById(R.id.rg);

Button btn = minflate.findViewById(R.id.btn);

iv.setImageResource(imageId);
for (int j = 0; j < count; j++) {
RadioButton rb = new RadioButton(mcontext);
rb.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
rb.setPadding(10,10,10,10);
rg.addView(rb);
}
((RadioButton) rg.getChildAt(position)).setChecked(true);
if (position == count-1){
btn.setVisibility(View.VISIBLE);
btn.setOnClickListener( v ->{
Toast.makeText(mcontext,"welcome", Toast.LENGTH_SHORT).show();
});
}
}
return minflate;
}
}

优势

  • 加快启动速度
  • 降低代码耦合度