Kotlin学习

第一行代码(安卓)

Kotlin

1.变量

用var 创建变量

image-20210710005253999

2.函数

image-20210710005522112

如果函数的返回值只有一句,那么可以使用Kotlin的语法糖,省略写法

image-20210710201320664

3.逻辑控制语句

1) if条件语句

普通写法

image-20210710201603089

精简1

image-20210710201931705

精简2

image-20210710202110874

精简3

image-20210710202122704

2)when条件语句

带参写法
1
2
3
when(传入的参数){
匹配值 -> {执行逻辑}
}

image-20210710202613342

允许类型匹配

image-20210710203012398

不带参写法

image-20210710203150863

3)循环语句

image-20210710203349947

循环体内的关键字

image-20210710203752176

4.函数

定义

1
2
3
4
//函数的权限修饰符 		函数的参数  		函数的返回值 
private fun doSomething(age:Int,flag:Boolean):String{
return "result"
}

函数默认参数

也就是在函数没有传入参数的时候,默认使用的值

1
2
3
4
//可以给默认参数
private fun fix(name:String = "jack",age: Int = 5){
println(name+"-----"+age)
}

具名函数

也就是在调用函数的时候,传入的参数可以指定参数的名字传进去

image-20210717050348869

函数无返回值时

Kotlin没有返回值就是uint

Nothing也可以是无返回值

1
2
//TODO的函数任务就是抛出异常,返回Nothing
TODO("nothing")

反引号函数名

可以用反引号包裹特殊的函数名,一般用于测试

image-20210717050803055

匿名函数
无参匿名

在{}内可以用lamada表达式对函数进行操作

1
2
3
val totals = "sadadagsdffgdfsgsd".count({
it -> it == 's'
})
有参匿名

image-20210717142645948

it关键字

image-20210717142823958

函数类型推断

无参

image-20210717143459806

有参

image-20210717143523374

定义参数是函数的函数

image-20210717145352294

函数引用

image-20210717155901376

image-20210717155853512

函数类型作为返回类型

image-20210717160725662

5.类与对象

新建一个类

image-20210711133945011

创建一个对象

image-20210711134003337

继承与构造方法

image-20210711152537469

init结构体

对变量进行初始化

接口

统一使用冒号进行实现

image-20210711153409365

image-20210711153417714

数据类

Java中需要重写toString equals等方法

在Koltin中只需要一个data关键字

image-20210711153948459

单例类

image-20210711153938822

6.容器

List集合

创建

image-20210711154421752

其他写法

不可变集合(不能改变集合的长度)

image-20210711154517943

可变集合的创建

image-20210711154625937

Set集合

image-20210711200745952

Map集合

image-20210711200934139

遍历

image-20210711201226132

函数式API

image-20210714124321847

空指针检查

在变量的类型后加?

代表这个变量可为空

image-20210717203656025

image-20210714134216717

判空辅助工具

不可空操作符

?. 的用法

image-20210714140229865

空合并操作符

?: 的用法

image-20210714140151306

非空断言工具

image-20210714141245421

let函数

image-20210714141729446

7.异常

image-20210717204116525

8.数据类型

字符串类型

字符串遍历

image-20210717204805138

字符串分割

image-20210717204856041

数字类型

安全转换函数

image-20210717205139422

四舍五入转换

image-20210717205349905

apply函数

image-20210717205851340

let函数

image-20210717210921209

换句话说,就是谁调用let,it就指代的是谁

1
2
3
4
5
6
7
//这里面it指代的是"testLet"这个字符串,最后执行完返回的是1
"testLet".let {
println(it)
println(it)
println(it)
return 1
}

let,apply,with,run函数区别

let

默认当前对象闭包的 it 参数,返回值是函数里面最后一行,或者指定return

1
2
3
4
5
6
7
8
fun testLet():Int {
"testLet".let {
println(it)
println(it)
println(it)
return 1
}
}
apply

调用某对象的 apply 函数,在函数范围内,可以调用该对象的任意函数,返回值是该对象。

1
2
3
4
5
6
7
fun testApply() {
ArrayList<String>().apply {
add("apply1")
add("apply2")
add("apply3")
}.let { print(it) }
}
with

