认识Android(常用布局,控件,四大组件,动画,自定义控件及异常消息处理机制)

一、布局

1、LinearLayout(线性布局):

SDKtools,android.bat
https://www.cnblogs.com/kuku19940613/p/10861460.html
https://blog.csdn.net/cto_1649900265/article/details/118930163
https://blog.csdn.net/ltf0425/article/details/78033116

public static void main(String[] args) {
        String[] sources = new String[]{"picture1.JPG", "picture2.gif", "picture3.BMP", "test", "梁尚武"};
//        匹配图片
        String reg = "(?i).+?\\.(jpg|gif|bmp)";
        for (int i = 0; i < sources.length; i++) {
            System.out.println(sources[i].matches(reg));
        }
        System.out.println("__________________");
        String test = "<a>百度</a>";
        String test1 = "小梁猪";
//        匹配HTML标记
        String regex = "<(.*)>.*<\\/\\1>|<(.*) \\/>";
        System.out.println(test.matches(regex));
        System.out.println(test1.matches(regex));
    }

显示特点:所有子控件按照横向或者竖向依次排列,android:orientation=“vertical”(竖向),android:orientation=“horizontal”(横向)。
(1)常用属性

orientation:布局中组件的排列方式,有horziontal(水平)和vertical(垂直)两种方式。
gravity:控制组件所包含的子元素的对齐方式,可多个组合,如(left|buttom)。
layout_gravity:控制该组件在父容器里的对齐方式。
layout_width:布局的宽度,通常不直接写数字,用wrap_content(组件实际大小),fill_parent或者match_parent填满父容器。
layout_height:布局的高度,参数同上。
id:为该组件设置一个资源id,在java文件中可以通过findViewByid(id)找到该组件。
background:为该组件设置一个背景图片,或者直接用颜色覆盖。

(2)Weight(权重)
该属性是用来等比例地划分区域。
要等比例划分,分谁,谁的layout_width=“0dp”,,layout_weight按比例即可。
(3)分割线

devider:设置分割线的图片
showDeviders:设置分割线所在位置,可选值:none,moddle,begining,end.
deviderPadding:设置分割线的padding

2、相对布局(RelativeLayout)

显示特点:和LinearLayout布局相似,所有子控件默认显示在RelativeLayout的左上角
(1)基本属性

gravity:设置容器内组件的对齐方式。

ignoreGravity:设置了该属性为true的属性的组件,将不受gravity属性的影响。

(2)根据父容器定位

layout_alignParentLeft:左对齐。
layout_alignParentRight:右对齐。
layout_alignParentTop:顶部对齐。
layout_alignParentBottom:底部对齐。
layout_centerHorizontal:水平居中。
layout_centerVertical:垂直居中。
layout_centerInParent:中间位置。

(3)根据兄弟组件定位

layout_toLeftOf:参考组件的左边。
layout_toRightOf:参考组件的右边。
layout_above:参考组件的上方。
layout_below:参考组件的下方。
layout_alignTop:对齐参考组件的上边界。
layout_alignBottom:对齐参考组件的下边界。
layout_alignLeft:对齐参考组件的左边界。
layout_alignRight:对齐参考组件的右边界。

(4)margin(偏移)

layout_margin:设置组件上下左右的偏移量。
layout_marginLeft:设置组件离左边的偏移量。
layout_marginRight:设置组件离右边的偏移量。
layout_marginTop:设置组件离上面的偏移量。
layout_marginBottom:设置组件离下面的偏移量。

(5)padding(填充)

padding:往内部元素的上下左右填充一定边距。
paddingLeft:往内部元素的左边填充一定边距。
paddingRight:往内部元素的右边填充一定边距。
paddingTop:往内部元素的上方填充一定边距
paddingBottom:往内部元素的下方填充一定边距。

3、GridLayout(网格布局)

显示特点:所有子控件默认在GridLayout中横向依次排列,当只等每行的列数时,到达指定列数
会自动换行显示。
(1)常见属性

layout_column 在网格的第几列
layout_row 在网格的第几行
layout_columnSpan 跨列
layout_rowSpan 跨行
layout_gravity 在一个网格中的重心位置
columnCount 每行列总数

4、FrameLayout(帧布局):

显示特点:所有的子控件默认显示在FrameLayout的左上角,会重叠在一起显示。
(1)常见属性

foreground:用来设置帧布局容器的前景图像。
foregroundGravity:用来设置帧布局容器的前景图像显示的位置。

5、RelativeLayout.LayoutParams的使用
RelativeLayout.LayoutParams是一个RelativeLayout的布局参数,我们改变控件的就需要使用到。
(1)初始化

// 包裹内容
RelativeLayout.LayoutParams layoutParams = new  RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT);

// 全部内容
RelativeLayout.LayoutParams layoutParams = new  RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,RelativeLayout.LayoutParams.MATCH_PARENT);

(2)addRule方法
addRule方法是我们最常用到的,动态设置控件的位置是一定要使用这个方法的。这个方法有两种方式
addRule(int verb)
addRule(int verb, int subject),其中subject参数是我们另外的控件ID。
RelativeLayout.LayoutParams支持的XML属性例如以下:

第一类:属性值为truefalse
    android:layout_centerHrizontal                                           水平居中
    android:layout_centerVertical                                            垂直居中
    android:layout_centerInparent                                           相对于父元素全然居中
    android:layout_alignParentBottom                                     贴紧父元素的下边缘
    android:layout_alignParentLeft                                          贴紧父元素的左边缘
    android:layout_alignParentRight                                        贴紧父元素的右边缘
    android:layout_alignParentTop                                          贴紧父元素的上边缘
    android:layout_alignWithParentIfMissing                            假设相应的兄弟元素找不到的话就以父元素做參照物

第二类:属性值必须为id的引用名“@id/id-name”
    android:layout_below                          在某元素的下方
    android:layout_above                          在某元素的的上方
    android:layout_toLeftOf                       在某元素的左边
    android:layout_toRightOf                     在某元素的右边

    android:layout_alignTop                      本元素的上边缘和某元素的的上边缘对齐
    android:layout_alignLeft                      本元素的左边缘和某元素的的左边缘对齐
    android:layout_alignBottom                 本元素的下边缘和某元素的的下边缘对齐
    android:layout_alignRight                    本元素的右边缘和某元素的的右边缘对齐

第三类:属性值为详细的像素值。如30dip,40px
    android:layout_marginBottom              离某元素底边缘的距离
    android:layout_marginLeft                   离某元素左边缘的距离
    android:layout_marginRight                 离某元素右边缘的距离
    android:layout_marginTop                   离某元素上边缘的距离

与之对应

ABOVE
ALIGN_BASELINE
ALIGN_BOTTOM
ALIGN_END
ALIGN_LEFT
ALIGN_PARENT_BOTTOM
ALIGN_PARENT_END
ALIGN_PARENT_LEFT
ALIGN_PARENT_RIGHT
ALIGN_PARENT_START
ALIGN_PARENT_TOP
ALIGN_RIGHT
ALIGN_START
ALIGN_TOP
BELOW
CENTER_HORIZONTAL
CENTER_IN_PARENT
CENTER_VERTICAL
END_OF
LEFT_OF
RIGHT_OF
START_OF
TRUE

(3)设置控件边距

setMargins(int left, int top, int right, int bottom)
// 当前控件设置边距,参数分别是左边,上边,右边,下边。单位是px。

setMarginStart(int start)
// 当前控件设置开始边距,国内默认是左边的边距,单位是px。

setMarginEnd(int end)
// 当前控件设置结束边距,国内默认是右边的边距,单位是px。

二、控件

1、TextView

显示文字,相当于Panel。一般用来文本展示,继承自android.view.View,在android.widget包中。
(1)常见属性

//控件id
android:id = "@+id/xxx"  @+id/xxx表示新增控件命名为xxx

//宽度与高度
android:layout_width="wrap_content"  //wrap_content或者match_parent
android:layout_height="wrap_content"  //wrap_content或者match_parent

//文本文字 
android:text="@string/hello_world" //两种方式,直接具体文本或者引用values下面的string.xml里面的元素

//字体大小
android:textSize="24sp"  //以sp为单位

//字体颜色
android:textColor="#0000FF"  //RGB颜色

//字体格式
android:textStyle="normal"  //normal,bold,italic分别为正常,加粗以及斜体,默认为normal

//文本显示位置
android:gravity="center"  //来指定文字的对齐方式,可选值有 top、bottom、left、right、center 等

//是否只在一行内显示全部内容
android:singleLine="true"  //true或者false,默认为false  

(2)shape文件,定义控件展示效果
以文本框背景引入

<!--    7、定义shape形状,如矩形rectangle,椭圆oval,线性line及环形ring-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <!--    1、圆角弧度-->
    <!--    <corners android:radius="20dp" />-->
    <!--    部分圆角弧度-->
    <corners
        android:bottomRightRadius="5dp"
        android:topRightRadius="5dp" />
    <!--    2、指定内部填充颜色-->
    <solid android:color="@color/purple_200" />
    <!--    3、渐变色-->
    <!--    <solid android:color="@color/teal_200"/>-->
    <!--    4、定义描边属性,分别是宽度,颜色,虚实线,虚线的间隔-->
    <stroke
        android:width="1dp"
        android:color="@color/black"
        android:dashWidth="1dp"
        android:dashGap="2dp" />
    <!--    5、内边距-->
    <padding
        android:bottom="5dp"
        android:left="10dp"
        android:right="10dp"
        android:top="5dp" />
    <!--    6、定义圆形的大小-->
    <size
        android:width="26dp"
        android:height="26dp" />
</shape>

延申:
(1)ring(环形)
<shape xmlns:android="http://schemas.android.com/apk/res/android"
		android:shape="ring"
		android:innerRadius="20dp"
		android:thickness="50dp"  
		android:useLevel="false">
		//这里一定要要加上useLevel属性并定义为false,不然没有效果
