Android疯狂讲义前10章知识点总结

/-------------------------10-31号晚上学习笔记----------------------------/
在设置了最小支持版本为18及以上的时候,需要将获得actionBar组件的包引用改为
import androidx.appcompat.app.ActionBar; 即可。

2.11.3:启用程序图标导航:
已实现

2.11.4:添加ActionView组件:

有些时候只可以使用监听事件处理机制

3.2基于监听的事件处理:
控制飞机移动实例实现:
逻辑:{
1.先创建一个飞机画图的view类
2.然后再mainActivity中创建一个内部类作为监听器
3.为飞机的view对象设置监听器实现对用户手势的监听
}

实现对于事件监听的监听器的设置方式:{
内部类
外部类(该监听器主要被很多GUI界面共享)
mainActivity实现监听器接口
使用Lambda表达式实现监听器
}

如果确实有多个监听器需要同一个业务逻辑组件,那么我们可以将这些业务逻辑
使用业务逻辑组件进行定义

3.2.5中Activity本身作为事件监听器类{
该种方法出现的问题:{
1.会造成程序结构的混乱(因为Activity类主要是用来初始化页面)
2.会觉得比较怪异
}
}

3.2.6 Lambda表达式作为事件监听器类:{
使用原因:
大部分的事件监听器没有复用价值(因为可以复用的都设置为事务逻辑组件)

实际上Lambda表达式时目前使用最为广泛的监听器的实现形式:{
实现形式:
textView.setOnClickListener(view -> show.setText(“xxx”));

}

}

直接绑定到标签:{

}

基于回调的事件处理:{
机制可以通过自定义View来实现,自定义View时重写该view的事件处理方法即可
此时的事件处理还是事件源本身来进行的
}

基于监听的事件处理中,事件源和事件处理是分开的。

3.3.2-基于回调的事件传播:{
如果事件处理的返回值是:true 表示该事件不会进行传播;
如果事件处理的返回值是:false 表示该事件会进行传播;

事件可以进行传播的情况下-触发事件的顺序是:{
1.自定义组件中实现的监听器内容
2.给组件注册的监听器中的内容
3.该组件所在的activity中的OnTouchEvent()方法中
}
}

3.4 响应系统设置的事件:{
3.4.1 Configuration 类{
作用:专门用于描述手机设备上的配置信息{包括的是用户特定的配置项和系统的动态配置项}
实例:获取系统设备状态{
实现步骤:{
第一步:获取系统当前的Configuration对象
第二步:调用Configuration对象的属性来获取设备状态
}
}
}
}

重写onConfigurationChanged()方法响应系统设置更改:

重点:Handler消息传递机制{
作用:处于对性能的优化。安卓的UI操作并不是线程安全的,这意味着如果有
多个线程并发操作UI组件,有可能导致的线程安全问题。为了解决的这个问题,使用了
handler处理机制。

在启动主线程的时候,即是UI界面只可以被主线程修改。但是后期也会继续
对UI界面进行修改,那么就需要使用Handler事件处理机制

handler类的作用主要有两个:{
1.在新启动的线程中发送消息
2.在主线程中获取、处理消息
}

实例:实现自动播放的动画
}

一个线程对应的是一个Looper()对象
在线程中使用Handler的步骤如下:
{
1.调用Looper的perpare()方法为当前线程创建Looper对象,创建Lopper对象的时候
它的构造器会创建与之配套的MessageQueue();
2.有了Looper之后,创建Handler子类的实例,重写handlemMessage()方法,
该方法负责处理来自其他线程的消息
3.调用Looper()的loop()方法启动Looper
}

//实例:使用线程计算质数
(使用另外一个线程计算的原因是:否则会导致ANR异常)

重点:不要在UI界面中引入一些过多的操作

3.6 使用异步任务{
AsyncTask实现调用的时候需要的是系统进行调用
实例:使用异步任务执行下载
}

截止到:234/767(电子版)

/-----------------------------2020-12-15号下午学习笔记-------------------------------/

第四章学习笔记:

使用activity继承自LauncherActivity实现的是整个activity作为一个列表实现渲染。

android应用要求所有的应用程序组件都必须显式进行配置
使用bundle在Activity之间交换数据
bundle是一个简单的数据携带包 该bundle对象包含了多个方法来存储数据{
putxxx()
putSerializable()
}等

intent中的提供的putExtra()方法以及向intent中存储数据其实还是用的bundle中的数据
如果调用以上的方法时,intent中没有bundle对象,那么就创建一个bundle对象
//截止到2020-12-15日下午 16:02学习笔记

2020-12-15日晚上学习笔记:
4.2Activity回调机制:
Activity中的回调和web中的回调是类似的
程序架构中的点:可以是接口形式存在也可以是抽象方法实现(后者就是Activity中的调用机制的实现方法)
回调实现的第一种实现方式就是典型的命令者模式
当前活动的Activity在栈顶
在onStart()方法之后一定会回调onResume()方法

Activity中的数据展示是由UI组件搭建而成的,但是Servlet中的数据的显示是由浏览器直接生成的