返回值是最后一行,函数范围中可以调用对象方法。像 let 和 apply 结合

1
2
3
4
5
6
7
8
fun testWith() {
with(ArrayList<String>()) {
add("testWith")
add("testWith")
add("testWith")
this
}.let { println(it) }
}
run

run函数和apply函数很像,只不过run函数是使用最后一行的返回,apply返回当前自己的对象。

1
2
3
4
5
fun testRun() {
"testRun".run {
println("this = " + this)
}.let { println(it) }
}

Android

Activity

设置主启动Activity

image-20210715133208581

在Activity中使用Toast

image-20210715133733402

创建Menu菜单

1.新建一个menu布局文件

image-20210715141642789

2.重写onCreateOptionsMenu方法

image-20210715141803589

3.重写onOptionsItemSelected方法

image-20210715142010213

销毁一个Activity

调用finish()方法销毁

使用Intent在Activity之间跳转

显示Intent跳转

image-20210715143334178

隐式跳转Intent

在AndroidManifest.xml中定义Activity的Action和Category属性

通过隐式调用intent会自动匹配合适的Action和Category

image-20210715144238307

image-20210715144303638

向下一个Activity传递数据

image-20210715145431182

image-20210715145458906

Activity生命周期

启动一个新的活动。入栈,并处于栈顶。

销毁一个活动时,栈顶活动出栈。
系统总是会显示处于栈顶的活动给用户,即栈顶活动可见。

Activity状态

运行状态

一个活动位于栈顶时,这活动处于运行状态。

暂停状态

一个活动不处于栈顶位置,但仍然可见时,这活动处于暂停状态。活动完全存活着。(比如栈顶活动为对话框形式)

停止状态
一个活动不处于栈顶位置,且完全不可见,这活动处于停止状态。(还在栈中)

销毁状态
一个活动从栈中移除后,这活动处于销毁状态。

Activity的生存期
Activity的七个回调方法
  1. onCreate()
    活动第一次被创建的时候调用,在该方法中完成初始化操作,加载布局、绑定事件等。

  2. onStart()
    由不可见变为可见的时候调用。(非栈顶到栈顶)

  3. onResume()
    在活动准备好和用户交互的时候调用。(栈顶)

  4. onPause()
    准备去启动或恢复另一个活动时调用。

  5. onStop()
    在活动完全不可见时调用。

  6. onDestroy()
    在活动被销毁前调用。完成释放内存操作。

  7. onRestart()
    在活动由停止状态变为运行状态前调用。
    除onRestart()外,其他两两对应,活动分为三种生存期:

  8. 完整生存期
    onCreate()和onDestroy()之间所经历的,就是完整生存期。

  9. 可见生存期
    onstart()和onStop()之间所经历的,就是可见生存期。

  10. 前台生存期
    onResume()和onPause()之间所经历的,就是可见生存期。

Activity的几种启动模式

standard:默认为标准模式,每次启动一个活动都会创建一个新的实例

singletop:如果启动的活动在栈顶,则不创建新的实例,但不在栈顶会

创建新的实例。

singTask:每次启动活动时都会检查返回栈中是否已有该活动的实例,如果有则将其上面的活动出栈。使得该活动处于栈顶。不在创建实例。

singleInstance:创建一个新的返回栈来管理这个活动,达到在多个应用程序中共享这个活动的实例。

UI

常用控件

TextView
常用属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<TextView
//指定控件的id
android:id="@+id/text1"
//控件的宽度 match_parent 为占满整个父控件 wrap_content为包裹内容
android:layout_width="match_parent"
//控件的高度 match_parent 为占满整个父控件 wrap_content为包裹内容
android:layout_height="wrap_content"
//控件内展示的文本
android:text="传递的数据"
//控件内展示的文本的字体大小
android:textSize="18sp"
//控件内展示的文本的字体样式
android:textStyle="bold"
//控件内展示的文本的字体颜色
android:textColor="@color/teal_700"
//android:gravity 组件的子组件在组件中的位置
android:gravity="center"/>
//android:layout_gravity 组件自身在父组件中的位置
Button