<solid android:color="#ff00ff"/>
</shape>

在这里插入图片描述
在这里插入图片描述
(3)设置TextView开头空两格

mTv.setText("\t\t\t\t" + getText(R.string.content))

2、Button

Button是最常用的按钮,继承自android.widget.TextView,在android.widget包中。他的常用子类CheckBox,RadioButton, ToggleButton。
(1)常见属性

//控件id
android:id = "@+id/xxx"  @+id/xxx表示新增控件命名为xxx

//宽度与高度
android:layout_width="wrap_content"  //wrap_content或者match_parent
android:layout_height="wrap_content"  //wrap_content或者match_parent

//按钮上显示的文字 
android:text="theButton" //两种方式,直接具体文本或者引用values下面的string.xml里面的元素@string/button

//按钮字体大小
android:textSize="24sp"  //以sp为单位

//字体颜色
android:textColor="#0000FF"  //RGB颜色

//字体格式
android:textStyle="normal"  //normal,bold,italic分别为正常,加粗以及斜体,默认为normal

//是否只在一行内显示全部内容
android:singleLine="true"  //true或者false,默认为false

//保存指定的原始文字内容,如英文字母小写
android:textAllCaps="false"  //true或者false,默认为true

(2)根据不同的状态,设置不同的背景效果
以按钮背景引入,item 标签设置状态

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!--    1、正常状态-->
    <item android:drawable="@color/green" />
    <!--    2、是否获得焦点-->
    <item android:state_focused="true" />
    <!--    3、是否获得窗口焦点-->
    <item android:state_window_focused="true" />
    <!--4、是否可用-->
    <item android:state_enabled="true" />
    <!--    5、可是否勾选-->
    <item android:state_checkable="true" />
    <!--    6、是否被勾选-->
    <item android:state_checked="true" />
    <!--7、是否被选择,有滚轮的情况-->
    <item android:state_selected="true" />
    <!--    8、是否被按下-->
    <item android:drawable="@color/red" android:state_pressed="true" />
    <!--    9、是否处于活跃状态-->
    <item android:state_active="true" />
    <!--    10、多个子控件,仅显示一个/first/中间/last-->
    <item android:state_single="true" />
    <item android:state_first="true" />
    <item android:state_middle="true" />
    <item android:state_last="true" />
</selector>

延申:
selector标签和shape标签可以结合使用

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape></shape>
    </item>
</selector>

(3)点击事件几种写法
①匿名内部类
②定义内部类,实现OnClickListender接口
③用activity实现OnClickListender接口
④指定button的OnClick属性

3、EditText

输入框,可编辑,可设置软键盘方式。继承自android.widget.TextView,在android.widget包中。
(1)常见属性

//控件id
android:id = "@+id/xxx"  @+id/xxx表示新增控件命名为xxx

//宽度与高度
android:layout_width="wrap_content"  //wrap_content或者match_parent
android:layout_height="wrap_content"  //wrap_content或者match_parent

//文本文字 
android:text="@string/hello_world" //两种方式,直接具体文本或者引用values下面的string.xml里面的元素

//文本提示内容
android:hint="hello_world" //android:text和android:hint区别是后者只是提示作用,真正需要输入的时候提示的内容会消失

//字体大小
android:textSize="24sp"  //以sp为单位

//字体颜色
android:textColor="#0000FF"  //RGB颜色

//默认提示文本颜色
android:textColorHint="@color/green"

//字体格式
android:textStyle="normal"  //normal,bold,italic分别为正常,加粗以及斜体,默认为normal

//文本显示位置
android:gravity="center"  //来指定文字的对齐方式,可选值有 top、bottom、left、right、center 等

//是否只在一行内显示全部内容
android:singleLine="true"  //true或者false,默认为false

//输入内容设置为password类型
android:password="true"  //输入的内容会变成······

//输入内容设置为phoneNumber类型
android:phoneNumber="true"  //只能输入数字

//对输入内容进行限制,可选值number,text......
android:inputType="number"

//指定最大行数,超过时文本会向上滚动
android:maxLines="3"

//设置最小行
android:minLines="2"

//只允许单行输入,且不滚动
android:selectAllOnFocus="true"

//点击输入框获取焦点,即选中所有内容(首次)
android:selectAllOnFocus="true"

//设置字与字水平间隔
android:textScaleX="1.2"

//设置字与字垂直间隔
android:textScaleY="1.2"

//设置字母大小写,可选值characters(仅第一个字母大写),words(每一个首字母大写),characters(全部大写)
android:capitalize="characters"

//设定光标为显示/隐藏
android:cursorVisible = "false" //true或者false,默认为true显示
获得或失去焦点
editText.requestFocus();
or
editText.clearFocus()
光标位置的控制

4、ImageView

ImageView控件负责显示图片,其图片的来源可以是在资源文件中的id,也可以是Drawable对象或者位图对象。还可以是Content Provider的URI。
(1)常见属性

<ImageView
//控件id
android:id = "@+id/xxx"  @+id/xxx表示新增控件命名为xxx

//宽度与高度
android:layout_width="wrap_content"   //wrap_content或者match_parent
android:layout_height="wrap_content"  //wrap_content或者match_parent

//此外,可以具体设置高度和宽度显示的像素,不过这样设置如果图片尺寸大于设置的显示的尺寸,则图片是显示不全的,这是可以配合android:scaleType属性。
android:layout_width="200dp"
android:layout_height="200dp" 

//android:scaleType属性,因为关于图像在ImageView中的显示效果,所以有如下属性值可以选择:
matrix:使用matrix方式进行缩放。
fitXY:横向、纵向独立缩放,以适应该ImageView;
fitStart:保持纵横比缩放图片,并且将图片放在ImageView的左上角;
fitCenter:保持纵横比缩放图片,缩放完成后将图片放在ImageView的中央;
fitEnd:保持纵横比缩放图片,缩放完成后将图片放在ImageView的右下角;
center:把图片放在ImageView的中央,但是不进行任何缩放;
centerCrop:保持纵横比缩放图片,以使图片能完全覆盖ImageView;
centerInside:保持纵横比缩放图片,以使得ImageView能完全显示该图片;

//图片来源,需要将图片复制放到res/drawable文件夹里面,引用的时候不需要写图片的后缀
android:src ="@drawable/beautiful">  
android:background="@drawable/pikachu"
以上两者的区别
src属性,给ImageView指定一张图片,按图片大小填充内容,不会拉伸,代码中使用mImgs.setImageDrawable()设置src属性
background属性,填入照片,会根据ImageView给定的宽度进行拉伸,代码中使用mImgs.setBackgroundDrawable()设置background属性

5、ProgressBar

ProgressBar 用于在界面上显示一个进度条,表示我们的程序正在加载一些数据,运行程序,会看到屏幕中有一个圆形进度条正在旋转。

<ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100" />

默认是圆形进度条,可以使用style属性设置为水平进度条;若为水平进度条,使用属性android:max设置最大值,然后在代码中动态地更改进度条的进度。
(1)常用属性
①android:visibility,设置进度条是否可见,可选值visible(可见),invisible(不可见但占据屏幕空间),gone(不可见且不占据空间)

三、Activity

Activity是一种可以包含用户界面的组件,主要用于和用户进行交互。

1、Activity使用流程

(1)自定义Activity类名,继承Activity类或者其他子类
(2)重写onCreate()方法,在方法中调用setContentView()设置要显示的视图
(3)在AndroidMainfest.xml对Activity进行注册
(4)启动Activity:调用startActivity(intent);

2、Activity生命周期

(1)onCreate():在活动第一次被创建的时候调用,用来完成活动的初始化操作,如加载布局、绑定事件等
(2)onStart():在活动由不可见变为可见时被调用
(3)onResume():在活动准备和用户交互的时候被调用。此时的活动一定位于返回栈的栈顶,并且处于运行状态
(4)onPause():在系统准备去启动或者恢复另外一个活动的时候调用。在此方法中将一些消耗CPU的资源释放,以及保存一些关键数据,,但这个方法一定要快,不然会影响到新的栈顶活动的使用
(5)onStop():在活动完全不可用时被调用。和onPause()的区别在于,如果启动一个对话框式的活动,那么onPause()方法会执行,而onStop()不会执行
(6)ondestroy():在活动被销毁之前调用,之后活动的状态将变为销毁态
(7)onRestart():在活动由停止状态变为运行状态之前调用,就是活动被重新启动
其关系图如下所示:
Activity的生命周期类别主要分为三种,如下:
(1)整个生命周期,Activity完整生命周期发生在onCreate()和onDestroy()之间。在onCreate()中执行一些全局性的设置(例如设置布局文件,初始化View等等),在onDestroy()中释放所有资源
(2)可见生命周期,Activity可见生命周期发生在onStart()和onStop()之间,在这段时间内,用户可以在屏幕上看见Activity并与之交互。在整个生命周期,Activity对用户可见和隐藏两种状态可能交替出现,系统就会多次调用onStart()和onStop()方法。
(3)前台生命周期,Activity的前台生命周期发生在onResume()和onPause()之间,在这段时间内,Activity位于屏幕上其他Activiy之前,而且获取屏幕的焦点。Activity可能频繁的转入或转出前台,例如当设备休眠或者弹出对话框时,系统会调用onPause()方法。因为此状态可能经常发生变化,所以在这两个方法中建议做一些轻量级操作。

3、启动方式

(1)显示intent
a、默认

Intent intent = new Intent(MainActivity.this,SecondActivity.class);
                startActivity(intent);

b、ComponentName

Button componentBtn = findViewById(R.id.componentIntent);
        componentBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                ComponentName comp = new ComponentName(MainActivity.this,SecondActivity.class);
                intent.setComponent(comp);
                startActivity(intent);
                }
       }