Android采用Task对多个Activity进行管理
当启动应用的时候Android会自动创建一个Task,然后是启动应用的入口Activity(也就是在intent-filter和zoo那个配置为
MAIN和LAUNCHER的Activity

开发者无法真正访问Task(因为Android并没有为Task组件提供一个接口)
但是可以通过所在的Activity中使用getTaskId()获得其对应的Task的Id。

先启动的Activity放到栈底然后后启动的放到栈顶

Android中的几种启动方式:
{
standard:表示的是每一次启动的时候只有一个Task也就是说后来启动的都是直接覆盖到原来的Task上(但是会产生很多的实例)
singleTop模式:表示的是基本上和standard差不多,有区别的地方在于:当将要创建的Activity在栈顶的时候,
系统就不再创建一个新的实例
singleTask模式:采用的singleTask这种加载模式的Activity能保证在同一个Task中只有一个实例,当系统采用这种模式的时候
分为三种情况:
{
1.如果将要启动的目标Activity不在,系统将会创建目标Activity的实例,并将它加入Task栈顶
2.如果已经位于栈顶,那么此时与singleTop模式的行为相同
3.如果是已经存在但是没有在栈顶,那么系统会将所有的Acticity移除栈顶从而使得目标的Activity在栈顶
}
}

singleInstance模式:
{
采用本模式的时候,无论系统从哪个Task启动目标Activity,只会创建一个目标Activity实例,并会使用一个全新的
Task栈来加载该Activity实例

并且采用本模式的时候有两种情况:
{
1.如果目标不存在,系统会创建一个全新的task并创建一个Activity并将它加入到新的task的栈顶
2.如果已经存在,那么系统都会把它调出来放到前台
}
}

需要说明的是采用singleInstance的模式创建Activity时,一个Task只包含一个Activity
设置xml文件中的加载模式的同时需要设置的是exported="true"对应的是:表示该Activity可以被其他应用启动

4.5 Android 0 升级的Fragment:
Fragment拥有自己的生命周期,并且也可以接受它自己的输入事件

Fragment必须被嵌入到Activity中使用(就说明:Fragment自身的生命周期也会受到Activity的控制)
也就是说只有当Activity活动的时候,程序员才可以对其中的fragment进行操作

通常来说,创建Fragment类的时候需要实现的方法是:
{
1。onCreate()方法
2.onCreateView()方法(当Fragment绘制组件的时候回调该方法,该方法返回的一个View表示该Fragment显示的View)
3.onPause()方法
}

创建一个对应的ListFragment类,无需重写onCreateView()方法

对于需要添加一个Fragment组件的Activity组件也需要继承自support-fragment下面的FragmentActivity
否则会出现错误。

实现大屏显示图书详情fragment逻辑(自己总结):
首先先创建一个BookListFragment类(其中定义了一个interface接口,然后在Main2Activity中实现对应的callBacks接口,
在接口中实现的是对应于该特定Activity下的显示逻辑,那就是将来在BookDetailFragment中显示数据的传递逻辑(使用的是)
Bundle + setArguments() + getSupportFragmentManager().beginTransaction().replace(R.id.book_detail_container, bookFragment).commit();)
这里重点说明一下:对于callBacks接口来说,不同的Main2Activity下,有不同的实现逻辑。
然后是,每一次在点击了Main2Activity中的左边的listFragment中的项之后,都会在右边的detailfragment中的onCreate()方法
从而实现对于数据的更新。
对于listFragment来说,直接在Main2Activity中的xml文件中第一次渲染的时候就调用了对应的onCreate()方法。

/------------------------12-16号晚上学android学习笔记--------------------------/
page 268回顾笔记:
在Activity中使用fragment的时候可以使用id或者是tag属性来标记fragment对象

4.5.4 fragment管理与fragment事务:
fragmentManager对象可以实现的功能是:{
1.使用findFragmentById()方法或者是findFragemntByTag()方法可以获得
在该Activity中绑定的Fragment组件
2.使用popBackStack()方法奖Fragment从后台弹出,模拟用户按下back返回键的情形
3.使用addOnBackStackChangeListener()注册一个监听器,用于监听后台栈的变化
}

需要添加 删除 替换 Fragment对象可以使用的是FragmenTransaction对象
(该对象代表的是Activity对Fragment执行的多个改变)

在使用事务对象commit之前,可以使用addToBackStack()方法将该事务对象添加到backStack
中,然后当用户按下回退的时候,直接返回的是上一个fragment的状态

实例:开发兼顾屏幕分辨率的应用
为了实现这个目的可以在res/目录下为大屏幕,600dpi的屏幕建立相应的
资源文件夹:values-large,values-sw600dp,在该文件夹下建立一个名为
refs.xml的引用资源文件。
该资源文件专门用于定义各种引用项。

实现上述目的的逻辑(自我总结):
首先在res/目录下面创建一个values-large文件夹,里面创建一个资源对象主要是用来存放对应资源文件

<?xml version="1.0" encoding="utf-8"?> @layout/activity_main2 然后在Main2Activity中的onCreate()方法中使用if语句判断当前布局界面是否包含container,如果是, 就表明是大屏幕显示,此时出现的listFragment是存在于activity_main2.xml中的booklist文件(此时还需要设置 此时的book_list组件时单选模式,用来实现大屏显示的效果(即:左边点击右边显示详情)) 对于Main2Activity中的实现的onItemSelected()方法来说,需要判断是否是大屏显示,如果是和原来的逻辑一样 如果不是大屏显示,那么需要在点击了对应的list之后跳转到对应的bookDetailActivity中,用来实现详情的显示。 在BookDetailActivity的onCreate()方法中,使用如下语句来创建新的显示详情的bookDetailFragment对象 if(savedInstanceState == null){ //创建BookFragment对象 BookDetailFragment bookDetailFragment = new BookDetailFragment(); //创建Bundle对象 Bundle bundle = new Bundle(); bundle.putInt(BookDetailFragment.ITEM_ID, getIntent().getIntExtra(BookDetailFragment.ITEM_ID,0));
        //向Fragment传入参数        bookDetailFragment.setArguments(bundle);        //将指定fragment添加到book_detail_container容器中        getSupportFragmentManager().beginTransaction()                .add(R.id.book_detail_container,bookDetailFragment).commit();    }

并在其中实现了对应的actionBar点击之后的时间方法逻辑:
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {

    if(item.getItemId() == android.R.id.home) {        //创建启动的MainActivity的Intent        Intent intent = new Intent(this,Main2Activity.class);        //添加额外的Flag,奖Activity栈中处于Main2Activity之上的Activity弹出来        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);        //启动intent对应的Activity        startActivity(intent);        return true;    }    return super.onOptionsItemSelected(item);}

至此,根据屏幕大小实现不同的显示的逻辑得以实现。

//截止到2020-12-17日凌晨(0:45)
截止到(270)

/**------------------------------2020/12/20号下午学习笔记-----------------------------*/
Fragement生命周期:
其中的onAttach()、onCreate()等,方法在该fragment被加入到对应的activity中的时候只会别调用一次。
onCreateView()方法在每一次被回调的时候创建一次。

最常用的是就是onCreateView()方法。

4.7管理Fragment导航
Android的support-fragment中下提供了一个ViewPager组件,该组件非常方便实现分页导航。
ViewPager只是一个容器组件,其中显示的内容可由Adapter进行提供。
{其中使用的PageAdapter和FragmentPageAdapter子类(该子类是专门用于管理多个Fragment的)}

实例:结合ViewPager实现分页导航。
实例:结合TabLayout实现Tab导航。

由于tablayout需要appcompat主题的支持,需要设置实例的主题是dark.actionbar

//第5章:使用intent和IntentFilter通信
使用intent提供了一致的编程模型,并且可以降低耦合。
这里的Intent有点类似于spring、sprngMVC中的逻辑视图名的设计。
Android中包含三种重要的组件:
{
Activity
Service
BroadcastReceiver
}

//5.2 Intent的属性和intent-filter配置
1.Component属性
创建一个ComponentName需要指定包名和类名(这样就可以唯一指定一个组件类)
Android中的context代表了访问该应用环境信息的接口,包名是作为应用的唯一标识,故
context和包名是一一对应的关系。
制定了Component的intent已经明确了它将要启动的那个组件,因此这种intent被称为显示intent
没有指定Component的intent被称为隐式intent
隐式intent没有指定要启动那个组件,所以会启动符合条件的组件,但是具体是哪一个组件
是不确定的。

当intent使用显式intent启动另外一个组件的时候,被启动组件基本上不需要使用

5.2.2 Action Category属性与intent-filter配置
action表示该Intent所要完成的一个抽象“动作”,而Category则用于为了Action增加额外的附加类
信息。通常Action属性会与Category属性结合使用。

其中的action表示的只是一种抽象动作。例如:Activity查看view,实际上,action这个字符串
并不管具体查看什么,具体查看取决于Activity的配置。

一个intent对象只能包括一个action属性,当程序创建的时候,该intent默认启动Category
属性值为Intent.CATEGORY_DEFAULT常量的组件。

5.2.3 指定Action,Category调用系统的Activity
实例:查看并获取联系人的电话
实例:返回系统 Home桌面

5.2.4 Data,Type属性与intent-filter配置

Data属性通常表示向action属性提供需要操作的数据。
Data属性接受一个Uri对象,该Uri对象通常通过如下形式的字符串来表示。

content://com.android.contacts/contacts/1
tel:123

Uri字符串总满足如下格式:
scheme://host:port/path

Type属性用于指定该Data属性对应的MIME类型,这种MIME类型可以是任何自定义的MIME类型,
只要符合abx/xyz格式的字符串即可。

Type和Data会产生覆盖,那么如果不想覆盖就可以使用intent.setDataAndType();

在Manifest中为组件声明Data Type 属性都通过元素,改元素的格式如下:

如果 子元素只有android:port属性,没有指定android:host属性,那么android:port属性,没有指定android属性将不会起作用

//5.2.5 Extra属性

//5.2.6 Flag 属性

//第六章Android应用资源
从物理存在形式上分有三大类:
{
界面布局文件.xml文件
程序源文件(应用中的Activity,Service,BroadcastReceiver,ContentProvider)
资源文件(主要还是以xml文件为主,还可有图片形式等)
}

Android中除了res/目录下存放资源外,assets目录也用于存放资源。
一般来说,assets资源存放的是无法直接访问的原生资源,应用程序需要通过AssetsManager以
二进制流的形式来读取资源。

6.1 应用资源概述

6.1.1 资源的类型及存储方式
animator/ 存放的是属性动画的XML文件
anim/ 定义补间动画的XML文件
color/ 定义不同状态下颜色列表的XML文件
drawable/ 存放适应不同屏幕分辨率的各种位图文件
mipmap/ 存放适应不同屏幕分辨率的应用程序图标
layout/ 存放各种用户界面的布局文件
menu/ 存放为应用程序定义各种菜单的资源,包括选项菜单、子菜单、上下文菜单资源
raw/ 存放任意类型的原生资源(比如:音频文件、视频文件等)
values/ 存放各种简单值的xml文件
xml/ 存放任意原生的xml文件

drawable-ldpi分辨率,drawable-mdpi中等分辨率,drawable-hdpi高分辨率,drawable-xhdpi超高分辨率
drawable-xxhdpi超超高分辨率 等子目录。

如果开发时候所有的屏幕提供的是同一张图片,那么可以直接放在drawable目录下。

6.1.2使用资源
1.在源程序中使用资源清单项
2.在源代码中访问实际的资源(使用Resources类,可以称为:“资源访问总管家”)
3.在xml文件中使用资源

6.2 字符串 颜色 尺寸资源
6.2.1 颜色值的定义
6.2.2 定义字符串 颜色 尺寸资源文件

尺寸资源位于:/vlues资源文件夹下

6.2.3 使用字符串 颜色 尺寸资源文件
Android中允许定义boolean资源文件
在values/下面,使用:
true
并使用R.bool.name实现访问

还可以定义整形变量:
定义整形变量

6.3 数组资源
Android中并不推荐在程序源代码中定义数组

数组元素包含三种子元素:


6.4 使用drawable资源
(只要一份xml文件可以被系统编译成Drawable子类的对象,那么这份xml文件即可作为Drawable资源)
6.4.1 图片资源
Android要求图片资源的文件名必须符合java或Kotlin标识符的命名规则,否则不会生成索引。

6.4.2 StateListDrawable资源
该对象可以组织多个Drawable对象,当使用该对象作为目标组件的背景的时候,该对象所显示的Drawable
对象可以随着目标组件状态的改变而自动切换。

定义StateListDrawable对象所显示的Drawable对象会随着目标组件状态的改变而自动切换。

定义的StateListDrawable对象的根元素为:
该元素可以包含多个item元素。
实例:高亮显示正在输入的文本框

//LayerDrawable资源
该对象类似于StateListDrawable对象,也包含很多item元素,系统会按照这些Drawable对象的数组
顺序来绘制他们,索引最大的Drawable对象将会被绘制在最上面。

定义LayerDrawable对象的XML文件的根元素为:
可以包含有多个item元素
item元素中的属性如下:
android:drawable
android:id 该Drawable对象指定一个标识
android:button|top|left|button:用于指定一个长度值,用于指定将该Drawable对象绘制
到目标组件的指定位置。

实例:定制拖动条外观

//6.4.4 ShapeDrawable资源

该组件用于定义一个基本的几何图形,定义该对象的xml文件的根元素是元素
android:shape=“rectangle|oval|line|ring”

定义格式如下:
定义几何图形四个角的弧度
定义使用渐变色填充
定义几何形状的内边距
定义几何形状的大小
定义使用单色填充
定义为几何形状绘制边框

实例:椭圆形、渐变背景的文本框
//截至2020/12/22日晚上19:05(由于笔记太长,换下一个新的笔记本)

//-------------------------------------以上是截止到12-22日的笔记----------------------------------------------//

实例:实现椭圆形 渐变背景的文本框
//截至到上边目录显示所谓的318页

//12-23号晚上学习笔记
//6.4.5 ClipDrawable资源
ClipDrawable代表从其他位图上截取一个“图片片段”。

使用该对象的时候可以使用setLevel()方法来设置截取的区域的大小。
当设置的level值为0时表示截取的片段为空,当该值为10000的时候表示截取的图片是整张图片
//实例:徐徐展开的风景(可以使用该对象实现图片进度条)

//6.4.6 AnimationDrawable资源
以实现补间动画为例:需要定义的是AnimationDrawable对象的属性是:{
alpha:设置透明度的改变
scale:设置图片进行缩放变换
translate:设置图片进行位移变换
rotate:设置图片进行旋转
}

该对象需要定义在anim文件夹中
上面属性包括在内还可以定义一个interpolator属性,该属性指定动画的变化速度
如果程序想让再一个set下面的所有元素使用相同的动画速度,那么可以指定属性:
android:shareInterpolator=“true”

为了在java代码中调用动画对象可以使用AnimationDrawableUtils对象

实际上想要保存对应的状态需要设置set中的属性fillAfter="true"即可。

6.5 属性动画资源
Animator是一个抽象类,实际开发中通常使用其子类
定义属性动画资源可以以{set objectAnimator animator}中的任意一个作为根元素

//实例:不断渐变的背景色

//6.6 使用原始的XML资源
6.6.1 原始资源的路径
原始xml资源一般保存在/res/xml路径下(但是开发者需要自己创建xml文件夹)
使用XmlResourceParser getXml()获取xml文档
使用InputStream openRawResource(int id);获取xml文档对应的输入流

大部分时候都可以直接调用getXml()方法来获取XML文档
Android默认使用内置的pull解析器来解析xml文件(pull解析器是一个开源项目)

pull解析器方式有点类似于SAX解析,都采用时间驱动方式来进行解析。
当pull解析器开始解析之后,开发者可不断调用pull解析器的next()方法获取下一个解析事件
当处于某一个标签时,可以调用XmlPullParser的nextText()方法来获取文本节点的值。

6.6.2 使用原始的XML文件

//6.7 使用布局资源

//6.8 使用菜单资源

//6.9 样式 和 主题 资源
6.9.1 样式资源
一个样式相当于多个格式的集合,其他UI组件通过style属性来指定样式

样式可以使用继承,继承之后子样式会覆盖对应的父样式

6.9.2 主题资源
主题与样式的区别是:
主题不可以用在单个view组件中
主题定义的格式应该是改变窗口外观的格式,例如:窗口标题,窗口边框等。

//6.10 属性资源
使用的情景:如果用户开发自定义的view组件需要指定属性,那么就需要属性资源的帮助了
属性资源文件的跟元素也是:元素,改元素包含如下两个属性。
{
attr
declare-styleable元素:定义一个styleable对象,每个styleable对象就是一组attr属性的集合
}

在自定义组件的构造器中通过AttributeSet对象来获取这些属性。

//6.11 使用原始资源
只要是Android没有为之提供专门的支持,这种资源都是原始资源
位于:raw(会在R中生成一个索引项)下 或 assets(是更彻底的资源,需要通过AssetsManager资源)下

//6.12 国际化
6.12.1 为Android应用提供国际化资源
我们需要在res/values/资源的文件夹下面保存程序中用到的字符串消息,为了给这些消息提供不同
国家、语言的版本,开发者需要为values目录添加几个不同的语言国家版本。
不同的values文件夹的命名方式为:
values-语言代码-r国家代码

在不同的国际化资源中所有的消息的key是相同的,但是对应的value不同

//6.12.6 国际化Android应用
定义的国际化资源可以自动发挥作用

//6.13 自适应不同屏幕的资源
尽量使用dp为分辨率单位

//6.14 本章小结

/--------------------第七章 图形于图像处理------------------/
//截止到2020/12/23号晚上22:02 (page 343)

/-----------2020/12/22号晚上23:58分开始学习------/
7.1 使用简单图片
//使用Drawable对象
7.1.2 Bitmap 和 BitmapFactory
BitmapDrawable里封装的图片就是一个Bitmap对象。
可以调用BitmapDrawable的构造方法将bitmap对象包装成一个BitmapDrawable对象

获得bitmapDrawable对象所包装的bitmap对象可以通过getBitmap();来获得

7.1.3 Android 新增的ImageDecoder
使用该api对图片解码的时候,程序返回一个AnimatedImageDrawable对象,调用该返回
对象的start()方法,可以开始执行动画。

截至到2020/12/24—凌晨0:44—7.2绘图 (page 347/511)

//2020/12/24号下午学习笔记7.2 绘图
7.2.1 绘图基础Canvas Paint等
Android中应该继承view类并重写其onDraw()方法
Canvas代表“依附于”一个指定View的画布
Canvas提供了一个Paint对象,因此Paint类主要用于设置绘制风格,包括画笔颜色等
Canvas中的另一个apiPath,代表任意多条直线连接而成的任意图形,Canvas根据path绘制时
可以绘制出任意的图形。

调用canvas.drawPath(path,paint)方法实现按照对应path路径绘制图片

前期只要美工把应用程序所需要的图片制作出来后期看发的时候直接使用就可以

7.2.2 Path类
该类可以预先将N个点连成一条“路径”,然后调用Canvas的drawPath()方法即可以沿着路径绘制
图形。

Android中还为路径绘制提供了PathEffect来定义绘制效果。

实现绘制文本的逻辑是:
//绘制路径
paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(paths[2],paint);
//沿着路径绘制一段文本
paint.setStyle(Paint.Style.FILL);
canvas.drawTextOnPath(drawStr,paths[2],-20f,20f,paint);

7.2.3 绘制游戏动画
所谓的动画就是不断调用onDraw()方法,在调用的同时需要更改一部分的数据。
而通知View实现重新绘制需要调用的是invalidate(在ui线程中) 或 postInvalidate(在不是ui的线程中)

//实例采用双缓冲实现图画板
所谓的双缓冲技术其实很简单:当程序需要在指定的View上进行绘制时,程序并不直接绘制到该
View组件上,而是先绘制到内存中的一个Bitmap对象上(这就是缓冲区),等到内存中的Bitmap绘制
好之后,再一次性地将Bitmap绘制到View组件上。

//实例:弹动的小球

7.3 图形特效处理
7.3.1 使用Matrix控制变换
使用该对象的步骤如下:
1.获取Matrix对象
2.调用Matrix的方法进行平移、旋转、缩放、倾斜等
3.将程序对Matrix所作的变换应用到指定图像或组件
Matrix 还可以用于对于View组件的平移、旋转和缩放。

Canvas调用drawBitmap()方法可以实现绘制bitmap的时候应用Matrix变换

7.3.2 使用drawBitmapMesh扭曲图像
该函数中的各种关键参数如下:
bitmap:指定需要扭曲的源位图
meshWidth:该参数控制在横向上把该源位图划分成多少格
meshHeight:该参数控制在纵向上把该源位图划分为多少格
verts:该参数是一个长度为(meshWidth + 1) * (meshHeight+1) * 2的数组
vertOffset:控制verts数组中从第几个数组元素开始才对bitmap进行扭曲

//7.3.3 使用Shader填充图形
前面介绍Paint时提到的Shader包含了一个setShader(Shader s)方法
该方法控制画笔的绘制效果

//7.4 逐帧动画(Frame)
7.4.1 逐帧动画AnimationDrawable与逐帧动画
只需要在元素中使用 子元素定义动画的全部帧,
并指定各真的持续时间即可。

其中item中的属性oneshot设置为true表示不循环播放

在Java代码中先创建AnimationDrawable对象,然后调用addFrame()向该动画中添加帧
一旦程序获取了AnimationDrawable对象之后,接下来就可以用ImageView把AnimationDrawable
对象显示出来(习惯上把AnimationDrawable设置程ImageView的背景即可)

//实例:在指定点爆炸(实现逻辑:
先在MainActivity中创建FrameLayout对象,然后设置背景,然后加载myView组件,并设置对应的
动画资源。
获取动画对象(目的是在后期的为frame设置的监听器中可以调用动画对象开始和停止动作)
自己定义的MyView对象,主要是在onDraw()实现部分逻辑的目的是,为了使得播放到最后一帧时
自动停止播放并将view组件设置为不可见。

//7.5 补间动画(Tween)
补间动画是指:开发者只需要指定对应的开始和结束图片,中间部分的图片系统自动补齐。
//7.5.1Tween动画与Interpolator

//Interpolator根据特定算法计算出整个动画所需要动态插入帧的密度和位置。

为了在动画资源文件中指定补间动画所使用的Interpolator,定义补间动画的集合元素
支持一个android:interpolator属性。

7.5.2 位置 大小 旋转度 透明度改变的补间动画
//实例:蝴蝶飞舞

7.5.3 自定义补间动画
自定义补间动画需要继承Animation类,关键是重写该基类的applyTransformation();方法

Camera提供了一个三维空间变换的工具,功能比matrix更加强大。

//截止到378/511 at 2020/12/25 0:28

/----------------------2020/12/26号晚上 21:05 学习笔记------------------------/
接着7.5.3学习:
自定义补间动画需要继承Animation类,关键是重写该基类的applyTransformation();方法
其中的参数:
{
interpolator:表示动画的时间进行比。不管动画实际的持续时间如何,该参数都是从
0-1进行变化的。

transformation:代表了补间动画在不同时刻对图形或组件的变形程度。(该对象可以)
实现对于图片或者视图的控制)
}

对于Camera类中的常用的方法有:
1.getMatrix()将Camera所作的变换应用到指定的matrix上
2.rotateX 表示使目标组件沿X轴旋转
3.rotateY 表示使目标组件沿Y轴旋转
4.rotateZ 表示使目标组件沿Z轴旋转
5.translate 表示使得目标组件在三维空间里进行位移变换
6.applyToCanvas() 表示将刚才进行的变换应用到对应的canvas中

对于android手机的三维空间坐标系来说
平行于屏幕从左到右是X轴
平行于屏幕从下到上是Y轴
垂直于屏幕从里到外是Z轴

//自己总结的关于实现自定义的三维动画逻辑:
首先是自定义一个类继承animation类,然后定义对应的构造方法,设置其中的initialize()方法
中的逻辑,设置其中的applyTransformation()方法实现的是对于出来组件的时候动画渲染逻辑
该方法中的重要逻辑是:{先调用camera.save()方法,然后根据interpolationTime控制三个坐标轴上的偏移量
然后使用camera.rotateX 与 camera.rotateY()方法设置X和Y轴上的旋转角度,然后获取Transformation
参数的Matrix对象。并使用camera.setMatrix(matrix);设置对象,然后调用matrix.preTranslation() 及 matrix.preTranslation()
;方法;最后调用camera.restore()方法实现保存 }

//7.6 Android 8 增强的属性动画:
从大体上来说,属性动画是增强版的补间动画,但是属性动画更加强大
补间动画只可以对UI组件执行动画,但是属性动画几乎可以对任何对象执行动画(不管他是不是显示在屏幕上)

//属性动画的API
Animator 提供了创建属性动画的基类(基本上不使用该类)
ValueAnimator属性动画主要的时间引擎,负责计算各个帧的属性值。

属性动画主要由两方面组成{
计算各帧的相关属性值
为指定对象设置这些计算后的值
}

ValueAnimator主要负责的是第一个方面的值

ObjectAnimator 是ValueAnimator的子类,用于组合多个Animator,并指定多个ANimtor
是按次序播放还是同时播放。

除此之外,属性动画还需要一个Evaluator该工具类控制属性动画如何计算属性值。

Android 8 还为AnimatorSet新增了几个方法:{
reverse(); //反向播放属性动画
long getCurrentPlayTime(); 获取动画的当前播放时间
setCurrentOlayTime(); 设置动画的播放时间
}

通过设置setCurrentPlayTime()方法可以使得直接在对应的时间点进行播放

测试实现:
1.ValueAnimator创建动画
四个步骤:
调用其中的ofInt() ofFloat() ofObject() 静态方法创建ValueAnimator实例
调用ValueAnimator的setXxx()方法设置动画持续时间,插值方式,重复次数。
调用ValueAnimator的start()方法启动动画
为ValueAnimator注册AnimatorUpdateListerner()监听器,在该监听器中可以监听
ValueAnimator计算出来的值的改变,并将这些值应用到指定对象上。

2.使用ObjectAnimator创建动画
该类可以直接将其父类中计算出来的值直接用到指定对象的属性上

与ValueAninmator不同的是,使用ObjectAnimator的注意点:
要为该对象对应的属性提供对应的setter方法
如果是对于其中方法的ofInt()等中的参数时,其中的values数组如果只提供了
一个值,那么该值会被认为是结束值,该对象还应该为该属性提供一个getter()方法,该getter
方法返回的值就是开始值。
若该动画对象不是view,为了显示动画效果,还需要在onAnimatorUpdate()事件监听方法中
调用.invalidate()方法,实现对应的组件的更新。(view组件会自动调用故不需要指定)

//7.6.2 使用属性动画
属性动画可以既可以用于UI组件,也可以用于对应的普通的对象

定义属性动画有两种方式:
使用ValueAnimator 或 ObjectAnimator 的静态工厂来创建动画
使用资源文件来定义动画

具体步骤如下:
创建ValueAnimator 或 ObjectAnimator 对象–既可从XML文件中加载该动画资源;也可以使用两个类的
静态构造方法来创建动画。
根据需要可以为animator设置对应的属性。
如果需要设置监听animator的动画开始事件等,应该为animator对象设置事件监听器。
如果有多个动画需要按次序或同时播放,则应该使用AnimatorSet组合这些动画。
调用Animator对象的start()方法启动动画。

//---------------2020/12/27晚上学习笔记(从p 383 开始学习)---------------------
-0x1000000 + red << 16 | (green << 8) | blue
对于上式的解释:
oxff000000代表透明度为ff,也就是完全不透明
red代表一个0-255的随机整数,但是这个整数要添加到oxff000000加粗
的两个位上,也就是说要将red的值左移(16位,对应为十六进制的4位)
,这就是red << 16的原因

green 代表的是0-255的随机整数,但是这个整数要添加到0xff000000张加粗的两个位置

blue…

//实例:大珠小珠落玉盘

//使用SurfaceView实现动画
View存在的缺陷:
{
View缺乏双缓冲机制
当程序需要更新view上的图片时,程序必须重绘View上显示的整张图片
新线程无法直接更新view组件
}

7.7.1 SurfaceView绘图机制
surfaceView会和surfaceHolder结合使用
使用getHolder()可以得到对应的holder对象

surfaceHolder对象提供下面的方法来获得canvas对象:
lockCanvas()锁定整个SrufaceView对象,获取该SurfaceView上的canvas
lockCanvas(Rect dirty) 锁定SurfaceView上的Rect划分区域,获取该区域上的canvas(该方法只更新对应的
圈出来的区域))

需要说的是当需要调用surfaceHolder的unlockCanvasAndPost()方法之后,该方法之前所绘制的图形所处于缓冲区中
下一次lockCanvas()方法锁定的区域可能会“遮挡”它。

//测试实现使用surfaceView动画效果实现
(实现逻辑(自己总结:{
首先创建一个FishView类,然后在构造方法中得到绑定的holder对象,并将本view
放进去,然后创建一个bitmap对象得到对应的资源文件;初始化对应的数组,然后
得到;实现对应的resume() pause()方法 surfaceCreated()方法 surfaceChanged()方法
创建一个心得thread类,然后再其中的run方法中得到fishView类的getHolder()方法,可以开始执行动画。
锁定SurfaceView并返回到绘图的Canvas,然后绘制背景图片,然后是判断鱼是否
已经出了屏幕,并使用matrix来控制鱼的旋转角度和位置,然后调用canvas.drawBitmao()
表示在画布上绘制对应的图片(并设置了旋转的角度和对应的速度,然后是解锁canvaas)并渲染)
当前图像,设置线程沉睡0.06秒,然后是设置如下代码即可:

void requestExitAndWait(){
//把这个线程标记为完成,并合并到主程序线程中
done = true;
try{
join();
}catch (Exception e){
e.printStackTrace();
}
}

    void onWindowResize(int w,int h){        //处理SurfaceView的大小改变事件        System.out.println("w:" + w + "===h:" + h);    }


}))

//截止到392 页面学习笔记 (2020/12/27晚上11:30)

/-----------------------2020/12/28 晚上学习笔记 (从392开始)----------------------------/
surfaceView来说和view的显著区别是,线程可以使用surfaceView对象的surfaceHolder对象来绘制图像。

//基于surfaceView开发示波器
重点:考虑surfaceView组件的情况是:程序或游戏界面的动画元素很多,并且很多动画元素的移动都需要
通过定时器来控制,就可以考虑该组件。

//2020/12/28号 22:30开始 晚上学习笔记
/----------------------------第八章 Android 数据存储与IO-----------------------/
Android中Sqlite数据库对应于一个文件(没有后台进程)
8.1 使用SharedPreference对象存储数据
使用情景:少量的数据保存 数据格式简单 都是普通的字符串 标量类型的值等
SharedPreference对象主要是保存类似于配置信息格式的数据,主要是key-value形式。
通过SharedPreference.Editor对象才可以写进去数据
使用Editor对象的.apply()方法可以更新数据。(使用commit会立即修改但是使用apply()不会阻塞前台进程)

为了得到SharedPreference对象可以是哟个context对象的getSharedPreference();方法得到
想要将本应用中的数据暴漏出来的话,Android 4.2 开始推荐使用ContentProvider对象来访问。

8.1.2 SharedPreference对象的存储位置和格式
使用该对象保存的数据放在:
/data/data//shared_prefs 目录下,并总是以xml格式来保存

//8.2 Filr存储
8.2.1 openFileOutPut 和 openFileInput
除此之外,context提供了几个方法来访问应用程序的数据文件夹
getDir()方法获得子目录
file getFileDir()获得绝对路径
String[] fileList返回应用程序数据文件夹下面的全部文件
deleteFile()删除指定文件

使用context存储的都是再手机的内置存储中

读写SD卡上的文件步骤如下:
1.请求动态获取读写SD卡的权限
2.调用Environment的getExternalStorageDirectory()方法来获取外部存储器(SD)的目录
3.使用FileInputStream FileOutputStream FileReader或FileWriter命令来创建虚拟存储卡,
4.为了读写SD卡上的数据,必须在应用程序的清单文件中设置权限:

//截止到2020/12/29号晚上23:32min

/---------------------------2020/12/30号下午13:46开始学习笔记--------------------/
from 404 begin

//实例:SD卡文件浏览器

8.3 SQLite数据库
该数据库是一个轻量级的数据库,是一个嵌入式的数据库引擎
该数据库只是一个文件

8.3.1 SQLiteDatabase(底层就是一个数据库文件)简介
该对象的作用有点类似于JDBC的connection接口

该对象支持的查询方法返回的都是一个Cursor对象

当移动到指定行之后,接下来就可以调用Cursor的getXxx()方法获取该行的指定列的数据了。

8.3.2 创建数据库和数据表

8.3.3 SQLiteOpenHelper类
实际项目中很少使用直接的openOrCreateDatebase()静态方法来打开数据库,通常都会继承该类并通过
该类的getReadableDatabase() 和 getWritableDatabase()方法打开数据库

该类可以用来进行版本更新和数据库的创建。

重写其中的onCreate()方法,该方法是只有在第一次生成数据库表结构的时候调用,在里面可以
设定数据库表的结构(也可以添加一些数据进去)

当用户创建一个helper对象时,程序员指定数据库版本号(当某一次创建的时候版本号高于原来的版本时,
系统会自动进行必需的表结构的更新)

当数据库升级失败的时候,需要先进行转储,然后对数据表进行更新,之后再保存回来原来的数据。

getWritableDatabase()以写的方式打开数据库,然后使用getReadableDatabase()方法,先以读写的方式打开
数据库,如果数据库的磁盘已经满了就会打开失败,当打开失败的时候会继续尝试以只读的方式打开数据库。

//8.3.4 使用SQL语句操作数据库
使用execSQL()方法可以执行任意的SQL语句,包括带有占位符号的Sql语句。
当需要执行查询语句的时候,可调用rawQuery()方法

//8.3.5 使用sqlite3工具
在android SDK 的 platform-tools 目录下提供了一个sqlite3.exe 文件,他是一个简单的sqlite数据库管理工具
类似于MySQL提供的命令行窗口。

sqlite3常用命令如下:
.database查看当前的数据库
.tables 查看当前数据库里的数据表
.help 查看sqlite3支持的命令

再将sql语句真正地插入到数据库中的之前先在sqlite3这个工具类中测试是否是有语法错误

需要说明的是sqlite内部只支持NULL,INTEGER,REAL(浮点数),text(文本),和BLOB(大二进制对象)

但实际上其他的类型该数据库也会接受只不过是在保存到底层数据库中的时候会转换成上述的5种数据。

同时,sqlite3还会把各种类型的数据保存到任意类型的字段中,开发者可以不用关心声明该字段所使用的数据类型。
但是有一种例外情况:
定义为:INTEGER PRIMARY KEY 的字段 只能存储64位整数,当向这种字段中保存除了整数之外的其他类型的数据时
SQLite会产生错误。

//8.3.6 使用特定的方法操作SQLite数据库

insert方法插入一行记录使用ContentValues存放(该类型类似于Map)
其中的参数:(String table,String nullColumnHack,COntentValues values)
第二个参数只有当vlues是null时,会添加除了主键之外其他字段都是null的记录。(第二个参数指定的是插入的列名)

8.3.7 事务
当结束事务的时候,会根据前边SQLiteDatabase对象是否调用了setTransactionSuccessful()方法来设置事务标志
如果事务执行中调用该方法设置了事务成功,则提交事务,否则程序将会回滚事务。

//8.3.8 SQLite数据库最佳实践建议

1.关于打开数据库的方式
通过前面介绍我们知道,打开SQLite数据库有两种方式
直接通过SQLiteDatabase的静态方法打开数据库;
通过SQLiteOpenHelper类的子类来打开数据库(强烈建议使用第二种方式)

2.关于SQLite的用途:
不要把大量的数据都放到SQLite中;如果需要保存大量数据需要设置服务器端。
SQLite可以缓存部分的服务器端的数据。

4.关于操作数据库的方式
使用execSQL()原生操作,rawQuery()方法执行原生操作SQL语句

使用insert update delete query 方法执行SQL语句

强烈建议使用OEM工具:Ormlite GreenDao LitePal等(有Hibenate经验优先)

//8.4 手势
Android提供了手势检测并为手势检测提供了相应的监听器
Android允许开发者自己添加手势,并提供了相应的API识别用户手势

8.4.1 手势检测
Android为手势检测提供了一个GestureDetector类,该类的一个实例代表了一个手势检测器
创建该类时需要传入一个GestureDetector.OnGestureListener实例(该实例就是一个监听器,负责给用户的手势提供相应)

使用Android手势的步骤:
使用GestureDetector对象(该对象必须实现的是监听器接口)
为应用程序的Activity的TouchEvent事件绑定监听器(将Activity上的touchEvent事件交给GestureDetector处理)

//实例:通过手势缩放图片
实现逻辑自己总结:
首先需要定义手势检测器变量、Bitmap变量、定义图片的宽和高、记录当前的缩放、控制图片缩放的matrix对象
然后定义一个手势检测器初始化,先得到对应的vx滑动速度,根据vx进行图片的缩放,然后重置matrix对象
缩放matrix对象,然后是得到BitmapDrawable对象,判断是否已经回收,如果没有回收就回收
然后根据新的matrix创建新的bitmap位图对象,然后设置imageView显示位图对象,最后是
在acitivity中的onTouchEvent()方法中返回一个手势检测器的onTouchEvent(event);结果。

处理多点触碰也通过重写onTouch()方法进行实现,通过该方法中的MotionEvent参数的getPointerCount()方法可判断
触碰点的数量

通过MotionEvent的getActionMasked()方法来判断触碰事件的类型。

//8.4.2 增加手势

Android中使用GestureLibrary来代表手势库,并提供了GestureLibraries工具类来创建手势库

Android还提供了一个手势编辑组件:GestureOverlayView组件,用户可以在组件上绘制的不是图形而是手势

该组件还提供了三个监听器
onGestureListener onGesturePerformedListener onGesturingListener (分别表示手势开始 结束 完成 取消等事件)
第二个是最常用的监听器,它用于在手势事件完成时提供相应。

注意:一个组件不是标准的视图组件的时候因此在界面布局中使用该组件时需要使用全限定类名

该组件中使用的android:gestureStrokeType="multiple"属性表示的是设置为single(手势只有一笔完成)
使用multiple表示多笔完成

实例:实现存储用户手势逻辑
自己总结逻辑:

1.创建GestureOverlayView 以及 Gesture对象
初始化手势编辑视图对象,为绘制手势对象绑定监听器并设置动态获得SD卡权限

2.然后在activity中的内部方法中实现保存手势的逻辑:
判断用户是否同意了获得读写权限,如果是,那么获得对应的布局文件,然后在布局文件中添加一个imageView组件
接着将刚才在GestureOverlayView组件中绘制的图像以bitmap(位图)显示在imageView组件中,然后创建一个对话框
将刚才准备好的布局组件显示到对话框中,然后设置确定的实现逻辑是:保存手势到手势库中。

//截止到2020.12.30 晚上 22:11  (p432)

//---------------截止到2020.12.31 晚 20:38开始学习笔记-------------//

//2020.12.31 晚 20:40 学习笔记:
8.4.3 识别用户自定义手势

GestureLibrary提供了recognize(Gesture ges) 方法来识别手势,该方法返回该手势库中所有与ges手势匹配的手势,
返回的结果是一个ArrayList 集合,其中的Prediction对象封装了匹配的属性信息,其中的name表示匹配的手势
名字,其中的score表示匹配的相似度。

//截止到2020.12.31 晚上 21:42 止 //截止到433页

//2021.1.1 上午 1:40 学习笔记:---------------------------------------------
//8.4.3 识别用户手势
识别用户手势逻辑自己总结:

定义手势编辑组件变量和手势变量;在onCreate()方法中请求用户允许读写SD卡权限;在activity中自带的方法中处理显示
组件逻辑(包括:判断用户是否同意授权,如果同意那么先得到之前创建的手势库,然后手势库装载,是否成功;如果成功,
获取手势编辑组件,给手势编辑组件增加监听器;在监听器中实现:{
创建一个List 数组用来保存返回的识别结果,然后创建以恶个List result 数组对象,实现的是
用来生成需要显示的语句;然后遍历得到的返回数组(当识别度大于2.0的时候生成对应的结果,然后判断自己定义的结果数组长度
是不是大于0的,如果是,则生成一个适配器用于准备结果;然后创建一个带有List的对话框来显示所有的手势;至此逻辑总结完毕)
})

8.5 让应用说话(TTS)(TextToSpeech)
Android提供的自动朗读功能。
android中还支持将文本生成的音频录制成音频文件。
使用的时候需要提供一个OnInitListener监听器,该监听器负责监听TextToSpeech的初始化结果。
在程序中获得了TextToSpeech对象之后可以使用setLanguage()方法来设置该TTS语音引擎应使用的语音,国家选项。
目前内置的TTS引擎对中文支持的效果不好,但是科大讯飞的TTS引擎对中文支持比较好。

TextToSpeech的两个常用的方法是:
speak() 该方法只是朗读文本
synthesizeToFile() 该方法可以将转换得到的音频保存到声音文件中。

speak()方法中的queueMode参数指定TTS发音队列模式,该参数支持下面的两个常量:
TextToSpeech.QUEUE_FLUSH 表示当TTS调用speak()方法时,会中断当前实例正在运行的任务。
TextToSpeech.QUEUE_ADD 表示将当前的语音加入到对应的语音队列中,并等待当前发音队列中的任务执行完之后才执行当前的语音。

//测试TTS

//8.6 本章小结

/-------------------------------第9章 使用ContentProvider实现数据共享--------------------------------/
ContentProvider是Android提供的用于实现不同应用之间数据交互的标准API
而其他的应用程序则可以使用ContentResolver对象来操作对应的数据。

//截止到 2021.1.1 下午 13:08 (p439)

//2021.1.1 下午 16:56 开始学习笔记:
ContentProvider也是android四大组件之一,也需要在Manifest.xml文件中进行配置

只要是一个应用提供了Contentprovider接口,那么不管该应用是否启动,外部的应用都可以访问数据等。

9.1 数据共享标准:ContentProvider组件
该对象提供Uri形式对外提供数据

9.1.1 ContentProvider简介

开发一个ContentProvider的步骤:
1.定义一个ContenProvider类,需要继承Android提供的ContentProvider基类。
2.向Android系统中注册一个ContentProvider,注册的时候需要指定一个Uri(也就是authorities)即可
只要在元素下添加子元素即可。

对于自己定义的provider类除了需要继承ContentProvider类之外还需要提供如下几个方法:
1.onCreate() 在ContentProvider创建后调用
2.insert()根据uri插入vlaues对应的数据
3.delete()根据uri删除selection条件所匹配的全部记录
4.update()根据uri修改selection条件所匹配的全部记录
5.query() 根据uri查询得到selection条件匹配的全部记录 其中的projection就是一个列名列表,表明只选择出来指定的数据列
6.getType()方法用于返回当前uri代表的MIME类型。

//9.1.2 Uri简介:
一个Uri分为三个部分:
content//:这个部分是Android的ContentProvider规定的,就像上网的协议默认是http一样,用来暴露对于contentprovider的访问
org.wyy.providers.testprovider 这个部分就是ContentProvider的authorities
words资源部分(或者说数据部分)。当访问着需要访问不同资源时,这个部分是动态改变的。
在words/2 表示的是words数据中的ID为2的数据
上述的Uri借本上遵循的是RESTFul

使用uri对象的Uri.parse(""); 可以实现将String类型的字符串转换为:Uri

9.1.3 使用ContentResolver操作数据(用来操作contentProvider对象中保存的数据)
该对象可以使用context对象的.getContentResolver()方法得到对应的contentResolver对象

一般来说contentProvider是单例的

//9.2 开发contentProvider

9.2.1 contentProvider 和 contentResolver的关系

9.2.2 开发contentProvider子类
需要注意的是:在ContentProvider子类中实现的各种方法不是自身调用的。
而是提供给其他应用程序调用的。

9.2.3 配置ContentProvider
Android要求的是所有的应用组件都需要进行显示配置
其中设置的属性android:exported属性表示指定该provider是否可以被调用(设置为true可以被调用)

其中设置的readPermission writePermission 属性表示设置需要的权限
或者使用permission来设置
如果不设置权限那么表示可以被所有的APP访问

//截止到2021.1.1 22:54为止(p444)

/--------------------------------2021.1.2 12:08 开始学习笔记--------------------------------------/
9.2.4 使用ContentResolver调用方法
其中update() delete()方法返回的是更新或删除的数据数目

//9.2.5 创建ContentProvider的说明
实际上,为了确定该ContentProvider实际可以处理的Uri,以及确定每一个方法中Uri参数所操作的数据,Android系统提供了
UriMatcher工具类

其中的addUri(String authority,String path,int code)方法用于向该对象注册Uri其中的authoirity和path组合成了一个Uri,而code代表该Uri对应的标识码。

int match(Uri uri) 可以根据前面注册的uri来判定指定uri对应的标识码。找不到返回-1。

可以使用ContentUris.parseId(uri)来获得对应的ID值

//实例:开发contentProvider实现单词本

建议:使用工具类定义常量可以实现对访问路径的定义。

在ContentProvider中使用sqlite3保存对应的数据。

注意的是:在使用contentProvider更新完数据之后需要使用getContext().getContentResolver().notifyChange(uri,null);
通知数据已经改变

//9.3 操作系统的ContentProvider
实际上,android中本身提供了很多的ContentProvider, 例如联系人信息,系统的多媒体信息等。

开发者可以使用ContentResolver对象来获得系统中的数据

通过查阅对应的API文档可以获得想要访问的系统contentProvider对象中的数据对应的Uri

//9.3.1 使用ContentProvider对象管理联系人

Android中提供了Contacts应用程序来管理联系人,而且还提供了provider

android要求访问本身的contentProvider的时候需要用户动态获取权限

//重点:
android中的联系人信息是一张表(主表),开发者需要先向该主表中插入记录。电话信息是一张从表(参照联系人表),
E-mail信息单独是一张从表(参照联系人表),因此开发者可以分别为一个联系人添加多个电话信息、多个E-mail信息。

//自己总结实现联系人信息获得的逻辑:
先给一个按钮注册一个请求获得动态访问权限的按钮,然后点击之后到activity中的onRequestPermmissionResult()方法,
在该方法中定义两个数组一个是存储联系人姓名的数组一个是用来存放联系人对应的数据的数组List
details,使用contentResolver对象的.query()方法可以根据系统提供的联系人Uri得到对应的数据,
,然后是使用while循环遍历所有的联系人{
先得到联系人的ID,得到联系人的名字,然后向names集合中添加名字,使用contentResolver对象的查询联系人的电话号码,
其中可以设置条件是使用的联系人的CONTACT_ID,然后创建一个detail数组,向该数组中通过遍历将本联系人的电话号数据
加入其中,然后得到联系人的E-mail,遍历该数组得到e-mail中对应的多个E-mail地址数据,并向该detial数组中添加
对应的e-mail地址。最后,将每一次循环得到的detail数组添加到对应的details数组中。
接着是获得布局视图,然后从该布局中得到对应的ExpandableListView对象,然后创建一个ExpandalbeListViewAdapter对象
其中的设置getGroupCount()返回的是names数组的长度;其中的getChildrenCount()返回的是details中对应的position数组的长度;
其中的getGroup()获得对应的position处的组数据;其中的getChild()方法得到的是对应位置的子列表项的数据(也就是电话号码和对应e-mail数据)
其中的getGroupId()方法得到的是第几组;其中的childId()方法得到的是对应的第几个子数据;其中的getGroupView()
方法决定的是每一个组选项的外观;其中的getChildView()方法得到的是每一个子选项的外观。最后为获得布局中的ExpandableListView组件
设置对应的监听器,从而实现了结果的显示。
}

//9.3.2 使用ContentProvider管理多媒体内容
获得对应的多媒体的Uri即可实现管理。

注意在使用RecyclerView的时候,需要给该对象设置一个recyclerView.
show.setLayoutManager(new LinearLayoutManager(this));
这样的话才可以显示出来组件。

//9.4 监听 ContentProvider的数据改变
9.4.1 ContentObserver简介:
需要向指定的uri注册一个ContentObserver监听器
提供了registerContentObserver(Uri uri,boolean notifyForDescendets,ContentObserver observer);

中间的参数设置为:false时候表示必须时Uri(content://abc)变化的时候才会监听到改变;
但是设置为true的时候表示尽管有:content://abc//xyz 或 content://abc//foo 也会监听到变化。

9.5 小节

/----------------------------------2020.1.2 17:46 晚上看笔记------------------------------------/
/第十章 Service 和 BroadcastReceiver
Service是四大组件中与Activity最相似的组件
Service一直在后台运行,绝对不会到前台来,有自己的生命周期。
BroadcastReceiver组件就像一个全局的事件监听器,只不过它用于系统发出的Broadcast。
通过使用该对象可以在不同的应用程序之间通信。

10.1 service简介

10.1.1 创建、配置Service

service组件生命周期方法:
IBinder onBind() 该方法时Service子类必须实现的方法。该方法返回的是一个IBinder对象,应用程序可通过该对象与
service组件通信
onCreate() 方法该service第一次被创建后将立即回调该方法。
onDestory() 该Service被关闭之前将会调用该方法
onStartCommand() 该方法的早期版本是void onStart(Intent intent,Int startId) 每次客户端调用startService(Intent)
方法启动该Service时都会回调该方法。

onUnbind() 当该service上绑定的所有客户端断开连接时将会回调该方法。

在onCreate() 或 onStartCommand() 方法中定义相关业务代码

设置Service组件中的process表示该组件处于某一个进程中(默认是处于该App所在的进程中)
android中的四大组件都可以通过该属性指定运行的进程

启动Service由两种方式:
1.通过context的startService()方法 该方法启动的service与访问者之间没有关联,即使访问者退出了,该service也仍然运行
2.通过context的bindService()方法,使用该方法启动service访问者与service绑定在一起,访问者一旦退出,service就终止。

10.1.2 启动和停止service

多次启动一个service不会多次调用onCreate()方法,但是会多次调用onStartCommand()方法

10,1.3 绑定本地service并与之通信
如果Service和访问者之间需要进行方法调用或交换数据,则应该使用bindService()方法 和 unBindService()方法。
bindService()方法中的参数:
其中的ServiceConnection conn,表示该对象用于监听访问者与service之间的连接情况。当访问者与service之间
连接成功时将回调该ServiceConnectionn对象的onServiceConnected(ComponentName name,IBinder service) 方法,当
service所在的宿主进行由于异常中止或其他原因终止,导致该service与访问者之间断开连接时回调该serviceConnection
对象的onServiceDisconnected(ComponentName)方法。(但是当调用者主动断开连接的时候onServiceDisconnected()方法不会被调用)

其中的参数flags表示绑定时是否自动创建service,该参数为0时,表示不自动创建;该参数为BIND_AUTO_CREATE(自动创建)

注意到在ServiceConnection中的onService()方法中有一个IBinder对象可以用来进行通信。

在绑定了本地service的情况下,onBind(Intent intent) 方法返回的IBinder对象将会传给ServiceConnection对象里
onServiceConnected()方法的service参数,这样访问者就可以通过该IBinder对象与Service进行通信。

实际上开发的时候将通常会继承Binder类的方式实现自己的IBinder对象。

IBinder 将会传给service的访问者

多次调用bindService()不会多次绑定

重点理解:
Service的生命周期:

onCreate()创建该Service之后立即调用的

如果需要该Service是由Activity的startService()方法启动之外,还需要Service子类重写onUnbind()方法时返回true;

只有是通过bindService调用的在使用了unBindService()之后才会调用onDestory()方法。

当一个activity使用bindService调用一个已经启动的service的时候只是可以得到对应的iBinder对象,
而在调用了对应的unBindService之后也不会使得该service彻底destroy

10.1.5 使用IntentService
Service存在的两个问题:
1.Service不会专门启动一个单独的进程,与其所在的应用位于同一个进程中
2.Service不是一条新的线程,因此不应该在Service中处理耗时的任务

//重点//
为什么不可以直接在activity中直接创建一个新的线程,因为如果是直接创建一个新的线程,那么
由于activity本身会被用户退出,并且是BroadCastReceiver的生命周期本身很短,那么会出现在子线程还没有结束的
情况下,activity已经被用户退出了,或是BroadcastRececiver已经结束了,这种情况下,此时它们所在的进程就
变成了空的进程,那么此时很可能被android的回收机制回收那么对应的子线程还没有执行完毕就结束了。

IntentService会使用队列来管理请求的intent对象,每当客户端代码请求i的时候都会创建一个新的worker线程来处理
该Intent。
对于异步的startService(),IntentService会按照次序处理队列中的intent请求,该线程保证同一时刻只处理一个intent。

并且IntentService会自动停止不需要开发者自己停止。

10.2 跨进程调用Service(AIDL Service)
IPC(跨进程通信)

10.2.1 AIDL Service(与java中的远程RMI具有一定的相似之处)
与RMI不同的是,service只是将service对象的代理iBinder返回给用户。

因此AIDL远程接口的实现类就是那个IBinder实现类。

远程Service的onBind()方法只是将IBinder对象的代理传给客户端的ServiceConnection的onServiceConnected()方法
的第二个参数。

10.2.2 创建AIDL文件(android接口定义语言)

AIDL接口的源代码必须以.aidl结尾
在AIDL中用到的数据类型,除了基本类型之外,其他的类型都需要导包,即使在同一个包中也需要导包。

开发人员定义的AIDL接口只是定义了进程之间的通信接口,service端。客户端都需要使用Android SDK安装目录下的build-tools子目录下面
的aidl.exe工具为该接口提供实现。(使用AS会自动为该接口提供实现)

在定义好了AIDL接口之后,android studio工具会自动在build/generated/source/aidl/debug目录下生成了一个XXX.java
接口,再改接口中包含了一个stub内部类。该内部类,该内部类实现了IBinder.ICat两个接口。
这个Stub类将会作为远程Service的回调类–它实现了IBinder接口,因此可以作为Service的onBind()方法的返回值。

如果没有自动生成接口那么可以使用ctrl+F9 强制生成接口。

10.2.3 将接口暴露给客户端
上一步定义好AIDL接口之后,接下来就可以定义一个Service实现类了,该Service的onBind()方法所返回的IBinder对象
应该是ADT所生成的ICat.Stub的子类的实例。

10.2.4 客户端访问AIDL Service
AIDL接口在客户端和服务器端都需要定义。
客户端绑定远程service步骤:
1创建ServiceConnection对象
2.以ServiceConnection对象作为参数,调用Context的bindService()方法绑定远程Service即可。

使用AIDL接口的作用是:
服务器端 ---- AIDL接口 ----- 客户端

Android将指定Component属性,以及指定Package属性的intent都当成显式Intent。

//实例:传递复杂数据的AIDL Service
Android要求调用远程Service的参数和返回值都必须实现Parcelable接口。
实现该接口不仅需要的是实现该接口中的方法,而且要求在实现类中定义一个名为CREATOR、类型为Parcelable.Creator的静态
常量。除此之外还要求使用AIDL代码来定义这些自定义类型。
其中定义的Creator静态常量是用来恢复自定义对象的。

Android中实现Parcelable接口是提供了一种轻量型的序列化机制。

在AIDL接口中定义方法时,需要指定形参的传递模式。

实现自定义复杂数据类型的AIDL访问步骤:
1.先定义一个Person.aidl形的接口;紧接着定义一个对应Person.java类型的接口()其中定义一个CREATOR常量,目的是
实现对应的数据类型的转换为序列化对象。(定义Pet类型的接口同上)
2.然后定义一个IPet.aidl通信接口。(其中指定的是定义一个Pet对象类型的list集合参数类型是
person类型的 并且使用了in表示传入参数的形式)
3.然后实现一个继承自Service的类,其中定义一个static常量,里面实现的是一些数据的初始化。
4.继承对应的xxx.Stub(也就是实现了IPet接口,并实现了IBinder接口),并在onBind()方法中返回对应的binder对象
5.然后实现客户端的Activity的创建,通过在ServiceConnection实现类中的onServiceConnected()方法实现:
petService = IPet.Stub.adInterface(service);得到返回来的对象代理然后进行处理得到Service对象

6.然后在onCreate()方法中得到对应的数据,并使用ArrayAdapter<> 进行包装并显示。

//同样地,调用getSystemService(String name):根据Service名称来获得系统的Serivice

10.3 电话管理器(TelephoneManager)

10.4 短信管理器

10.5 音频管理器AudioManager
10.5.1 简介:
adjustStreamVolumn(int streamType,int direaction,int flag)
第一个参数:调整手机指定类型的声音。
第二个参数:是调大还是调小
第三个参数:是设置调整声音时的标志

setMicrophoneMute()表示设置是否让麦克风静音
setMode() 设置声音模式,
setRingMode()设置手机的电话铃声模式。
setSpeakphone()设置是否打开扩音器
setStreamVolume直接设置手机的指定类型的音量值。

10.6 振动器(Vibrator)
10.6.1 Vibrator简介

vibrator提供了如下的三个方法来创建振动效果:
creaeteOneShot() 创建只震动一次的振动效果
createWaveform创建波形振动的振动效果
createWaveform创建波形振动效果第二种

10.6.2 使用Vibrator控制手机振动

10.7 手机闹钟服务(AlarmManager)

10.7.1 AlermManager简介

10.7.2 设置闹钟
AlermManager是一个全局定时器,即使在程序推出之后也会启动指定组件。

10.8 广播接收器(BroadcastReceiver 四大组件之一)
本质上是一个全局监听器,可以方便实现不同系统之间的通信。
10.8.1 简介:

用来接受程序发出的BroadcastIntent对象。
启动BroadcastReceiver步骤:
1.创建需要启动的BroadcastReceiver的Intent
2.调用Context的sendBroadcast()或sendOrderBroadcast()方法来启动指定的BroadcastReceiver

指定该对象可以匹配的intent对象的两种方式:
1.使用代码指定,调用BroadcastReceiver的Context的registerReceiver(BroadcastReceiver xxx,
Intent intent); 方法指定
2.在AndroidManifest.xml文件中配置。

注意:系统指定的BroadcastReceiver找不到时,系统也不会异常终止。

不要在receive中实现一个耗时的操作因为可能会导致ANR异常。
建议使用intent在一个service中进行耗时操作(建议使用IntentService)

10.8.2 发送广播

10.8.3 有序广播

Broadcast被分为两种:
1.普通广播 是完全异步的,可以在同一时刻被所有接收者收到,消息的传递效率比较高。

2.有序广播 接收者将按照预先声明的优先级依次接受Broadcast。接收者的优先级可以在intent-filter/>
的periority中设置。

优先接收到Broadcast的接收者可以调用abortBroadcast()终止进程。
优先收到的接收者可以调用setResultExtras(Bundle)方法将处理结果存入Broadcast对象中
然后传给下一个接收者。

下一个接收者可以通过Bundle bundle = getResultExtras(true);获取上一个接收者存入的数据。

//实例:基于Service的音乐播放器

//10.9 接受系统广播消息

//实例:开机自动运行的Activity

//实例:手机电量显示

sendStickyBroadcast():发送持续广播

//------------------------截止到 p511(2020.1.3 凌晨1:14截至)------------------------//

用以记录自己的学习过程,为了方便日后复习。

更多相关文章

  1. 如何在Android 11 中正确请求位置权限?以及Android 8 - 11位置权
  2. 详解Android中IntentService的使用方法
  3. ReactNative调用Android原生方法
  4. Cocos项目避免不断复制粘贴android lib库的方法
  5. Android去除系统自带动画的两种方法
  6. android studio中建立assets和jnilibs的方法
  7. Android unspecified' depends on one or more Android Librarie
  8. Android 之 自定义标签 和 自定义组件
  9. android控件-ImageView使用方法整理

随机推荐

  1. C语言中用于结构化程序设计的三种基本结
  2. 在c语言中char型数据在内存中的存储形式
  3. 引用和指针有什么区别
  4. c语言include<stdlib.h>什么意思?
  5. \0在c语言中代表什么?
  6. C语言中typedef的用法有哪些?
  7. c源程序中不允许出现空语句吗?
  8. c++11新特性有哪些
  9. C语言中main函数可以在什么位置
  10. 在一个C语言程序中,main函数可以在任何地