同上

1
android:textAllCaps="false" //默认button上的英文会以大写的方式展示,设置为false就保留原始文字内容展示
EditText

同上

1
2
3
4
5
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
//指定输入框提示性的文字
android:hint=""/>
ImageView

同上

ProgressBar

同上

特殊属性

1
2
3
4
5
6
7
<ProgressBar
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:progressBarStyleHorizontal"//更改进度条样式,使用水平进度条
android:max="100"//进度条最大长度
/>

显示隐藏进度条/加快进度条

image-20210716155657337

AlertDialog对话框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
val button: Button = findViewById(R.id.AlertDialogBtn)
button.setOnClickListener {
val builder = AlertDialog.Builder(this)
builder.setTitle("弹窗的标题")
builder.setMessage("弹窗消息")
builder.setCancelable(false)//弹窗是否可以点击空白区域关闭
builder.setPositiveButton("OK") { dialog, which ->
Toast.makeText(this, "点击了OK", Toast.LENGTH_SHORT).show()
}
builder.setNegativeButton("Cancel") { dialog, which ->
Toast.makeText(this, "点击了OK", Toast.LENGTH_SHORT).show()
}
builder.show()
}

基础布局容器

Android 的UI 可以分为两类,一类叫做ViewGroup容器,一类叫做View视图

View视图:(TextView,Button,ImageView)都是常用常见的视图.

ViewGroup容器:内部可以承载、放置、添加View视图

布局
  • LinearLayout线性布局:横着或竖着按顺序排列
  • RelativeLayout相对布局:起始坐标时屏幕坐上角,以同级或上级为参考系定位位置
  • FrameLayout帧布局:像千层饼一样,一层压着一层
  • ConstraintLayout约束布局:google于2016年新发布的一种布局方式,它不在android的基础api包里,需要额外引入
  • AbsoluteLayout绝对布局(以屏幕左上角为参考系,定位自己的位置,从android 2.2版本后废弃)
  • GridLayout网格布局(可以指定行数列数,子控件自动根据行列数进行分配位置,于android 4.0后新增进api中)
  • TableLayout表格布局(类似于网格布局,以一个TableRow标签定义为一行或一列)
线性布局LinearLayout

线性布局就是从左到右从上到下顺序排列的一种布局。下面讲一讲LinearLayout的基础属性。

属性 可选值 说明
orientation 1.vertical:垂直排列 2.horizontal:水平排列 也就是这个线性布局到底是水平方向逐个排列还是垂直方向逐个排列
layout_width layout_height 1.match_parent:填充父容器的剩余空间 2.wrap_content:根据子视图宽高自适应自己的宽高 3.自定义大小50dp layout_width和layout_height是android中控件的必要属性,规定了控件的宽度和高度,这个两个属性的值可以是指定的值,也可以根据内容自适应,还可以填充整个剩余空间
background #ff0000 红色 填充背景色
gravity 1.center:所有子视图相对于父容器居中显示 2.horizontal_center:所有子容器的横向方向上相对父容器居中显示 3.vertical_center:所有子视图的纵向方向上相对父容器居中显示 决定子控件相对该父容器的位置
layout_gravity 1.center:该容器相对于它的父容器居中显示 2.horizontal_center:该容器横向方向上相对它的父容器居中显示 3.vertical_center:该容器纵向方向上相对它的父容器居中显示 决定该容器相对它的父容器的位置
weight 按比例分配父容器剩余的宽度或高度

效果展示

  • android:orientation =”vertical”所有子视图纵向摆放

linear_vertical

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
<?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"
android:gravity="center" //子视图相对父视图居中显示
android:orientation="vertical"> //所有子视图纵向摆放

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="普通按钮" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="普通按钮" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="普通按钮" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="普通按钮" />

</LinearLayout>
  • android:orientation =”horizontal”所有子视图横向摆放

linear_horizontal

1
2
3
4
5
6
7
8
<?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"
android:gravity="center" //子视图相对父视图居中显示
android:orientation="horizontal"> //所有子视图横向摆放
......... 省略
</LinearLayout>
相对布局RelativeLayout