ComponentName,顾名思义,就是组件名称,通过调用Intent中的setComponent方法,我们可以打开另外一个应用中的Activity或者服务,实例化一个ComponentName需要两个参数,第一个参数是要启动应用的包名称,这个包名称是指清单文件中列出的应用的包名称:
在这里插入图片描述
第二个参数是你要启动的Activity或者Service的全称(包名+类名),代码如下:
启动一个Service:

Intent intent = new Intent();
ComponentName comp = new ComponentName("com.example.aidl", "com.example.aidl.MyAidlService");
intent.setComponent(comp);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

c、setClass

Intent intent = new Intent();

                intent.setClass(MainActivity.this,SecondActivity.class);
//                或者
                intent.setClassName(MainActivity.this,SecondActivity.class.getName());
//                或者
                Intent intent1 = new Intent("android.intent.action.MAIN");
                intent1.setClassName(MainActivity.this,SecondActivity.class.getName());

                startActivity(intent);

延伸:setClass和setClassName的区别
setClass:跳转到与该工程下的(同一个Application中的)activity或者service
setClassName:跳转到不同Applicaiton的activity或者service
(2)隐式intent
在AndroidMainfest.xml中配置内容,可以指定当前Activity能够响应的action和category

<activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="com.example.demo.SecondActivity.ACTION_START" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

MainActivity

Intent intent = new Intent("com.example.demo.SecondActivity.ACTION_START");
                startActivity(intent);

category是默认的,也可以增加(MainActivity)

intent.addCategory("com.example.demo.SecondActivity.MY_CATEGORY");

同时在AndroidMainfest.xml添加一个category声明

<category android:name="com.example.demo.SecondActivity.MY_CATEGORY" />

4、启动模式

(1)Standard模式(标准模式)
在Standard模式下,每当启动一个新的Activity,都会创建一个该Activity的新实例,它就会在返回栈中入栈,且处于栈顶的位置。
应用场景:默认
(2)SingleTop模式(栈顶复用模式)r
若Activity的启动模式为SingleTop,在启动Activity时,会检查返回栈的栈顶是否为该Activity,若是则直接使用,否则创建新的Activity实例。
应用场景:APP接收到多条推送消息,点开不同消息,均由同一实例展示。
(3)SingleTask模式(栈内复用模式)
若Activity的启动模式为SingleTask,在启动Activity时,系统首先会在返回栈中检查是否存在该Activity的实例,若存在则直接使用该实例,并把在这个Activity之上的所有其他Activity统统出栈,若没有则创建一个新的Activity实例。
应用场景:APP的主页,无论哪种业务场景下再次回到此页,都不应保留之上Activity,如浏览器,微博等。
(4)SingleInstance模式(单一实例模式)
若Activity的启动模式为SingleInstance,会启动一个新的返回栈来管理这个Activity。每个应用程序都有自己的返回栈,同一个Activity在不同的返回栈中入栈时必然创建了新的实例;在此种模式下,会有一个单独的返回栈来管理这个Activity,不管哪个应用程序来访问这个Activity,都共用同一个返回栈,也就解决了共享Activity实例的问题。
应用场景:APP经常调用的拨打电话、系统通讯录、地图类APP 等页面,不同APP调用此类Activity 时,首次创建实例,之后其他APP只能复用此实例。

5、横竖屏切换

(1)横竖屏设置
① 在AndroidManifest.xml中设置activity中的android:screenOrientation属性值来实现。
a.竖屏:android:screenOrientation=“portrait”
b.横屏:android:screenOrientation=“landscape”
② 在Java代码中通过类似如下代码来设置 (不推荐这种方法,在大的app不同方向启动时会慢)
a.竖屏: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
b.横屏:setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
(2)如果要彻底禁止翻转,忽略重力感应带来的切换,(模拟器上不管用,在真机上是正确的)
a.忽略重力:android:screenOrientation=“nosensor”
(3)Activity 横竖屏切换时需要回调两个函数 ,所以在此将这个两个函数暂时看成是Activity 横竖屏切换的生命周期的一部分,这两个函数如下:
onSaveInstanceState(Bundle outState) :Activity 即将销毁时保存数据
onRestoreInstanceState(Bundle savedInstanceState) : Activity 重建或者恢复时候取出数据。
注意:
①MiniSdkVersion大于等于 13 时候:android:configChanges=“orientation” 或者 android:configChanges=“orientation|keyboardHidden” 重新调用各个生命周期
②MiniSdkVersion小于 13 时候:
(1)不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
(2)设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
(3)设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

6、状态保存

onCreate(Bundle savedInstanceState);
onSaveInstanceState(Bundle outState);
onRestoreInstanceState(Bundle savedInstanceState);

7、Activity之间的数据传递

1》向下一个Activity传递数据
(1)使用Inten的putExtra传递
第一个Activity中

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
        //设置传递键值对
        intent.putExtra("name", "独爱空城梦");
        startActivity(intent);

第二个Activity中

// 获取意图对象
   Intent intent = getIntent();
   String result = intent.getStringExtra("name");
   mText.setText(result);

(2)使用Intention的Bundle传递
       Bundle主要用于传递数据;它保存的数据,是以key-value(键值对)的形式存在的。
Bundle经常使用在Activity之间或者线程间传递数据,传递的数据可以是boolean、byte、int、long、float、double、string等基本类型或它们对应的数组,也可以是对象或对象数组。
       当Bundle传递的是对象或对象数组时,必须实现Serializable 或Parcelable接口。
       Bundle提供了各种常用类型的putXxx()/getXxx()方法,用于读写基本类型的数据。
       Bundle使用有3种情况,分别为在activity间传递信息,线程间传递信息和使用序列化Seriazable时使用Bundle对象,如下:
①在activity间传递信息
第一个Activity中

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
       //用数据捆传递数据
       Bundle bundle = new Bundle();
       bundle.putString("name", "沙漏的心");
       //把数据捆设置给意图
       intent.putExtra("bund", bundle);
       startActivity(intent);

第二个Activity中

// 获取意图对象
   Intent intent = getIntent();
   Bundle bundle = intent.getBundleExtra("bund");
   String result = bundle.getString("name");
   mText.setText(result);

②线程间传递
通过Handler将带有dundle数据的message放入消息队列,其他线程就可以从队列中得到数据

Message message=new Message();//new一个Message对象     
message.what = MESSAGE_WHAT_2;//给消息做标记  
Bundle bundle = new Bundle(); //得到Bundle对象    
bundle.putString("text1","消息传递参数的例子!");  //往Bundle中存放数据    
bundle.putInt("text2",44);  //往Bundle中put数据    
message.setData(bundle);//mes利用Bundle传递数据  
mHandler.sendMessage(message);//Handler将消息放入消息队列 

读取数据
这里用的是Handler的handleMessage(Message msg)方法处理数据

String str1=msg.getData().getString("text1");  
int int1=msg.getData().getString("text2"); 

(3)使用序列化对象Seriazable
工具类

public class Student implements Serializable {
    private String name;
    private Integer age;
    private String College;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getCollege() {
        return College;
    }

    public void setCollege(String college) {
        College = college;
    }
}

第一种情况,不使用Bundle
第一个Activity中

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
Student student = new Student();
//通过set方法把数据保存到Student对象中
student.setName("小梁");
student.setAge(18);
student.setCollege("兰州大学");
intent.putExtra("stu", student);
startActivity(intent);

第二个Activity中

// 获取意图对象
Intent intent = getIntent();
//反序列化数据对象
Serializable lizable = intent.getSerializableExtra("stu");
if (lizable instanceof Student) {
    //获取到携带数据的Student对象stud
    Student stud = (Student) lizable;
    mText.setText("姓名:" + stud.getName() + ",年龄:" + stud.getAge() + ",毕业学校:" + stud.getCollege());

第二种情况,使用Bundle
第一个Activity中

Student student = new Student();
student.setName("小梁");
student.setAge(18);
student.setCollege("天津农业大学");
//  实例化一个Bundle
Bundle bundle = new Bundle();
//  把数据student放入到bundle中
bundle.putSerializable("students", student);
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtras(bundle);
startActivity(intent);

第二个Activity中

Intent intent = getIntent();
//  实例化一个Bundle
Bundle bundle = intent.getExtras();
//  从bundle中获取student数据
Student student = (Student) bundle.getSerializable("students");
String name = student.getName();
Integer age = student.getAge();
String college = student.getCollege();
mText.setText("姓名:" + name + ",年龄:" + age + ",毕业院校:" + college);

(4)使用静态变量传递数据
第一个Activity

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
       SecondActivity.hello = "哈喽,";
       SecondActivity.word = "好久不见";
       startActivity(intent);

第二个Activity

//静态变量
protected static String hello;
protected static String word;
mText.setText(hello + word);

2》返回数据给上一个Activity
第一个Activity
       startActivityForResult()有两个参数,第一个参数是Intent,第二个参数是请求码,用于在之后的回调中判断数据的来源,请求码只要是一个唯一值即可,这里传入1;

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
       startActivityForResult(intent, 1);

第二个Activity
       setResult()专门向上一个Activity返回数据,两个参数,第一个参数用于向上一个Activity返回处理结果,一般只使用RESULT_OK和RESULT_CANCELED这两个值,第二个参数把带有数据的Intent传递回来,最后,调用finish()方法来销毁当前Activity。

mBackBtn.setOnClickListener(new View.OnClickListener() {
   @Override
    public void onClick(View v) {
        Intent intent = new Intent();
        intent.putExtra("names", "阿梁");
        setResult(RESULT_OK, intent);
        finish();
    }
});

       在第一个Activity中重写onActivityResult()方法,来得到返回的数据,该方法有三个参数,第一个参数requestCode,在启动Activity时传入的请求码;第二个参数resultCode,在返回数据时传入的处理结果;第三个参数data,即携带着返回数据的Intent。

@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1 || resultCode == RESULT_OK) {
            String returnData = data.getStringExtra("names");
            mTextFro.setText(returnData);
        }
    }

四、Fragment

1、认识Fragment