相对布局在摆放子视图位置时,按照指定的参考系来摆放子视图的位置,默认以屏幕左上角(0,0)位置作为参考系摆放位置

  • 相对于父元素 7个常用属性
属性 可选值 说明
layout_alignParentTop true/false 是否让控件相对于父容器顶部对齐
layout_alignParentBottom true/false 是否让控件相对于父容器底部对齐
layout_alignParentLeft true/false 是否让控件相对于父容器左边对齐
layout_alignParentRight true/false 是否让控件相对于父容器右边对齐
layout_centerHorizontal true/false 相对父容器水平居中显示
layout_centerVertical true/false 相对父容器垂直居中显示
centerInParent true/false 相对父容器居中显示
  • 相对于兄弟元素 4个常用属性
属性 可选值 说明
layout_above @+id/ 指定在那个控件的上侧
layout_below @+id/ 指定在那个控件的上侧
android:layout_toLeftOf @+id/ 指定在那个控件的左侧
android:layout_toRightOf @+id/ 指定在那个控件的右侧
  • 相对于兄弟元素的对齐方式
属性 可选值 说明
layout_alignLeft @+id/ 该控件的左边沿与指定控件的左边对齐
layout_aliginRight @+id/ 该控件的右边沿与指定控件的右边对齐
layout_alignTop @+id/ 该控件的上边沿与指定控件的上边沿对齐
layout_alignBottom @+id/ 该控件的下边沿与指定控件的下边沿对齐

效果演示

relative_below_right_of

使用layout_below使得后面一个组件位于前面一个组件的下方

配合layout_toRightOf使得后面一个组件位于前面一个组件的右方

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"
android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="普通按钮1" />

<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/btn1"
android:layout_toRightOf="@+id/btn1"
android:text="普通按钮2" />

<Button
android:id="@+id/btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/btn2"
android:layout_toRightOf="@+id/btn2"
android:text="普通按钮3" />

<Button
android:id="@+id/btn4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/btn3"
android:layout_toRightOf="@+id/btn3"
android:text="普通按钮4" />
</RelativeLayout>
帧布局FrameLayout

组件的默认位置都是左上角,组件之间可以重叠。像千层饼一样,一层压着一层 可以设置上下左右的对齐、水平垂直居中、设置方式与线性布局相似

  • 常用属性
属性 可选值 说明
layout_gravity center/center_vertical/center_horizontal 组件相对父容器的位置
layout_marginLeft 具体的数值100dp 左侧外间距
layout_marginTop 具体的数值100dp 上侧外间距
layout_marginRight 具体的数值100dp 右侧外间距
layout_marginBottom 具体的数值100dp 下侧外间距

效果演示

viewgroup_framelayout

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
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<!-- android:background="@color/purple_200"设置文本的背景色
android:gravity="center_horizontal" // 文本中的文字对齐方式
android:paddingTop="100dp" // 文本的上边内间距
android:text="layout_gravity:center" // 现实的文本内容
android:textSize="30dp" /> // 文本字号大小-->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@color/purple_200"
android:gravity="center_horizontal"
android:paddingTop="100dp"
android:text="layout_gravity:center"
android:textSize="30dp" />

<TextView
android:layout_width="300dp"
android:layout_height="360dp"
android:layout_gravity="center"
android:background="@color/purple_500" />

<TextView
android:layout_width="240dp"
android:layout_height="240dp"
android:layout_gravity="center"
android:background="@color/purple_700" />

<TextView
android:layout_width="140dp"
android:layout_height="140dp"
android:layout_gravity="center"
android:background="@color/teal_700" />


<TextView
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="center"
android:background="#ffffff"
android:gravity="center" />
</FrameLayout>
总结

viewgroup_zongjie

自定义控件

1.引入布局

image-20210716165538935

2.隐藏actionbar
1
supportActionBar?.hide()
3.创建自定义控件

image-20210716165649869

4.在mainActivity布局文件中引入全限定名

image-20210716165732296

ContentProvider内容提供器

在程序运行时申请权限

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
package com.example.runtimepermissiontest;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.make_call);
button.setOnClickListener((View v) -> {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CALL_PHONE}, 1);
}else {
call();
}
});
}

private void call() {
try {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length>0 &&grantResults[0]==PackageManager.PERMISSION_GRANTED){
call();
}else {
Toast.makeText(this,"没权限", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
}

访问其他程序中的数据

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
package com.example.contactstest;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
ArrayAdapter adapter;
List<String> contactsList = new ArrayList<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取ListView实例
ListView contactsView = findViewById(R.id.contacts_view);
//创建一个数组适配器 第一参数传入上下文对象 第二参数传入item的布局 第三个参数传入一个数组
adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, contactsList);
//ListView调用setAdapter()方法,传入刚刚创建好的适配器对象
contactsView.setAdapter(adapter);
//判断是否有权限,如果有权限,就调用读取联系人的方法
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 1);
} else {
readContacts();
}
}

private void readContacts() {
Cursor cursor = null;
try {
//获取ContentResolver实例,调用query()查询方法,传入uri参数
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()){
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactsList.add(displayName + "\n" +number);
}
adapter.notifyDataSetChanged();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (cursor!=null){
cursor.close();
}
}
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
readContacts();
} else {
Toast.makeText(this, "没权限", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
}

创建内容提供器的步骤

暂时跳过

手机多媒体

使用通知

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
package com.example.notificationtest;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.send_notice);
//获取notificationManager实例
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//在新版本中,必须有以下的代码
//判断是新版还是旧版的安卓
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
//创建一个id为normal 名字为Normal 的 默认的重要性 的通知渠道
//参数为通道ID,通道名,通道重要性
NotificationChannel channel = new NotificationChannel("normal", "Normal", NotificationManager.IMPORTANCE_DEFAULT);
//调用NotificationChannel的createNotificationChannel()方法创建
notificationManager.createNotificationChannel(channel);
}
button.setOnClickListener((v -> {
//创建一个通知 参数为上下文 通知通道的id
Notification notification = new NotificationCompat.Builder(this, "normal")
.setContentTitle("通知标题")
.setContentText("通知文本")
//指定通知创建的时间
.setWhen(System.currentTimeMillis())
//设置通知的小图标
.setSmallIcon(R.mipmap.ic_launcher)
//设置通知的大图标
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
//调用build()方法构建好通知
.build();
//通过notificationManager通知管理器调用notify()方法将通知推送出来
//参数为 id和通知的实例 这里每条的通知id都必须唯一
notificationManager.notify(1, notification);
}));
}
}

为通知添加点击跳转

image-20210725044221545

点击通知后通知消失

1.setAutoCancel方法

image-20210725045230032

2.显示调用

image-20210725045406295

通知的进阶用法

  1. 设置震动

  2. 设置提示音

  3. 设置为大图片

  4. 设置通知重要性

    image-20210725205736746

调用摄像头拍照

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
package com.example.cameraaibumtest;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.Button;
import android.widget.ImageView;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {
ImageView picture;
Uri imageUri;
private static final int TAKE_PHOTO = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button takePhoto = findViewById(R.id.take_photo);
picture = findViewById(R.id.picture);
takePhoto.setOnClickListener(v -> {
//创建一个file对象,图片名字叫output_image.jpg 存放地址为应用关联缓存目录
//getExternalCacheDir这个就是获取应用关联缓存目录的方法
File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
try {
//判断文件是否存在
if (outputImage.exists()) {
//存在就删除
outputImage.delete();
}
//不存在就创建一个文件对象
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
//判断安卓版本是否大于7.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//大于7.0就 调用内容提供器封装成一个URI对象共享给外部提高应用安全性
//第一个参数为上下文对象 第二个参数为任意字符串 第三参数为刚刚创建的file对象
imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cameraaibumtest.fileprovider", outputImage);
} else {
//如果7.0以下就调用fromFile方法转换为uri对象
imageUri = Uri.fromFile(outputImage);
}
//启动相机程序
Intent intent=new Intent("android.media.action.IMAGE_CAPTURE");
//指定图片输出的地址 第二个参数为刚刚转换完成的uri对象
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
//以下两行代码适配Android 7.0 解决了无法加载图片的问题
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent,TAKE_PHOTO);
});
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case TAKE_PHOTO:
if (resultCode == RESULT_OK) {
try {
//将拍摄的照片显示出来
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
Log.d("this", "==== " + bitmap);
picture.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
}

image-20210726011907798

调用相册里的图片

image-20210726011847764

使用网络技术

WebView的用法

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
package com.example.webviewtest;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView = findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
try {
if (!url.startsWith("http:") ||!url.startsWith("https:")) {
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse(url));
startActivity(intent);
return true;
}
}
catch (Exception e){
return false;
}