Fragment:是Android3.0开始新增的概念,意为碎片。
(1)为什么要有Fragment?
Android运行在各种各样的设备中,有小屏幕的手机,还有大屏幕的平板,电视等。同样的界面在手机上显示可能很好看,在大屏幕的平板上就未必了,手机的界面放在平板上可能会有过分被拉长、控件间距过大等情况。针对屏幕尺寸的差距,Fragment的出现能做到一个App可以同时适应手机和平板。这就是为什么要有Fragment的原因。
(2)特点
①Fragment是依赖于Activity的,不能独立存在的。
②一个Activity里可以有多个Fragment,而一个Fragment可以被多个Activity重用。
③Fragment有自己的生命周期,并能接收输入事件。
④能在Activity运行时动态地添加或删除Fragment。
(3)优势:
①模块化:我们不必把所有代码全部写在Activity中,而是把代码写在各自的Fragment中。
②可重用:多个Activity可以重用一个Fragment。
③可适配:根据硬件的屏幕尺寸、屏幕方向,能够方便地实现不同的布局,这样用户体验更好。

2、生命周期

在这里插入图片描述
• onAttach():Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。
• onCreate():Fragment被创建时调用。
• onCreateView():创建Fragment的布局。
• onActivityCreated():当Activity完成onCreate()时调用。
• onStart():当Fragment可见时调用。
• onResume():当Fragment可见且可交互时调用。
• onPause():当Fragment不可交互但可见时调用。
• onStop():当Fragment不可见时调用。
• onDestroyView():当Fragment的UI从视图结构中移除时调用。
• onDestroy():销毁Fragment时调用。
• onDetach():当Fragment和Activity解除关联时调用。
fragment生命周期解析:
       当一个fragment被创建的时候,需调用以下生命周期方法:onAttach(), onCreate(), onCreateView(), onActivityCreated()
       当这个fragment对用户可见的时候,需调用:onStart() ,onResume()
       当这个fragment进入后台模式需调用:onPause(),onStop()
       当这个fragment被销毁或者是持有它的Activity被销毁了,调用:onPause() ,onStop(), onDestroyView(), onDestroy() onDetach()

3、加载Fragment

(1)静态加载
流程
①定义Fragment的xml布局文件
②自定义Fragment类,继承Fragment,重写onCreateView()方法,并加载布局文件

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_front, container, false);
        return view;

    }

③在需要加载的xml布局文件中,使用fragment标签的name属性指定要加载的Fragment,即Fragment的全限定类名

android:name="com.example.project.fragment.FirstFragment"

④Activity在加载布局
(2)动态加载
流程
①创建待添加的Fragment实例
②获得FragmentManager对象,通过getSupportFragmentManager()
③开启事务使用beginTransaction()加载
④调用add()方法或者replace()方法Fragment,需要传入两个参数,传入容器的id和待添加的Fragment实例
⑤提交事务,调用commit()方法

SecondFragment fragment = new SecondFragment();
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transition = manager.beginTransaction();
transition.add(R.id.linear, fragment);
transition.commit();

4、Fragment与Activity的交互

1》数据传递
(1)Activity向Fragment传递数据
①在Activity中传出,使用setArguments()方法

SecondFragment fragment = new SecondFragment();
Bundle bundle = new Bundle();
bundle.putString("name", "独爱空城梦");
fragment.setArguments(bundle);

②在Fragment中接收,使用getArguments()方法

Bundle bundle = getArguments();
mTextSe.setText(bundle.getString("name"));

(2)Fragment向Activity传递数据,使用回调接口,在Fragment中定义一个内部回调接口,再让包含该Fragment的Activity实现该回调接口,实现两者之间的通信
①定义一个回调接口:(Fragment中)

//  定义回调接口
    public interface CallBack {
        //  定义一个获取信息的方法
        public void getResult(String result);
    }

②接口回调(Fragment中)

//  调用接口
    public void getData(CallBack callBack) {
        String msg = "我是Fragment传递给Activity的值";
        callBack.getResult(msg);
    }

③使用接口回调方法读数据(Activity中)
(3)Fragment向Fragment传递数据
2》组件获取
(1)Fragment获取Activity中的组件

getActivity().findViewById(R.id.textTop);

(2)Fragment获取Fragment中的组件

getFragmentManager().findFragmentByTag(R.id.test);

5、Fragment+ViewPager

viewpager有三个适配器,pagerAdapter,FragmentpagerAdapter,FragmentStatePagerAdapter。
(1)pagerAdapter是普通的pager适配器,操作方便,结构和功能大概和recyclerView的适配器类似;
(2)FragmentPagerAdapter是用于碎片的适配器,这个适配器相比第三种FragmentStatePagerAdapter来说,更适合界面少的情况,因为这个适配器会缓存当前页面在内的左右共三个界面,实现快速展示,但是界面多的时候则会占用大部分资源;
(3)当使用FragmentStatePagerAdapter 时,实现将只保留当前页面,当页面离开视线后,就会被消除,释放其资源;而在页面需要显示时,生成新的页面。这么实现的好处就是当拥有大量的页面时,不必在内存中占用大量的内存。

public class ViewPagerAdapter extends FragmentStatePagerAdapter {
    private List<Fragment> mList;

    public ViewPagerAdapter(FragmentManager fm, List<Fragment> list) {
        super(fm);
        mList = list;
    }

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

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

五、Adapter

       adapter是view和数据的桥梁。在一个ListView或者GridView中,你不可能手动给每一个格子都新建一个view,所以这时候就需要Adapter的帮忙,它会帮你自动绘制view并且填充数据。

1、BaseAdapter

最实用最常用,原因就在于它的全能性,结合ListView或者GridView使用
(1)BaseAdapter+ListView最基本的方法:

public int getCount(): 适配器中数据集的数据个数;
public Object getItem(int position): 获取数据集中与索引对应的数据项;
public long getItemId(int position): 获取指定行对应的ID;
public View getView(int position,View convertView,ViewGroup parent): 获取每一行Item的显示内容。

(2)ListView加载不同布局
①增加Type
②重写getViewTypeCount方法
③重写getItemViewType方法
④重写getView方法

点击demo查看使用案例
扩展:
点击不同item,携带参数跳转到百度并搜索

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Uri uri = Uri.parse("http://baike.baidu.com/" + mList.get(position).getName());
                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                startActivity(intent);
            }
        });

六、动画

Android动画可以分为帧动画(Frame Animation)、补间动画(Tweened Animation)和属性动画。
1、帧动画
       帧动画就是顺序播放一组预先定义好的图片,就类似于我们观看视频,就是一张一张的图片连续播放。
       帧动画的使用很简单,总共就两个步骤:
(1)在res/drawable目录下定义一个XML文件,根节点为系统提供的animation-list,然后放入定义更好的图片;
(2)使用AnimationDrawable类播放第一步定义好的Drawable中的图片,形成动画效果;
2、补间动画
       补间动画的原理是对通过对View的平移(TranslateAnimation),缩放(ScaleAnimation),旋转RotateAnimation)和透明(AlphaAnimation,透明度的变化范围(0,1),0是完全透明,1是完全不透明)的操作,从而产生的动画。
       补间动画只调整View在Canvas上显示,并没有真实的改变View原来的位置,因此它的点击和触摸事件还保留在没有经过动画前的位置。
(1)平移动画Translation

TranslateAnimation(0, 200, 0, 200)

参数解析:

android:fromXDelta对应的就是TranslateAnimation(float fromXDelta,) 起始点X轴坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点);
android:fromYDelta对应的就是TranslateAnimation(, float fromYDelta,) 起始点Y轴从标,平移的规律同上;
android:toXDelta对应的就是TranslateAnimation(, float toXDelta,) 结束点X轴坐标,平移的规律同上;
android:toYDelta对应的就是TranslateAnimation(, float toYDelta) 结束点Y轴坐标,平移的规律同上

(2)缩放动画Scale

ScaleAnimation(1, 2, 1, 2, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)

参数解析:

android:fromXScale对应的就是(ScaleAnimation(float fromX,)这个属性代表的是:初始X轴缩放比例,1.0表示无变化;
android:toXScale对应的就是(ScaleAnimation(, float toX,)这个属性代表的是:结束X轴缩放比例;
android:fromYScale对应的就是(ScaleAnimation(, float fromY,)这个属性代表的是:初始Y轴缩放比例;
android:toYScale对应的就是(ScaleAnimation(, float toY,)这个属性代表的是:结束Y轴缩放比例;
android:pivotX对应的就是(ScaleAnimation(, float pivotX,)这个属性代表的是:缩放起点X轴坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点);
android:pivotY对应的就是(ScaleAnimation(, float pivotY)这个属性代表的是:缩放起点Y轴坐标,同上规律

(3)旋转动画Rotation

RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)

参数解析:

android:fromDegrees对应的就是(RotateAnimation(float fromDegrees,))这个属性代表的是:旋转开始角度,正代表顺时针度数,负代表逆时针度数;
android:toDegrees对应的就是(RotateAnimation(, float toDegrees,))这个属性代表的是:旋转结束角度,正代表顺时针度数,负代表逆时针度数;
android:pivotX对应的就是(RotateAnimation(, float pivotX,))这个属性代表的是:缩放起点X坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点);
android:pivotY对应的就是(RotateAnimation(, float pivotY))这个属性代表的是:缩放起点Y坐标,同上规律

(4)透明度动画Alpha

AlphaAnimation(1, 0.1f)

参数解析:

android:fromAlpha对应的就是(AlphaAnimation(float fromAlpha,) )这个属性代表的是:动画开始的透明度(0.01.00.0是全透明,1.0是不透明)
android:toAlpha对应的就是(AlphaAnimation(, float toAlpha))这个属性代表的是:动画结束的透明度(0.01.00.0是全透明,1.0是不透明)

(5)AnimationSet动画集合的方式实现
注意:补间动画执行之后并未改变View的真实布局属性。假设现在Activity中有一个 Button在屏幕上方,设置了平移动画移动到屏幕下方然后保持动画最后执行状态呆在屏幕下方。如果点击屏幕下方动画执行之后的Button是没有任何反应,而点击原来屏幕上方没有Button的地方却响应的是点击Button的事件,这一点是需要注意的。
3、属性动画
       实现动画的原理:通过不断控制值的变化,再不断手动赋给对象的属性,从而实现动画效果。
(1)两个使用方法类:ValueAnimator 类 & ObjectAnimator 类
①ValueAnimator
I、使用流程
a.调用ValueAnimator的ofInt(),ofFloat()或ofObject()静态方法创建ValueAnimator实例
b.调用实例的setXxx方法设置动画持续时间,插值方式,重复次数等
c.调用实例的addUpdateListener添加AnimatorUpdateListener监听器,在该监听器中 可以获得ValueAnimator计算出来的值,你可以将值应用到指定对象上~
d.调用实例的start()方法开启动画! 另外我们可以看到ofInt和ofFloat都有个这样的参数:float/int… values代表可以多个值!
II、常用方法

void addUpdateListener(ValueAnimator.AnimatorUpdateListener listener):添加值变化监听器。主要监听值变化,实现动画。
void addUpdateListener(AnimatorUpdateListener listener):添加动画状态监听器。重写动画开始、结束、取消、重复四个方法,监听不同状态。
void cancel (): 取消动画。
void end ():让动画到达最后一帧。
void start():开始动画。
void pause():暂停动画。
void resume():继续动画。
void reverse ():反向播放动画。
boolean isRunning():是否在运行中。
boolean isStarted():是否已经开始。

III、属性相关方法

void setCurrentFraction(float fraction):设置当前时间因子。即时间到达的百分比。
float getAnimatedFraction():获取当前时间因子。即时间到达的百分比。
void setCurrentPlayTime (long playTime):设置当前的时间,取值为0-duration,单位毫秒。
long getCurrentPlayTime ():获取当前的时间,单位毫秒。
ValueAnimator setDuration (long duration):设置动画总时长,单位毫秒。
long getDuration ():获取动画总时长,单位毫秒。
void setFrameDelay (long frameDelay):设置每一帧之间间隔多少毫秒。
long getFrameDelay ():获取每一帧之间间隔多少毫秒。
void setInterpolator (TimeInterpolator value):设置动画的Interpolator,和View AnimationInterpolator通用。
TimeInterpolator getInterpolator ():获取当前使用的插值器。
void setRepeatCount(int value):设置重复次数。
int getRepeatCount():获取重复次数。
void setRepeatMode(int value):设置重复模式。有RESTART和REVERSE两种。
int getRepeatMode():获取重复模式。
void setStartDelay(long startDelay):设置开始前延迟毫秒数。
long getStartDelay():获取开始前延迟毫秒数。

void getAnimatedValue():获取计算出来的当前属性值。

getAnimatedValue(String propertyName):获取计算出来的当前某个属性的值。
void setEvaluator(TypeEvaluator value):设置求值器。
void setFloatValues(float… values):设置Float型变化值,一般设置初始值和结束值,当然你也可以设置中间值,因为这是一个可变参数,长度可变。
void setIntValues(int… values):设置Int型变化值,一般设置初始值和结束值,当然你也可以设置中间值,因为这是一个可变参数,长度可变。
setObjectValues(Object… values):设置Object型变化值,一般设置初始值和结束值,当然你也可以设置中间值,因为这是一个可变参数,长度可变。

②ObjectAnimator
I、ObjectAnimator的初始化也是通过一系列ofXXX()方法来进行,但是参数有所变化,格式如下:

ofInt(Object target, String propertyName, int... values)

参数分析:

参数解析
target对象,指定要改变谁的属性
propertyName属性名,指定要改变对象的什么属性,这个属性名要求在兑现中必须有对应的public的setPropertyName的方法
values一系列这个属性将会到达的值

II、常用的属性动画
因为属性动画可以改变提供了set方法的任意属性,所以就不像ViewAnimation一样仅仅局限于旋转,平移,缩放,渐变这四种动画了

ObjectAnimator第二个参数的属性对应的set方法效果
alphapublic void setAlpha(float alpha)改变透明度
translationXsetTranslationX沿X轴平移
translationYsetTranslationY沿Y轴平移
scaleXsetScaleX沿X轴缩放
scaleYsetScaleY沿Y轴缩放
rotationXsetRotationX绕X轴旋转
rotationYsetRotationY绕Y轴旋转
rotationsetRotation绕Z轴旋转

III、setScaleType(android:scaleType)
       该方法必须在ImageView设置内容(前景)图片后才有效果,背景是没有效果的,即xml文件中为android:src(代码中为setImageDrawable(drawable);setImageBitmap(bm);setImageResource(resId))才有效果,而android:background(代码中为setBackgroundResource())没有效果。
setScaleType(android:scaleType)参数详解:

FIT_XY:对原图宽高进行放缩,不保持原比例将ImageView填充满

MATRIX:不改变原图大小从ImageView的左上角开始绘制,超过ImageView部分不再显示

CENTER:对原图居中显示,超过ImageView部分不再显示

CENTER_CROP:对原图居中显示后进行等比放缩处理,使原图最小边等于ImageView的相应边

CENTER_INSIDE:若原图宽高小于ImageView宽高,这原图不做处理居中显示,否则按比例放缩原图宽()是之等于ImageView的宽()

FIT_START:对原图按比例放缩使之等于ImageView的宽高,若原图高大于宽则左对齐否则上对齐

FIT_CENTER:对原图按比例放缩使之等于ImageView的宽高使之居中显示

FIT_END:对原图按比例放缩使之等于ImageView的宽高,若原图高大于宽则右对齐否则下对齐

IV、 ofInt()
       通过ofInt()来实例化对象,那么属性值必须为int型,通常我们通过ofInt可以实现很多动画,例如实现颜色渐变等;ofInt()也有几个重载函数,对目标对象T的property属性值进行改变。例如颜色值的变化

ofInt(Object target, String propertyName, int… values)
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(mObgImg, "backgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
        objectAnimator.setEvaluator(new ArgbEvaluator());
        objectAnimator.setDuration(3000L);
        objectAnimator.start();

V、ofFloat()
ofFloat()来实例化对象,那么属性值必须为float型,通常我们通过ofFloat可以实现很多动画,例如实现位置变化等;ofFloat()也有几个重载函数,对目标对象T的property属性值进行改变。例如实现一个贝塞尔曲线

ObjectAnimator.ofFloat (Object target, String xPropertyName, String yPropertyName, Path path)
Path path = new Path();
        path.quadTo(800, 200, 800, 800);
        ObjectAnimator animator = ObjectAnimator.ofFloat(mObgImg, "x", "y", path);
        animator.setDuration(4000L);
        animator.start();

VI、 ofArgb()
ofArgb()可以帮助我们实现颜色的渐变效果,因为它里面封装了对ArgbEvaluator的使用。

ObjectAnimator animator = ObjectAnimator.ofArgb(mObgImg, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);
        animator.setDuration(4000L);
        animator.start();

③ValueAnimator类 & ObjectAnimator 类的区别
ValueAnimator 类是先改变值,然后手动赋值给对象的属性从而实现动画;是间接对对象属性进行操作;
ObjectAnimator类是先改变值,然后自动赋值给对象的属性从而实现动画;是直接对对象属性进行操作;
(2)两个辅助使用类:插值器 & 估值器
①插值器,TimeInterpolator(时间插值器)
       插值器作用:设置属性值从初始值过渡到结束值的变化规律,如匀速,加速&减速等;即根据时间流逝的百分比计算出当前属性值改变的百分比。
默认采用的加减速插值器,它会在动画开始执行逐渐加速,然后又逐渐减速直至动画结束;
a.Android系统中共内置了9种插值器,如下表所示:

插值器名称作用
AccelerateDecelerateInterpolator加减速插值器,动画先加速,后减速
LinearInterpolator线性插值器,动画匀速运行
AccelerateInterpolator加速插值器,动画加速运行至结束
DecelerateInterpolator减速插值器,动画减速运行至结束
OvershootInterpolator快速完成动画,超出终点一小部分后再回到终点
AnticipateInterpolator先后退一小步再加速前进至结束
AnticipateOvershootInterpolator先后退一小步再加速前进,超出终点一小部分后再回到终点
BounceInterpolator弹性插值器,在动画结束之前会有一个弹性动画的效果
CycleInterpolator周期运动

b.TimeInterpolator接口
       自定义插值器我们需要实现 Interpolator / TimeInterpolator 接口并实现接口方法getInterpolation。属性动画实现的是TimeInterpolator接口,而补间动画实现的则是Interceptor接口。事实上Interpolator接口继承了TimeInterceptor接口,这样做是基于兼容性的考虑,使得过去所有应用于补间动画的插值器都可以直接应用在属性动画上。为了我们的自定义插值器具有更好的兼容性,推荐实现Interpolator接口。
c.应用场景:实现非线性运动的动画效果,如加速或者减速运动等。
②估值器,TypeEvaluator(类型估值算法,即估值器)
       估值器作用:设置属性值从初始值过渡到结束值的变化具体数据,即根据当前属性改变的百分比来计算改变后的属性值。另外一种说法,定义从初始值过渡到结束值的计算规则。
a.系统已有的估值器:

估值器名称作用
IntEvaluator针对整型属性
FloatEvaluator针对浮点型属性
ArgbEvaluator针对Color属性

b.自定义估值器
点击demo查看使用案例

七、Service

       Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。服务可由其他应用组件启动(如Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

1、Service的两种形式

a.Service启动状态
       当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务, 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。
       首次启动会创建一个Service实例,依次调用onCreate()和onStartCommand()方法,此时Service 进入运行状态,如果再次调用StartService启动Service,将不会再创建新的Service对象, 系统会直接复用前面创建的Service对象,调用它的onStartCommand()方法!
b.Service绑定状态
       当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
       当首次使用bindService绑定一个Service时,系统会实例化一个Service实例,并调用其onCreate()和onBind()方法,然后调用者就可以通过IBinder和Service进行交互了,此后如果再次使用bindService绑定Service,系统不会创建新的Sevice实例,也不会再调用onBind()方法,只会直接把IBinder对象传递给其他后来增加的客户端!
       如果我们解除与服务的绑定,只需调用unbindService(),此时onUnbind和onDestory方法将会被调用!这是一个客户端的情况,假如是多个客户端绑定同一个Service的话,当一个客户完成和service之间的互动后,它调用 unbindService() 方法来解除绑定。当所有的客户端都和service解除绑定后,系统会销毁service。
Service生命周期图,如下所示:
在这里插入图片描述
相关方法讲解:
——onCreate():当Service第一次被创建后立即回调该方法,该方法在整个生命周期 中只会调用一次!
——onDestory():当Service被关闭时会回调该方法,该方法只会回调一次!
——onStartCommand(intent,flag,startId):当客户端调用startService(Intent)方法时会回调,可多次调用StartService方法, 但不会再创建新的Service对象,而是继续复用前面产生的Service对象,但会继续回调 onStartCommand()方法!
——IBinder onOnbind(intent):该方法是Service都必须实现的方法,该方法会返回一个 IBinder对象,app通过该对象与Service组件进行通信!
——onUnbind(intent):当该Service上绑定的所有客户端都断开时会回调该方法!
StartService启动Service后bindService绑定
       如果Service已经由某个客户端通过StartService()启动,接下来由其他客户端 再调用bindService()绑定到该Service后调用unbindService()解除绑定最后在 调用bindService()绑定到Service的话,此时所触发的生命周期方法如下:
onCreate( )->onStartCommand( )->onBind( )->onUnbind( )->onRebind( )
结论:使用bindService来绑定一个启动的Service,注意是已经启动的Service!!! 系统只是将Service的内部IBinder对象传递给Activity,并不会将Service的生命周期 与Activity绑定,因此调用unBindService( )方法取消绑定时,Service也不会被销毁!

2、IntentService

       客户端通过startService(Intent)来启动IntentService; 我们并不需要手动地区控制IntentService,当任务执行完后,IntentService会自动停止; 可以启动IntentService多次,每个耗时操作会以工作队列的方式在IntentService的 onHandleIntent回调方法中执行,并且每次只会执行一个工作线程,执行完一,再到二这样!

3、前台服务

       Service一般都是运行在后来的,但是Service的系统优先级 还是比较低的,当系统内存不足的时候,就有可能回收正在后台运行的Service,对于这种情况我们可以使用前台服务,从而让Service稍微没那么容易被系统杀死,当然还是有可能被杀死的…所谓的前台服务就是状态栏显示的Notification!

4、后台定时线程

       执行定时任务, 比如轮询,就是每间隔一段时间就请求一次服务器,确认客户端状态或者进行信息更新等!Android常用的定时方法是Alarm机制,它具有唤醒CPU的功能,也要区分CPU 唤醒与屏幕唤醒!

八、异常消息处理机制

1、Handler

       Handler是Android提供用来异步更新UI的一套机制,也是一套消息处理机制,可以用它来发送消息,也可以用它来处理消息。
       Android的UI线程是安全的,如果在子线程中更新UI会出现异常,导致程序崩溃;如果在UI中做耗时操作又会出现ANR异常。所以有了Handler,实现进程间的通信。
(1)异步消息由四个部分组成:
       a.Message是在线程之间传递消息,它可以在内部携带少量的信息,用于不同线程之间的传递数据。
       b.Handler用于发送消息和处理消息,由Handler的sendMessage()方法、post()方法等发送消息,最终会传递到到Handler的handleMessage()方法中。
       c.MessageQueue是消息队列,主要用于存放所有通过Handler发送的消息,这部分消息会一直存放在队列中,等待被处理。每个线程只会有一个MessageQueue。
       d. Looper是每个线程中的MessageQueue管家,调用Looper的loop()方法后就会进入到一个无限循环中,然后每当发现MessageQueue中存在一个消息,就会将他取出,并传递到Handler的handleMessage()方法中。每个线程也只会有一个Looper,因为Looper要产生一个MessageQueue,而MessageQueue一个线程只能有一个。
(2)基本的流程:
       ① 首先在主线程中创建一个Handler对象,并重写handleMessage()方法
       ② 然后当子线程需要进行UI操作时,就创建一个Message对象,设置message.what字段用于标识此Message。
       ③ 使用Handler的sendMessage(Message message)方法发送②中的Message,之后会送达到MessageQueue消息队列中。
       ④ Looper则会一直尝试从MessageQueue中取出消息,发回给Handler的handleMessage(Message msg)方法中Handler是在主线程中创建的,所以handleMessage(Message msg)中的代码也会在主线程中运行,在handleMessage()中就可以进行UI操作了。一条消息从子线程到主线程,从不能更新UI到更新UI,异步消息处理的核心就是如此。runOnUiThrea( new Runnable) 就是一个异步消息处理机制的接口封装。作用就是切换回主线程来。
(3)Handler的执行流程图如下:
在这里插入图片描述
(4)Handler的相关方法:

void handleMessage(Message msg):处理消息的方法,通常是用于被重写!
sendEmptyMessage(int what):发送空消息
sendEmptyMessageDelayed(int what,long delayMillis):指定延时多少毫秒后发送空信息
sendMessage(Message msg):立即发送信息
sendMessageDelayed(Message msg):指定延时多少毫秒后发送信息
final boolean hasMessage(int what):检查消息队列中是否包含what属性为指定值的消息 如果是参数为(int what,Object object):除了判断what属性,还需要判断Object属性是否为指定对象的消息

使用案例,点击这里

2、AsyncTask

       提供了AsyncTask这个封装好的轻量级异步类,通过几十行的代码就可以完成我们的异步操作,而且进度可控;相比起Handler,AsyncTask显得更加简单,快捷~当然,这只适合简单的异步操作,实际异步使用场景是网络操作,图片加载,数据传输等。
AsyncTask指定3个泛型参数:

a. Params:开始异步任务执行时传入的参数类型,对应excute()中传递的参数,可用于在后台任务中使用
b. Progress:异步任务执行过程中,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度条单位。
c. Result:异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型保持一致

       AsyncTask中的几个方法才能完成对任务的定制,需要重写的方法有以下四个:

a.onPreExecute()这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
b.doInBackground(Params...)这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。
任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,
就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,
比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
c.onProgressUpdate(Progress...)当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,
方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
d.onPostExecute(Result)当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。
返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,
以及关闭掉进度条对话框等。
e.onCancelled()取消线程操作时调用

       AsyncTask的onPreExecute()执行一些初始化操作;doInBackground()该方法是核心方法,代码在子线程中运行,所以耗时操作在这里完成;可以用publishProgress()方法来更新进度,onProgressUpdate(),调用publishProgress()方法就会执行该方法,可以根据进度对UI进行更新;onPostExecute(), 当doInBackground执行完毕的时候调用,进行一些收尾工作。
使用案例,点击这里

3、Binder机制

(1)功能
Binder机制,实现进程间通信的机制,还可以实现以下功能:
①用驱动程序来推进进程间通信;
②通过共享内存来提高性能;
③为进程请求分配每个进程的线程池;
④针对系统中的对象引入了引用计数和跨进程的对象引用映射;
⑤进程间同步调用。
(2)Binder机制的组成
       Binder机制由三部分组成,即:Client、Server和ServiceManager。
三部分组件之间的关系:
①Client、Server、ServiceManager均在用户空间中实现,而Binder驱动程序则是在内核空间中实现的;
②在Binder通信中,Server进程先注册一些Service到ServiceManager中,ServiceManager负责管理这些Service并向Client提供相关的接口;
③Client进程要和某一个具体的Service通信,必须先从ServiceManager中获取该Service的相关信息,Client根据得到的Service信息与Service所在的Server进程建立通信,之后Clent就可以与Service进行交互了;
④Binder驱动程序提供设备文件/dev/binder与用户空间进行交互,Client、Server和ServiceManager通过open和ioctl文件操作函数与Binder驱动程序进行通信;
⑤Client、Server、ServiceManager三者之间的交互都是基于Binder通信的,所以通过任意两者这件的关系,都可以解释Binder的机制。
在这里插入图片描述
I、注册服务
在这里插入图片描述
首先,Server在自己的进程中向Binder驱动申请创建Service的Binder实体
Binder驱动为这个Service创建位于内核的Binder实体节点和Binder引用
Server通过Binder驱动将Service名字和Server的Binder引用打包发送给ServerManager
ServerManager收到数据包后,取出Service名字和引用,填入一张查找表
II、获取服务
在这里插入图片描述
Client利用handle值为0的引用找到ServiceManager
向ServiceManager发送XXXService的访问请求
ServiceManager从请求包中获取XXXService的名字,在查找表中找到对应的条目,取出对应的Binder引用
ServiceManager将XXXService的Binder引用回复给Client
III、使用服务
在这里插入图片描述
Client和Server既是发送方,又是接收方
发送方通过自身的Binder实体进行发送操作,把数据通过接收方的Binder引用发送给接收方
Binder驱动会处理这个发送请求,利用内核空间进程共享的机制:
a.把发送方的数据放入写缓存,对于接收方来说是读缓存
b.接收方之前一直在阻塞状态中,当写缓存有数据,则会读取数据,执行命令操作
c.接收方执行完后,会把返回结果同样采用写入缓存区,对于发送方,为读缓存区
(3)Binder通信机制流程图
在这里插入图片描述
流程解析:
①Client调用某个代理接口中的方法时,代理接口的方法会将Client传递的参数打包成Parcel对象;
②然后代理接口把该Parcel对象发送给内核中的Binder driver;;
③ 然后Server会读取Binder Driver中的请求数据,假如是发送给自己的,解包Parcel对象, 处理并将结果返回;
PS:代理接口中的定义的方法和Server中定义的方法是一一对应的, 另外,整个调用过程是一个同步的,即Server在处理时,Client会被Block(锁)住! 而这里说的代理接口的定义就是等下要说的AIDL(Android接口描述语言)
换句话说:
每个Android进程,只能运行在自己的进程所拥有的虚拟地址空间,Client与Server进程通信,利用进程间可共享的内核空间来完成底层通信工作。
(4)Binder驱动的实现简介
①Binder采用了AIDL来描述进程间的接口;
②Binder是一个特殊的字符型设备,设备节点为dev/binder。
③Binder驱动程序由以下两个文件实现:
  I、kernel/drivers/staging/binder.h
  II、kernel/drivers/staging/binder.c
④在Binder驱动的实现过程中,以下函数起着关键作用:
  I、使用binder_ioctl()函数与用户空间交换数据;
  II、BINDER_WRITE_READ用来读写数据,数据包中的cmd域用于区分不同的请求;
  III、使用binder_thread_write()函数来发送请求或返回结果,在binder_thread_write()函数中,通过调用binder_transaction()函数来转发请求并返回结果.当收到请求时,binder_transaction()函数会通过对象的handle找到对象所在的进程,如果handle结果为空,则认为此对象是context_mgr,然后把请求发给context_mgr所在的进程,并将请求中所有的Binder对象放到RB树中,最后把请求放到目标进程的队列中以等待目标进程的读取。
  IV、使用binder_thread_read()函数来读取结果;
  V、在函数binder_parse()中实现数据解析工作。
Binder与传统IOC对比

Binder内存共享 Socket
性能需要拷贝一次无需拷贝需要拷贝两次
特点基于C/S架构,易用性高控制复杂,易用性差基于C/S架构,作为一款通用接口,其传输效率较低,开销大
安全性为每个APP分配UID,同时支持实名和匿名依赖上层次协议,访问接入点是开放的,不安全依赖上层次协议,访问接入点是开放的,不安全

4、AIDL

       AIDL(Android 接口定义语言) 是 Android 提供的一种进程间通信 (IPC) 机制,通过AIDL可以实现RPC方式,所谓RPC是指远程过程调用(Remote Procedure Call),可以简单的理解为就像在本地一样方便的调动远程的方法。在Android的跨进程通讯的方案中,只有AIDL可以实现RPC方式。
(1)AIDL文件支持数据类型

基本数据类型:intlongcharbooleandoubleString
CharSequence
ArrayList:里面每个元素也需要被AIDL支持
HashMap:里面的每个KeyValue也都需要被AIDL支持
Parcelable:所有实现了此接口的对象
AIDL:所有的AIDL接口本身也可以在AIDL文件中使用

(2)AIDL文件可以分为两类
一类用来声明实现了Parcelable接口的数据类型,以供其他AIDL文件使用那些非默认支持的数据类型。还有一类是用来定义接口方法,声明要暴露哪些接口给客户端调用,定向Tag就是用来标注这些方法的参数值。
(3)定向Tag
定向Tag表示在跨进程通信中数据的流向,用于标注方法的参数值,分为 in、out、inout 三种。其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。此外,如果AIDL方法接口的参数值类型是:基本数据类型、String、CharSequence或者其他AIDL文件定义的方法接口,那么这些参数值的定向 Tag 默认是且只能是 in,所以除了这些类型外,其他参数值都需要明确标注使用哪种定向Tag。
(4)明确导包
在AIDL文件中需要明确标明引用到的数据类型所在的包名,即使两个文件处在同个包名下。
(5)AIDL 如何编写
AIDL 的编写主要为以下三部分:
①创建 AIDL
I、创建要操作的实体类,实现 Parcelable 接口,以便序列化/反序列化
II、新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射 aidl 文件
III、Make project ,生成 Binder 的 Java 文件
②服务端
I、创建 Service,在其中创建上面生成的 Binder 对象实例,实现接口定义的方法
II、在 onBind() 中返回
③客户端
I、实现 ServiceConnection 接口,在其中拿到 AIDL 类
II、bindService()
III、调用 AIDL 类中定义好的操作请求
有关AIDL完成跨进程通信使用demo和AIDL自动生成的AIDL文件解析,点击AIDL跨进程通信及解析查看
延伸:
跨进程通信方式:Intent,Bundle,Messenger,Binder,AIDL,ContentProvider,广播,Socket,文件共享

九、数据储存技术——持久化技术

       数据持久化就是将那些存储在内存中的瞬时数据保存到存储设备中,保证在手机或电脑关机时数据不会丢失;保存在内存中的数据处于瞬时状态,而保存在存储设备中的数据处于持久状态的;持久化技术提供了一种机制,可以让数据在瞬时状态和持久化状态之间进行转换。
持久化技术类型:文件存储,SharePreferences存储,数据库存储,SD卡

1、文件存储

       Android中最基本的数据存方式,它不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中的,适合存储一些简单的文本数据或二进制数据;若想存储复杂的文本数据,需要定义一套自己的格式规范。
1】将文件存储到文件中
       Context类中提供了一个openFileOutput()方法,可以将数据存储到指定的文件中,该方法有两个参数,参数1-文件名,参数2-文件的操作模式,可选值有MODE_PRIVATE(默认,文件名相同时,覆盖原文件内容)和MODE_APPEND(文件名相同时,追加内容)
2】从文件中读取数据
       Context类中提供了一个openFileInput()方法,用于从文件中读取数据,有一个参数(文件名),系统自动到/data/data//files/目录下加载这个文件,并返回一个FileInputStream对象,再通过Java流方式将数据读取出来。
查看对应案例,点击“蓝色字体”即可,文件存储案例

2、SharePreferences存储

       SharePreferences存储是使用键值对的方式存储数据的,即当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把对应的值取出来。且SharePreferences支持不同的数据类型存储。
1】将数据存储到SharePreferences中
使用方法:
       首先,获取SharePreferences对象,获取对象的方法有以下几种:
(1)Context类中的getSharedPreferences()方法,有两个参数,参数1-指定SharePreferences文件的名称,若不存在则创建一个,文件存放在/data/data//shared_prefs/目录下,参数2-指定操作模式,可选值为MODE_PRIVATE,和直接传入0效果是相同的,表示只有当前的应用程序才可以对这个SharePreferences文件进行读写。
(2)Activity类中的getPreferences()方法,和Context类中的getSharedPreferences()方法相似,有一个参数(操作模式),使用该方法时会自动将当前活动的类名作为SharePreferences的文件名。
(3)PreferenceManager类中的getDefaultSharedPreferences()方法
该方法为静态方法,它接收一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharePreferences文件。
       其次,得到SharePreferences对象后,开始向SharePreferences文件中存储数据,主要分3步实现:
①调用SharePreferences对象的edit()方法来获取一个SharePreferences.Editor对象;
②向SharePreferences.Editor对象中添加数据,使用putString,putInt…
③调用apply()方法将添加的数据提交,从而完成数据存储操作。
2】从SharePreferences中读取数据
       从SharedPreferences文件中读取数据,使用get()方法,如getString,getInt…,该方法有两个参数,参数1-键(存储数据时对应的键),参数2-默认值,找不到键时以该默认值返回。
查看对应案例,点击“蓝色字体”即可,SharePreferences存储案例

3、SQLite数据库存储

       SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,且适合在移动设备上使用;SQLite不仅支持标准的SQL语句,还遵循数据库的ACID事务。
       Android为了更好地管理数据库,提供了一个SQLiteOpenHelper帮助类,借助该类实现对数据库的创建和升级;SQLiteOpenHelper是一个抽象类,使用方法就是创建一个帮助类去继承它,该类有以下方法:
       两个抽象方法:onCreate()和onUpgrade(),重写这两个方法实现数据库的创建和升级;
       两个实例方法:getReadableDatabase()和getWriteableDatabase(),两个方法都可以创建或打开一个现有数据库(若数据库已存在则直接打开,否则创建一个新的数据库),返回一个可对数据库进行读写操作的对象。不同的是,若数据库不可写(如磁盘空间已满),getReadableDatabase()方法返回的对象将以只读的方式打开数据库,而getWriteableDatabase()方法则将出现异常;
       构造方法:该方法有四个参数,参数1-context,参数2-数据库名称,参数3-查询数据库时返回的Cursor(一般为null),参数4-数据库版本号。
数据库文件存放路径:/data/data/package name/databases/目录下

九、ContentProvider

1、定义
       ContentProvider(内容提供者)是Android中的四大组件之一。主要用于对外共享数据,也就是通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对指定应用中的数据进行操作。
2、ContentProvider
       Android提供了一些主要数据类型的ContentProvider,比如音频、视频、图片和私人通讯录等。
主要重写方法:
1】onCreate(),创建和升级数据库,仅当ContentResolver尝试访问我们程序中的数据时,内容提供器才会被初始化
2】query(),查询表中数据,参数Uri指那张表,查询结果存放再Cursor对象中
3】insert(),添加数据,待添加数据存放在values参数中。添加后返回这条新纪录的Uri
4】update(),更新数据,返回结果为更新行数
5】delete(),删除数据,返回结果为删除行数
6】getType(),根据传入的内容Uri来返回相应的MIME类型

public boolean onCreate() 在创建ContentProvider时调用
public Cursor query(Uri, String[], String, String[], String) 用于查询指定UriContentProvider,返回一个Cursor
public Uri insert(Uri, ContentValues) 用于添加数据到指定UriContentProviderpublic int update(Uri, ContentValues, String, String[]) 用于更新指定UriContentProvider中的数据
public int delete(Uri, String, String[]) 用于从指定UriContentProvider中删除数据
public String getType(Uri) 用于返回指定的Uri中的数据的MIME类型

通配符:*:表示匹配任意长的的任意字符,#:表示匹配任意长度的数字
UriMatcher实现匹配内容Uri功能,提供方法addURI(),3个参数authority,path和自定义代码;这样,当调用UriMatcher的match()方法时,就可以将一个Uri对象传入,返回值是某个能够匹配这个Uri对象所定义的自定义代码,根据这个自定义代码,可以判断出调用方期望访问哪张表中的数据。

public String getType(@NonNull Uri uri) {
        switch (mUriMatcher.match(uri)) {
            case TABLE1_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.android.provider.table1";
            case TABLE1_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.android.provider.table1";
            case TABLE2_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.android.provider.table2";
            case TABLE2_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.android.provider.table2";
            default:
                break;
        }
        return null;
    }

创建UriMatcher实例。调用addURI(),将期望匹配的内容Uri传递进去
MIME字符串格式:
a.必须以vnd开头
b.若内容Uri以路径结尾,后接android.cursor.dir/,若内容Uri以id结尾,后接android.cursor.item/
c.最后接vnd..
3、ContentResolver
当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver类来完成,要获取ContentResolver对象,可以使用Context提供的getContentResolver()方法。

ContentResolver cr = getContentResolver();

ContentResolver提供的方法和ContentProvider提供的方法对应的有以下几个方法。

public Uri insert(Uri uri, ContentValues values) 用于添加数据到指定UriContentProvider中。
public int delete(Uri uri, String selection, String[] selectionArgs) 用于从指定UriContentProvider中删除数据。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) 用于更新指定UriContentProvider中的数据。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 用于查询指定UriContentProvider

4、Uri
       Uri指定了将要操作的ContentProvider,其实可以把一个Uri看作是一个网址,我们把Uri分为三部分。
第一部分是"content://"。可以看作是网址中的"http://"。
第二部分是主机名或authority,用于唯一标识这个ContentProvider,外部应用需要根据这个标识来找到它。可以看作是网址中的主机名,比如"blog.csdn.net"。
第三部分是路径名,用来表示将要操作的数据。可以看作网址中细分的内容路径。

十、广播

       为了便于进行系统级别的消息通知,Android引入一套类似广播的消息机制,允许应用程序自由地发送和接收广播。
       Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器)。广播作为Android组件间的通信方式,可以使用的场景如下:
1.同一app内部的同一组件内的消息通信(单个或多个线程之间);
2.同一app内部的不同组件之间的消息通信(单个进程);
3.同一app具有多个进程的不同组件之间的消息通信;
4.不同app之间的组件之间消息通信;
5.Android系统在特定情况下与App之间的消息通信。

1、广播的分类
(1)按照发送的方式分类
①标准广播
       是一种异步的方式来进行传播的,广播发出去之后,所有的广播接收者几乎是同一时间收到消息的。他们之间没有先后顺序可言,而且这种广播是没法被截断的。
②有序广播
       是一种同步执行的广播,在广播发出去之后,同一时刻只有一个广播接收器可以收到消息。当广播中的逻辑执行完成后,广播才会继续传播。
(2)按照注册的方式分类
①动态注册广播
所谓动态注册是指在代码中注册。步骤如下 :

  • 实例化自定义的广播接收器。
  • 创建IntentFilter实例。
  • 调用IntentFilter实例的addAction()方法添加监听的广播类型。
  • 最后调用Context的registerReceiver(BroadcastReceiver,IntentFilter)动态的注册广播。

在Activity中onResume()注册,onPause()销毁。
原因:
两个方法成对出现;
onPause()在Activity死亡之前一定会被执行,确保Broadcast在APP死亡之前一定会被注销。
不在onStop()和onDestroy()中销毁,是因为有时候onStop()和onDestroy()不会被执行。
②静态注册广播
动态注册要求程序必须在运行时才能进行,有一定的局限性,如果我们需要在程序还没启动的时候就可以接收到注册的广播,就需要静态注册了。主要是在AndroidManifest中进行注册。
两者及其接收广播的区别:
(1)动态注册广播不是常驻型广播,也就是说广播跟随Activity的生命周期。注意在Activity结束前,移除广播接收器。
静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
(2)当广播为有序广播时:优先级高的先接收(不分静态和动态)。同优先级的广播接收器,动态优先于静态
(3)同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
(4)当广播为默认广播时:无视优先级,动态广播接收器优先于静态广播接收器。同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后册的。
(3)按照定义的方式分类
①系统广播
Android系统中内置了多个系统广播,每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,由系统自动发出。
系统广播,只要手机的基本操作发生变化(如开机、网络状态变化、拍照等等),都会发出相应的广播。当使用系统广播的时候,只需要在注册广播接收者时候定义相关的action即可,不需要我们手动的发送广播,系统会自动捕获。常见的如表格所示:

系统操作action
监听网络变化android.net.conn.CONNECTIVITY_CHANGE
关闭或打开飞行模式Intent.ACTION_AIRPLANE_MODE_CHANGED
充电时或电量发生变化Intent.ACTION_BATTERY_CHANGED
电池电量低Intent.ACTION_BATTERY_LOW
电池电量充足(即从电量低变化到饱满时会发出广播Intent.ACTION_BATTERY_OKAY
系统启动完成后(仅广播一次)Intent.ACTION_BOOT_COMPLETED
按下照相时的拍照按键(硬件按键)时Intent.ACTION_CAMERA_BUTTON
屏幕锁屏Intent.ACTION_CLOSE_SYSTEM_DIALOGS
设备当前设置被改变时(界面语言、设备方向等)Intent.ACTION_CONFIGURATION_CHANGED
插入耳机时Intent.ACTION_HEADSET_PLUG
未正确移除SD卡但已取出来时(正确移除方法:设置–SD卡和设备内存–卸载SD卡)Intent.ACTION_MEDIA_BAD_REMOVAL
插入外部储存装置(如SD卡)Intent.ACTION_MEDIA_CHECKING
成功安装APKIntent.ACTION_PACKAGE_ADDED
成功删除APKIntent.ACTION_PACKAGE_REMOVED
重启设备Intent.ACTION_REBOOT
屏幕被关闭Intent.ACTION_SCREEN_OFF
屏幕被打开Intent.ACTION_SCREEN_ON
关闭系统时Intent.ACTION_SHUTDOWN

②自定义广播
由应用程序开发者自己定义的广播
2、特点
静态:常驻进程中,不受组件生命周期影响
动态:跟随组件的生命周期变化,如果Activity销毁,那么广播接收器也被销毁。
缺点:必须在程序启动后才能接收广播,因为注册的逻辑是写在onCreate()方法中的。
3、应用场景
①静态:需要时刻监听广播。计算每隔一段时间的网络访问量:可静态注册,后台保存数据。
如—开机广播
②动态注册:需要在特定时刻接受广播。
如—监听手机是否插入耳机广播
动态+静态注册
如—监听手机屏幕解锁开锁
扩展:
隐式广播:指哪些没有具体指定发送给哪个应用程序的广播,大多数系统广播属于隐式广播,但少数特殊的广播仍使用静态注册的方式来接收。所有隐式广播不允许使用静态注册的方式来接收了。

十一、自定义控件

Android 自定义控件三种实现方法为组合原生控件,自己绘制和继承原生控件
1、组合原生控件,
2、自己绘制控件
a.view的绘制原理
(1)measure用来测量View的宽和高,主要是View中的:measure(),setMeasuredDimension(),onMeasure()方法
(2)layout用来确定View在父容器中放置的位置,主要是View中的:layout(),onLayout(),setFrame()方法
(3)draw用来将view绘制在屏幕上,主要是View中的:draw(),onDraw()方法。
View 的绘制分为六步:
①绘制背景;
②如果需要,保存图层信息;
③绘制 View 的内容;
④如果 View 有子 View,绘制 View 的子 View;
⑤如果需要,绘制 View 的边缘(如阴影等);
⑥绘制 View 的装饰(如滚动条等)。
其中以上六步,第二步和第五步并不是必须的,所以只需重点分析其他四步即可。
b.在实际使用中,继承 View,在 onDraw方法中绘制视图
Paint 画笔类,定义了画笔的颜色、样式、粗细、阴影等,常用方法说明如下:

setAntiAlias(); 设置是否使用抗锯齿功能。主要用于画圆圈等曲线。
setDither(); 设置是否使用防抖动功能。
etColor(); 设置画笔的颜色。
setShadowLayer(); 设置画笔的阴影区域与颜色。
setStyle(); 设置画笔的样式。Style.STROKE 表示线条,Style.FILL  表示填充。
setStrokeWidth(); 设置画笔线条的宽度。

Canvas 画布类的常用方法:

canvas.drawArc(); 绘制扇形弧形
canvas.drawBitmap(); 绘制图像
canvas.drawCircle(); 绘制圆形
canvas.drawLine(); 绘制直线
canvas.drawOval(); 绘制椭圆
 canvas.drawPath(); 绘制路径,即不规则曲线
 canvas.drawPaint(); 绘制点
 canvas.drawRect(); 绘制矩形
 canvas.drawRoundRect(); 绘制圆角矩形
 canvas.drawText();  绘制文本

3、继承原生控件

十二、多媒体

多媒体相关知识及案例,链接———>https://blog.csdn.net/weixin_44495678/article/details/121721908


# 总结

《Android 图片实现单击放大缩小》
https://blog.csdn.net/qq_41577078/article/details/86632303?utm_source=app&app_version=4.6.0

《安卓学习笔记之:实现ImageView图片双击放大及缩小》
https://blog.csdn.net/true100/article/details/50480358?utm_source=app&app_version=4.6.0

《Android 多线程:这是一份详细的AsyncTask使用教程》
https://blog.csdn.net/carson_ho/article/details/79314325?utm_source=app&app_version=4.6.1

Aidl
https://www.cnblogs.com/tangZH/p/10775848.html

https://www.cnblogs.com/codingblock/p/8436529.html

https://blog.csdn.net/u011240877/article/details/72765136

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值