view.loadUrl(url);
return true;
}
});
webView.loadUrl("https://www.baidu.com");
}


}

OkHttp3

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
package com.example.networktest;

import com.google.gson.Gson;

import androidx.appcompat.app.AppCompatActivity;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.example.notice;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
TextView responseText;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button sendRequest = findViewById(R.id.send_request);
responseText = findViewById(R.id.response_text);
sendRequest.setOnClickListener(this);
}

@Override
public void onClick(View v) {
if (v.getId() == R.id.send_request) {
//sendRequestWithHttpURLConnection();
sendRequestWithOKHttp();
}
}

private void sendRequestWithOKHttp() {
new Thread(new Runnable(){
@Override
public void run() {
try {
//创建OkHttpClient实例
OkHttpClient client = new OkHttpClient();
//创建一个Request对象
Request request = new Request.Builder().url("http://124.93.196.45:10001/prod-api/api/metro/notice/1").build();
//发起get请求,返回一个response对象
Response response = client.newCall(request).execute();
//response调用body获取响应体,调用string方法获取到响应的的字符串
String responseData = response.body().string();
//展示
//showResponse(responseData);
//parseJSONWithJSONObject(responseData);
parseJSONWithJSONGSON(responseData);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
private void parseJSONWithJSONGSON(String responseData){
Gson gson = new Gson();
notice notice = gson.fromJson(responseData, notice.class);
System.out.println(notice);
showResponse(notice.toString());
}
private void parseJSONWithJSONObject(String responseData) {
try {
JSONObject jsonObject = new JSONObject(responseData);
JSONObject data = jsonObject.getJSONObject("data");
showResponse(data.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}

private void sendRequestWithHttpURLConnection() {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
//创建一个URL
URL url = new URL("https://www.gravity.wang");
//开启连接
connection = (HttpURLConnection) url.openConnection();
//设置读取超时的时间
connection.setReadTimeout(8000);
//设置GET请求
connection.setRequestMethod("GET");
//设置连接超时的时间
connection.setConnectTimeout(8000);
//获取输入流
InputStream is = connection.getInputStream();
//下面对获取的输入流进行读取
reader = new BufferedReader(new InputStreamReader(is));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
showResponse(response.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}

private void showResponse(String response) {
runOnUiThread(new Runnable() {
@Override
public void run() {
responseText.setText(response);
}
});
}
}

解析XML

Pull解析方式

暂时跳过

SAX解析方式

暂时跳过

解析JSON

使用JSONObject
1
2
3
4
5
6
7
8
9
private void parseJSONWithJSONObject(String responseData) {
try {
JSONObject jsonObject = new JSONObject(responseData);
JSONObject data = jsonObject.getJSONObject("data");
showResponse(data.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
使用GSON

使用GSON可以将json直接封装成一个java对象(bean)

导入GSON的包

1
2
3
4
5
6
7
8
private void parseJSONWithJSONGSON(String responseData){
//创建GSON对象
Gson gson = new Gson();
//调用fromJson()方法,传入json和腰封装的对象
notice notice = gson.fromJson(responseData, notice.class);
System.out.println(notice);
showResponse(notice.toString());
}

Service后台服务

在子线程更新UI

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
package com.example.androidthreadtest;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView text;
//创建一个handler并重写handleMessage()方法
//如果子线程调用了sendMessage()方法,就好传递回一条消息,并由handleMessage()接收到
private Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
text.setText(msg.obj.toString());
break;
default:
break;
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button changeText = (Button) findViewById(R.id.change_text);
text = (TextView) findViewById(R.id.text);
changeText.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.change_text:
new Thread(new Runnable() {
@Override
public void run() {
//创建一条消息
Message message = new Message();
//往消息里添加数据
message.what = 1;
message.obj = "Nice to meet you";
//发送消息
handler.sendMessage(message);
//如果未发送的消息就会到MessageQueue(消息队列)里进行排队
//Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后
//就会进入到一个无限循环当中,然后每当发现MessageQueue()中存在一条消息
//就会将它去除,并传递到Handler的handleMessage()方法中
}
}).start();
break;
}
}
}

AsyncTask

AsyncTask(抽象类)实现原理基于异步消息处理机制。
AsyncTask类的三个泛型参数:
(1)Params:在执行AsyncTask是需要传入的参数。
(2)Progress:后台执行任务时,如果需在界面显示当前进度,则该泛型作为进度单位。
(3)Result:任务执行完毕后,如需对结果进行返回,该泛型作为返回值类型。

用AsyncTask类经常需重写的方法:
(1)onPreExecute(): 后台任务开始执行前调用,用于界面上的初始化操作。
(2)doInBackground(): 该方法中所有代码会在子线程中运行,在这里处理耗时任务,用return返回任务执行结果。如需更新UI元素,调用publishProgress(Progress…)来完成。
(3)onProgressUpdata(Progress…): 当后台调用publishProgress(Progreess…)后,该方法很快被调用,在这个方法中可进行UI操作。
(4)onpostExecute(Result): 后台任务执行完毕并通过return进行返回时,该方法被调用。返回的数据作为参数传递到该方法中,利用返回的数据可进行一些UI操作,例关闭进度条对话框等。

总结:在doInBackground()中执行耗时的任务,在onProgressUpdate()中进行UI操作,在onpostProgress()中执行任务的收尾工作。

Service服务

基本使用
启动和停止服务

创建一个服务

重写三个方法

onCreate会在服务被创建的时候调用

onStartCommand会在服务启动的时候被调用

onDestroy会在服务被销毁的时候被调用

image-20210729233500426

在AndroidManifest.xml中注册服务image-20210729233701498

服务的启动和停止

image-20210729233728464

活动和服务进行通信

在服务的类创建一个子类

子类继承于Binder

子类内部定义方法,可以被activity(活动)访问

最后在OnBind()方法进行返回

image-20210729233839370

创建一个服务连接对象

onServiceConnected方法是连接时执行的

onServiceDisconnected是在断开连接执行的

image-20210730014850689

最后将activity和service进行绑定

image-20210730015103386

服务的生命周期

一旦在项目的任何位置调用了Context 的startService()方法,相应的服务就会启动起来,并回调onStartCommand()方法。如果这个服务之前还没有创建过,onCreate()方法会先于onStartCommand()方法执行。服务启动了之后会一直保持运行状态,直到stopService()或stopSelf()方法被调用。注意虽然每调用一次startService()方法,onStartCommand()就会执行一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次startService()方法,只需调用一次stopService()或stopSelf()方法,服务就会停止下来了。
  另外,还可以调用Context 的bindService()来获取一个服务的持久连接,这时就会回调服务中的onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于onBind()方法执行。之后,调用方可以获取到onBind()方法里返回的IBinder 对象的实例,这样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。
  当调用了startService()方法后,又去调用stopService()方法,这时服务中的onDestroy()方法就会执行,表示服务已经销毁了。类似地,当调用了bindService()方法后,又去调用unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。但是需要注意,我们是完全有可能对一个服务既调用了startService()方法,又调用了bindService()方法的,这种情况下该如何才能让服务销毁掉呢?根据Android 系统的机制,一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,这种情况下要同时调用stopService()和unbindService()方法,onDestroy()方法才会执行。

使用前台服务

image-20210730020533831

使用IntentService(多线程)

创建一个子类继承IntentService

onHandleIntent这个方法已经在子线程里了

image-20210730022111071

启动服务

image-20210730022217045

Material Design


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!