目录
- 摘要:
- 一、四大组件是什么
- 二、Activity之间的通信方式
- 三、Activity各种情况下的生命周期
- 四、Fragment生命周期
- 五、两个Activity 之间跳转时必然会执行的是哪几个方法?
- 六、Activity的四种启动模式对比
- 七、Activity 怎么和Service 绑定?
- 八、请描述一下Service 的生命周
- 九、谈谈你对ContentProvider的理解
- 十、在manifest 和代码中如何注册和使用BroadcastReceiver?
- 十一、Android属性动画特性
- 十二、如何导入外部数据库?
- 十三、谈谈对接口与回调的理解
- 十四、介绍下SurfaceView
- 十五、RecycleView的使用
- 十六、 两种序列化的区别
摘要:
这篇介绍Android基础知识的文章,重点介绍Activity、Service、BroadcastReceiver、ContentProvider四大组件在各种情况下的生命周期,ApplicationContext和ActivityContext的区别,接口和回调的理解,自定义估值器和自定义插值器等内容,进一步加深了对基础知识的理解,距离上篇文章距今将近一个月哈
一、四大组件是什么
Activity、Service、ContentProvider和BroadcastReceiver
四大组件的生命周期和简单用法
Activity
生命周期 | 简单用法 |
---|---|
onCreate(Bundle) |
activity启动的时候回调的方法,进行大部分初始化的工作,比如:setContentView |
onStart() |
执行onCreate 之后回调的方法或暂停的activity执行onRestart 之后回调的方法 |
onRestart() |
执行onStop 之后的activity重新显示回调的方法 |
onResume() |
执行onRestoreInstanceState 、onRestart 或onPause 之后,与用户交互回调的方法 |
onPause() |
activity切换到了后台(但还没有kill掉)回调的方法
可以这么理解:Activity B位于Activity A前面,Activity A会回调 |
onStop() |
activity不再与用户交互时,回调的方法,紧接着可能会执行onRestart 、onDestroy 方法,也可能什么也不做 |
onDestroy() |
当activity结束或系统回收activity实例后回调的方法,执行最后的清理工作 |
Service
Context.startService()
生命周期 | 简单用法 |
---|---|
onCreate() |
service创建的时候回调该方法 |
onStartCommand() |
执行onCreate 之后回调的方法,并获取Client传递过来的参数 |
onDestroy() |
service被有效地终止后回调的方法,执行最后的清理工作,比如停止线程、解除注册
Note that:除非调用 |
Note that: 反复调用Context.startService()方法不会多次创建Service实例,即不会多次执行onCreate方法,但会多次执行onStartCommand方法
Context.bindService()
生命周期 | 简单用法 |
---|---|
onCreate() |
创建Service实例的时候,回调该方法 |
onBind() |
Client重写ServiceConnection.onServiceConnnected 方法时,获取IBinder回调的方法 |
onUnbind() |
当所有的客户端断开连接的时候回调该方法 |
onDestroy() |
service被有效地终止后回调的方法,执行最后的清理工作,比如停止线程、解除注册 |
ContentProvider
生命周期 | 简单用法 |
---|---|
onCreate() |
初始化provider回调的方法 |
query() |
将数据返回给调用者 |
insert() |
把新的数据插入到content provider |
update() |
更新content provider存在的数据 |
delete() |
从content provider中删除数据 |
getType() |
返回content provider数据的MIME类型 |
Note that: 数据访问的方法(如insert或update)可以同时被多个线程调用,它是线程安全的;其他的方法(如onCreate)只可以在应用程序的主线程中调用,同时避免耗时操作
BroadcastReceiver
BroadcastReceiver对象生命周期
生命周期 | 简单用法 |
---|---|
onReceive() |
BroadcastReceiver对象的生命在onReceive 方法没有回调的这段时间内,被认为是有效的;一旦执行了onReceive 方法内的代码,系统认为该实例已经结束并不再存活 |
BroadcastReceiver进程生命周期
生命周期 | 简单用法 |
---|---|
onReceive() |
正在执行的BroadcastReceiver是一个运行在后台的进程,除非系统内存不足外,否则该进程一直存活着onReceive 方法执行后,BroadcastReceiver的对象的生命周期结束
因此,要求在该方法执行的操作必须是同步的,否则在该方法执行完成后,系统先于非同步操作把对象所在的进程kill掉,将会存在很大的问题 |
二、Activity之间的通信方式
通信的方式 | 特点 |
---|---|
I/O数据共享 | 将数据写入数据库或文件,在另一个activity中读取,数据的读写速度比较慢,简单的数据类型可以考虑SharedPreferences ,在多线程环境下需要考虑线程安全 |
公共静态全局变量 | 对变量的修改,直接影响依赖该变量的所有activity,在多线程环境下需要考虑线程安全 |
Intent数据传递 | Intent支持保存:基本数据类型、实现Parcelable接口类型、Bundle类型、基本数据类型数组 |
广播数据传递 | 在另一个activity中注册广播接收器(BroadcastReceiver)接收数据,在当前activity将数据保存Intent并发送,实质是利用Intent传递数据方式 |
注册监听 | 在另一个activity中监听接口,在当前activity中注册接口(难点在于怎么获取另一个activity的引用) |
公共的成员变量 | 和注册监听一样,反射获取另一个activity的实例并调用其成员方法 |
三、Activity各种情况下的生命周期
正常情况
参考上文Activity生命周期介绍,绘制一个完成的生命周期图谱
title | description |
---|---|
情况1 | 一个标准的生命周期:onCreate——>onStart——>onResume——>onPause——>onStop——>onDestroy |
情况2 | onCreate——>onStart——>onResume——>onPause——>onResume.. |
情况3 | onCreate——>onStart——>onResume——>onPause——>onStop——>onRestart——>onStart——>onReusme...
Note that:这种情况先执行onRestart,再重新执行onStart、onResume |
情况3 | onCreate——>onDestroy
这种情况直接从Activity A跳转到Activity B,之间不经过其他生命周期 |
情况4 | 比较熟悉的是上面的几种情况,因为Activity的生命周期是一个回调的方法,也就是说上面的方法可能会回调也可能不会回调,还可能只是回调其中的几种(按照组合的概念,还存在其他的情况) |
异常情况
title | description |
---|---|
情况1 | onCreate——>onStart——>onReusme——>onPause——>onSaveInstanceState——>onStop... |
情况2 | onCreate——>onStart——>onResume——>onSaveInstanceState——>onPause——>onStop... 结合情况1和情况2说明:
1、 2、但 |
情况3 | onCreate——>onStart——>onResume——>onSaveInstanceState——>onDestroy (还存在其他组合的情况) |
切换横竖
title | description |
---|---|
切换横屏或切换竖屏 | onPause——>onSaveInstanceState——>onStop——>onDestroy(当前实例销毁)——>onCreate(重新初始化)——>onStart——>onRestoreInstanceState——>onResume |
显示或隐藏键盘
title | description |
---|---|
键盘显示或隐藏 | onCreate——>onStart——>onResume |
Note that:
onSaveInstanceState
方法将数据写入Bundle对象中,可以在onCreate或onRestoreInstanceState方法中读取保存的数据
onRestoreInstanceState
于onStart
方法之后回调,即onStart——>onRestoreInstanceState
onSaveInstanceState
方法和onRestoreInstanceState
方法仅在Activity异常情况或切换横竖屏情况下,才会被执行
onSaveInstanceState
可能出现在onPause之前,也可能出现在onPause之后,但onSaveInstanceState
肯定出现在onStop之前,即onSaveInstanceState——>onStop
(情况2)
Activity上有Dialog的时候按Home键时的生命周期
Activity启动到可见,经历生命周期:
onCreate——>onStart——>onResume
从Dialog弹出到按下Home键,经历生命周期:
onPause——>onSaveInstanceState——>onStop
重新打开上面的Activity,经历生命周期:
onRestart——>onStart——>onResume
横竖屏切换的时候,Activity 各种情况下的生命周期
先理解android:configChanges
标签的作用,该可以添加的属性值包括:
orientation
,指定Activity切换横竖屏影响其生命周期,参考上文生命周期介绍
keyboardHidden
,指定Activity显示隐藏键盘影响其生命周期(演示生命周期没变化)
screenSize
,指定Activity屏幕大小改变后影响其生命周期
title | description |
---|---|
切换横屏或竖屏 | 1. onPause——>onSaveInstanceState——>onStop——>onDestroy(当前Activity销毁)——>onCreate(重新初始化)——>onStart——>onRestoreInstanceState——>onReusme...
包括下面情况: 2. 包括下面情况: |
弹出或隐藏键盘 | 除了onCreate——>onStart——>onResume 生命周期外,不添加额外的(测试结果)
包括下面情况: |
Activity与Fragment之间生命周期比较
四、Fragment生命周期
title | description |
---|---|
onAttach(Context) |
在onCreate 方法前调用,将fragment挂载到其上下文环境中,随后调用onCreate 方法 |
onCreate() |
调用该方法初始化创建fragment,在onAttach 之后onCreateView 之前调用
注意,正当activity被创建的过程中调用该方法,因此不要在该方法中做一些与activity交互的操作,最好在activity创建好后,即在 |
onCreateView() |
回调该方法获取fragment实例与用户交互的视图,在onCreate 方法和onActivityCreated 方法之间被调用
Note that:重写该方法并返回View后,当View被释放的时候,需要回调其 |
onViewCreated() |
当onCreateView 返回后立即回调的方法,但是是在恢复保存的View状态之前 |
onActivityCreated() |
当activity被创建且fragment的view视图实例化后回调该方法,通常用来做一些最后的初始化工作
在 |
onViewStateRestored() |
当所有保存的状态被恢复到fragment的视图层次结构时回调该方法,可以用来做一些基于保存状态的初始化工作
在 |
onStart() |
当fragment对用户可见时回调该方法,同时该方法绑定于activity生命周期的onStart 方法 |
onResume() |
当fragment对用户可见时且正在运行时回调该方法,同时该方法绑定于activity生命周期的onResume 方法 |
onPause() |
当fragment不再可见的时候回调该方法,同时该方法绑定于activity生命周期的onPause 方法 |
onStop() |
当fragment不再启动的时候回调该方法,同时该方法绑定于activity生命周期的onStop 方法 |
onDestroyView() |
当由onCreateView 方法创建的View从fragment卸载的时候回调该方法,该方法在onStop 之后onDestroy 之前回调 |
onDestroy() |
当fragment不再被使用的时候回调该方法,同时该方法绑定于activity生命周期的onDestroy 方法 |
onDetach() |
当fragment从activity上卸载时回调该方法,该方法是在onDestroy 之后调用 |
对比Activity生命周期图谱(参考上文)
发现,Fragment不仅具备Activity的全部生命周期方法,而且还添加了Activity生命周期没有的多个回调方法,这些回调方法包括:
onAttach()
,当fragment挂载于activity时回调该方法
onCreateView()
,当实例化fragment与用户交互的视图时回调该方法
onActivityCreated
,当ativity被创建且fragment视图被实例化后回调该方法
onDestroyView
,当fragment视图被卸载时回调该方法
onDetach
,当fragment从activity卸载时回调该方法
各种情况下的生命周期
title | description |
---|---|
Fragment在Activity中replace | 新替换的Fragment:onAttach > onCreate > onCreateView > onViewCreated > onActivityCreated > onStart > onResume
被替换的Fragment:onPause > onStop > onDestroyView > onDestroy > onDetach |
Fragment在Activity中replace,并addToBackStack | 新替换的Fragment(没有在BackStack中):onAttach > onCreate > onCreateView > onViewCreated > onActivityCreated > onStart > onResume
新替换的Fragment(已经在BackStack中):onCreateView > onViewCreated > onActivityCreated > onStart > onResume 被替换的Fragment:onPause > onStop > onDestroyView |
Fragment在ViewPager中切换 | 有多种情况,先将切换前的简称PF;切换后的简称NF;其他简称OF(在ViewPager中setUserVisibleHint能反映出Fragment是否被切换到后台或前台,所以在这里也当作生命周期)
情况1: Fragment未加载 情况2: Fragment已加载 情况3,重写FragmentPagerAdapter的destroyItem方法,且相关Fragment已加载 |
Fragment进入了运行状态 | Fragment在上述的各种情况下进入了onResume后,则进入了运行状态,以下4个生命周期方法将跟随所属的Activity一起被调用:onPause > onStop > onStart > onResume |
关于Fragment的onActivityResult方法 | 使用Fragment的startActivity方法时,FragmentActivity的onActivityResult方法会回调相应的Fragment的onActivityResult方法,所以在重写FragmentActivity的onActivityResult方法时,注意调super.onActivityResult |
五、两个Activity 之间跳转时必然会执行的是哪几个方法?
Activity A跳转到Activity B
首先,执行了Activity A的onCreate()——>onStart——>onResume——>onPause()
方法
其次,执行Activity B的onCreate——>onStart——>onResume
方法
最后,执行Activity A的onSaveInstanceState——>onStop
//演示两个Activity 之间跳转时必然会执行的是哪几个方法?
final Button button=new Button(this);
button.setText("点我跳转喔");
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
//第一种情况
startActivity(new Intent(LifecycleHomePressedActivity.this,ChangeScreenStateActivity.class));
//执行Activity A 生命周期:onCreate—>onStart—>onResume—>onPause,
// 其次Activity B 生命周期:onCreate—>onStart—>onResume,
// 再执行Activity A 生命周期:onSaveInstantState—>onStop
}
});
ll.addView(button);
//第二种情况
startActivity(new Intent(LifecycleHomePressedActivity.this,ChangeScreenStateActivity.class));
//执行Activity A 生命周期:onCreate—>onStart—>onResume—>onPause,
// 其次Activity B 生命周期:onCreate—>onStart—>onResume,
// 再执行Activity A 生命周期:onSaveInstantState—>onStop
前台切换到后台,然后再回到前台,Activity生命周期回调方法
title | description |
---|---|
前台切换后台 | onCreate——>onStart——>onResume——>onPause——>onStop |
后台切换前台 | onRestart——>onStart——>onResume |
弹出Dialog,生命值周期回调方法
弹窗Dialog,Activity的生命周期不变,参考上文:Activity上有Dialog的时候按Home键时的生命周期
六、Activity的四种启动模式对比
title | description |
---|---|
standard |
标准启动模式,每次创建一个Activity实例并加入栈内 |
singleTop |
启动的Activity位于栈顶,则重用该实例;否则创建新的实例并加入栈内 |
singleTask |
启动的Activity位于栈中,则重用该实例并清除位于其栈顶上的所有实例
否则判断实例需要的任务栈是否存在,存在则创建Activity的实例并加入栈内 否则,先创建一个栈,再创建Activity的实例并加入栈内 |
singleInstance |
加强版的singleTask ,创建的Activity实例位于独立的任务栈 |
Activity状态保存与恢复
Activity在异常情况、横竖屏切换、按下Home键时回调其onSaveInstanceState
方法,该方法用于保存需要恢复的状态信息
恢复状态信息可以在onCreate(Bundle)
方法中执行,也可以在onRestoreInstanceState()
方法中执行
Fragment状态保存startActivityForResult是哪个类的方法,在什么情况下使用?
startActivityForResult
是FragmentActivity类中的方法
在当前Fragment需要跳转到某个Activity获取返回数据的时候使用,需要注意的是:
- 必须使用Fragment的
startActivityForResult(Intent,int)
方法(参考第一篇引用) - Fragment嵌套的子fragment将无法接收到返回的数据()参考第三篇引用)
- activity重写了
onActivityResult
,一定要加上super.onActivityResult()
Android-Fragment 的 onActivityResult 收不到结果
Android踩坑随笔Fragment中onActivityResult方法不被调用
Android的Fragment中onActivityResult不被调用的解决方案(绝对管用)
谈谈Fragment中的onActivityResult
如何实现Fragment的滑动?
第一种方式:PagerSlidingTabStrip+ViewPager+Fragment(新闻栏目切换效果) demo源码
第二种方式:TabLayout+ViewPager+Fragment(类似微信底部导航切换效果) demo源码
第三种方式:ActionBar+ViewPager+Fragment demo源码
fragment之间传递数据的方式?
在当前的Fragment注册接口,在另一个Fragment中监听接口,实现数据的传递
通过Fragment通过的setArguments(Bundle)
方法和getArguments(Bundle)
实现数据的传递
可以多个Fragment共享挂载的Activity成员变量的方式实现数据的传递
也可以通过文件的方式或数据库的方式实现数据的传递
通过EventBus第三方框架实现数据的传递
七、Activity 怎么和Service 绑定?
通过startService(Intent)
,将一个Service绑定到当前Activity所在的上下文环境中,调用stopService(Intent)
或stopSelf()
停止服务
通过bingService(Intent,ServiceConnection,int)
,将一个Service绑定到当前Activity的上下文环境中,调用unbindService(ServiceConnection)
解除绑定
怎么在Activity 中启动自己对应的Service?
使用bindService(Intent,ServiceConnection,int)
,在Activity中启动自己对应的Service
Activity回调了onStop()
,Service同时也停止运行,直到Activity回调onResume()
方法才恢复运行
service和activity怎么进行数据交互?
通过广播的方式,在当前Activity中发送广播,在Service中注册广播并接收传递过来的数据
通过Handler+Message的方式,当前Activity持有Service的对象,可以使用Service声明的Handler实例发送消息,并在Service中处理
不同进程的Service和Activity,通过Messenger的方式实现跨进程通信
通过Intent的方式,将需要交互的数据存储到Intent中,并在Service的onStartCommand()
方法中处理
Service的开启方式
一种是startService(Intent)
,使用该种方式开启的Service,经历onCreate()——>onStartCommand()
,除非调用stopService()
或stopSelf()
方法停止服务,否则服务在资源充足且无异常情况下将一种运行
另一种是bindService(Intent,ServiceConnection,int)
,使用该种方式开启的Service,经历onCreate()——>onBind()
,服务的生命周期伴随着Activity的生命周期,当前Activity回调了onStop
方法,服务停止;当Activity回调了onResume
方法,服务继续运行,调用unbind(ServiceConnection)
解除绑定
八、请描述一下Service 的生命周
使用startService(Intent)
方法启动的服务,生命周期是onCreate()——>onStartCommand()——>onDestroy()
。多次调用startService(Intent)
,执行一次onCreate()
方法,执行多次onStartCommand()
方法,除非调用stopService()
或stopSelf()
方法,否则将在资源充足且无异常的情况下一种运行
使用bindService(Intent,ServiceConnection,int)
方式启动的服务,生命周期是onCreate()——>onBind()——>onUnbind()——>onDestroy
,服务的生命周期伴随着Activity的生命周期
Android中startService的使用及Service生命周期
Android中bindService的使用及Service生命周期
九、谈谈你对ContentProvider的理解
ContentProvider提供在不同应用程序之间数据的共享,实现跨进程之间的通信,同时其insert()
和update()
方法允许同步操作,它是线程安全的
在Context的上下文环境中,通过getContentResolver()
返回一个ContentResolver
对象,用它来操作ContentProvider提供的方法
说说ContentProvider、ContentResolver、ContentObserver 之间的关系
ContentProvider是当前应用程序提供数据共享的对象,ContentResolver可以看作是ContentProvider的代理,其他想要访问数据的应用程序只需要于ContentResolver交互,保证ContentProvider内部数据的安全
ContentObserver是观察Uri是否发生变化的观察者,通过ContentResolver.registerContentObserver()
注册观察者
请描述一下广播BroadcastReceiver的理解
BroadcastReceiver是一个广播接收器,它是一个抽象类,子类通过重写onReceive(Context,Intent)
处理接收到的信息
可以用于不同组件之间数据的传递
动态注册的广播仅当组件运行时,广播接收器才接收到广播;静态注册的广播随着程序一起启动,接收符合条件的广播
广播又区分为有序广播和无序广播:
有序广播的特点是高优先级的BroadcastReceiver先接收,相同优先级的先注册的接收,没有丢弃的广播继续传递给下一个接收器,支持使用getResultData()
、setResultData()
和abortBroadcast()
常见的应用场景:广播拦截
广播的分类
title | description |
---|---|
粘性广播 | 在Android 5.0被弃用 |
普通广播 | 注册多个接收器,不确定哪个先执行,常用的一种广播 |
有序广播 | 高优先级的接收器先接收,同时拥有终止广播的权利,相同优先级的广播,先注册的先接收,优先级低的最后接收 |
系统广播 | 比如:网络变化、电池电量、屏幕状态等 |
应用内广播 | 使用应用内广播实现跨进程通信 |
广播使用的方式和场景
方式 | 场景 |
---|---|
普通广播 | 实现不同组件之间的通信 |
有序广播 | 实现广播的拦截 |
系统广播 | 监听系统状态并提示 |
应用内广播 | 实现不同进程之间的通信 |
十、在manifest 和代码中如何注册和使用BroadcastReceiver?
在manifest注册
<receiver android:name=".CustomBroadcast">
<intent-filter>
<action android:name="cn.teachcourse.action"/>
<category android:category="cn.teachcourse.category"/>
</intent-filter>
</receiver>
在代码中注册
IntentFilter filter=new IntentFilter("cn.teachcourse.action");
registerBroadcast(mReceiver,filter);
registerOrderedBroadcast();
本地广播和全局广播有什么差别?
本地广播仅限于当前应用程序内使用,更加高效和安全,不支持静态注册
LocalBroadcastManager lbm=LocalBroadcastManager.getInstance(this);
lbm.registerBroadcast(mReceiver,mIntentFilter);//注册本地广播
sendBroadcast(new Intent(LOCAL_ACTION));//发送本地广播
unregisterBroadcast(mReceiver);//解除本地广播
全局广播既可以在当前应用程序内使用,也可以在不同应用程序间使用
BroadcastReceiver,LocalBroadcastReceiver 区别
参考上文:本地广播和全局广播有什么差别
AlertDialog,popupWindow,Activity区别
AlertDialog | PopupWindow | Activity |
---|---|---|
AlertDialog的所有构造方法都是protect,只可以通过内部类AlertDialog.Builder返回一个实例
AlertDialog封装了一系列的方法快速设置弹窗的样式,比如:单选样式、复选样式和对话框样式 AlertDialog默认居中显示,除显示内容外, 添加了一层遮罩,默认点击显示内容外区域弹窗消失 |
PopupWindow使用new关键字创建一个实例
PopupWindow需要指定显示的相对位置,提供 PopupWindow默认不支持点击空白区域弹窗消失 |
Activity设置Dialog主题样式,实现弹窗效果,效果和AlertDialog差不多
Activity实质是一个Activity组件,可以在生命周期内处理复杂的逻辑,如果你的弹窗需要显示较多的数据且需要组件之间的通信,可以考虑Activity弹窗 Activity继承了Dialog弹窗,考虑继承的单一性,制约了其他主题样式的使用 |
对话框:阻塞式PopupWindow 和非阻塞AlertDialog
Android AlertDialog和PopupWindow使用和区别
安卓开发中Dialog和PopupWindow和新建Activity的使用场景有什么区别?
Application 和 Activity 的 Context 对象的区别
Application的Context | Activity的Context |
---|---|
Application的Context是整个应用程序的生命周期,应用程序退出,Context生命周期结束
在大部分情况下可以使用Application的Context代替Activity的Context,但与UI相关的,只能使用Activity的Context,比如:show a Dialog,start a Activity或Layout inflation
|
Activity的Context是Activity的生命周期,Activity销毁Context可能还被其他对象持有,造成内存泄露,除了在特定情况下使用Activity的Context,其他情况下建议使用Application的Context
在Activity的Context中添加了ContextThemeWrapper封装类,允许修改和替换ContextWrapper |
Android Application中的Context和Activity中的Context的异同
两种Context的区别,Activity 和Application
十一、Android属性动画特性
在一定时间内,通过不断对值执行改变并应用到属性上
插值器
TimeInterpolator
插值器实现类及其它们之间的区别:
常用插值器 | 区别 |
---|---|
AccelerateDecelerateInterpolator |
先加速再减速 |
AccelerateInterpolator |
仅加速 |
AnticipateInterpolator |
先向后再向前最后碰撞 |
AnticipateOvershootInterpolator |
先向后再向前最后晃动 |
BounceInterpolator |
小球弹跳效果 |
CycleInterpolator |
循环播放效果 |
DecelerateInterpolator |
减速效果 |
LinearInterpolator |
线性效果 |
OvershootInterpolator |
晃动效果 |
重写TimeInterpolator接口的getInterpolation()
方法,实现各色各样的动画效果
例子源码
Android开发 - 图形化生成的贝塞尔插值器
Android动画TimeInterpolator(插值器)和TypeEvaluator(估值器)分析
渗透理解Animation时间插值Interpolator类
差值器动画演示(在线网站)
估值器
TypeEvaluator
估值器传入一个具体的类型,实现evaluate(float fraction,T start,T end)
抽象方法,估值的思想:start+(fraction*(end-start))
,最后返回得到的值,这可以从源码看出来
ArgbEvaluator
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
//估值的思想:`start+(fraction*(end-start))`
return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
(int)((startR + (int)(fraction * (endR - startR))) << 16) |
(int)((startG + (int)(fraction * (endG - startG))) << 8) |
(int)((startB + (int)(fraction * (endB - startB))));
}
FloatArrayEvaluator
public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
float[] array = mArray;
if (array == null) {
array = new float[startValue.length];
}
for (int i = 0; i < array.length; i++) {
float start = startValue[i];
float end = endValue[i];
//估值的思想:`start+(fraction*(end-start))`
array[i] = start + (fraction * (end - start));
}
return array;
}
FloatEvaluator
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
//估值的思想:`start+(fraction*(end-start))`
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
IntArrayEvaluator
public int[] evaluate(float fraction, int[] startValue, int[] endValue) {
int[] array = mArray;
if (array == null) {
array = new int[startValue.length];
}
for (int i = 0; i < array.length; i++) {
int start = startValue[i];
int end = endValue[i];
//估值的思想:`start+(fraction*(end-start))`
array[i] = (int) (start + (fraction * (end - start)));
}
return array;
}
IntEvaluator
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
//估值的思想:`start+(fraction*(end-start))`
return (int)(startInt + fraction * (endValue - startInt));
}
PointFEvaluator
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
//估值的思想:`start+(fraction*(end-start))`
float x = startValue.x + (fraction * (endValue.x - startValue.x));
float y = startValue.y + (fraction * (endValue.y - startValue.y));
if (mPoint != null) {
mPoint.set(x, y);
return mPoint;
} else {
return new PointF(x, y);
}
}
RectEvaluator
public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
int left = startValue.left + (int) ((endValue.left - startValue.left) * fraction);
int top = startValue.top + (int) ((endValue.top - startValue.top) * fraction);
int right = startValue.right + (int) ((endValue.right - startValue.right) * fraction);
int bottom = startValue.bottom + (int) ((endValue.bottom - startValue.bottom) * fraction);
if (mRect == null) {
return new Rect(left, top, right, bottom);
} else {
mRect.set(left, top, right, bottom);
return mRect;
}
}
它们之间的区别是,一种类型对应一种估值器,ValueAnimator
和ObjectAnimator
针对TypeEvaluator
编程,使用策略设计模式动态注入估值器实现类,实现估算当前类型的目的。
定义一个估值器,确定需要估算值类型,按照估值器思想:start+(fraction*(end-start))
,返回得到的值
Android 属性动画探究(二)——TypeEvaluator解析与自定义
浅析Android动画(三),自定义Interpolator与TypeEvaluator
常用数学函数表达式演示(在线网站)
十二、如何导入外部数据库?
将外部数据库文件放置资源文件夹目录:res/raw
,然后通过读取文件流的方式将文件写入到Android数据库目录: /data/data/package_name/db_name
,最后使用数据库相关api操作数据库内容
如果数据库的大小发生了变化或上述方式读取和写入出错,可以批量导出SQL语句并批量将SQL语句导入数据库
LinearLayout、RelativeLayout、FrameLayout的特性及对比,并介绍使用场景。
LinearLayout | RelativeLayout | FrameLayout |
---|---|---|
线性布局,可在水平方向或垂直方向按照权重大小排列多个控件 | 相对布局,选取父控件或相邻控件为参照,设定当前控件的位置,控件可以重叠 | 帧布局,栈型结构,最后添加的控件覆盖最先添加的控件 |
十三、谈谈对接口与回调的理解
回调的定义:
类A调用类B的方法methodB()
,方法执行后,类B调用类A的方法methodA()
,通知类A执行的结果,那么最后调用的methodA()
,是一个回调的方法
public class SimpleExampleTest {
public static void main(String[] args) {
A a = new A();
B b=new B(a);//同理,类B持有类A的引用
a.setB(b);//类A持有类B的引用
a.b.methodB();//类A调用类B的方法methodB(),然后类B回调了类A的方法methodA()
}
static class A {
B b;
public void setB(B b) {
this.b = b;
}
/**
* 在该方法中接收执行结果
*/
void methodA() {
System.out.println("类B回调了类A的方法:methodA()");
}
}
static class B {
A a;
public B(A a) {
this.a = a;
}
void methodB() {
System.out.println("类B执行的方法:methodB()");
a.methodA();// 通知类A,已经执行了类B的方法
}
}
}
接口符合回调的定义,接口充当类B的角色,接口定义的抽象方法称为回调方法,在另一个类A中回调接口的setXXX()
方法,并在重写接口的回调方法中接收通知,因此接口可以用于不同类之间的通信
public interface IEmployee {
void result();
}
public class Boss {
IEmployee employee;
public void setEmployee(IEmployee employee) {
this.employee = employee;
}
/**
* 老板给员工分配了一个任务
*/
void allocateTask() {
System.out.println("Boss:下班前,手写一个冒泡排序算法。。。");
employee.result();
}
}
public class IEmployeeImpl implements IEmployee{
Boss boss;
public IEmployeeImpl(Boss boss) {
this.boss = boss;
}
@Override
public void result() {
int a[]={1,3,4,5,8,3,1};
sortingOfBubble(a);
System.out.println("Employee:已经完成冒泡排序算法的封装,经过测试没有问题。。。");
}
private void sortingOfBubble(int[] a) {
for(int i=0;i<a.length-1;i++){
for(int j=0;j<a.length-1;j++){
int temp;
if(a[j]>a[j+1]){
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
}
}
public class TestDriver {
public static void main(String[] args) {
Boss boss = new Boss();
IEmployee employee = new IEmployeeImpl(boss);
boss.setEmployee(employee);
boss.allocateTask();//Boss类调用allocateTask()方法,并调用了Employee的result()方法
}
}
回调的原理
根据上面回调的例子,大概明白一个原理,实现类重写了接口的方法,声明的接口变量最终执行的是被重写的方法,回调的原理使用的是多态的特性。
多态的特点:
将实例化对象的引用赋值给超类变量,超类变量引用的是被子类重写的方法,或者
将实例化对象的引用赋值给接口变量,接口变量引用的是被实现类重写的方法
写一个回调demo
回调的定义:
类A调用类B的方法methodB()
,方法执行后,类B调用类A的方法methodA()
,通知类A执行的结果,那么最后调用的methodA()
,是一个回调的方法
十四、介绍下SurfaceView
SurfaceView继承自View,对View进行优化,适合在子线程频繁地刷新界面,并实现了双缓存机制
SurfaceHolder.Callback
提供管理SurfaceView生命周期的三个方法:surfaceCreated()
、surfaceChanged()
、surfaceDestroyed()
,在相应的回调方法中处理操作
Note that: 不用画布,直接在窗口上进行绘图叫做无缓冲绘图。用了一个画布,将所有内容都先画到画布上,在整体绘制到窗口上,就该叫做单缓冲绘图,那个画布就是一个缓冲区。用了一个画布和一个Bitmap,画布保存临时的绘图,Bitmap保存绘图的历史记录,这样就叫做双缓冲绘图。
public class DrawingRectangleBoard extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private SurfaceHolder mSurfaceHolder;
private Canvas mCanvas;
private boolean isDrawing;
Paint mPaint;
Path mPath;
float firstX;
float firstY;
public DrawingRectangleBoard(Context context) {
this(context, null);
}
public DrawingRectangleBoard(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
/**
* 初始化
*/
private void init() {
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaint.setStrokeWidth(6f);
mPaint.setColor(Color.parseColor("#FF4081"));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPath = new Path();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
isDrawing = true;
Log.e("surfaceCreated", "--" + isDrawing);
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isDrawing = false;
Log.e("surfaceDestroyed", "--" + isDrawing);
}
@Override
public void run() {
while (isDrawing) {
try {
mCanvas = mSurfaceHolder.lockCanvas();
if (mCanvas != null) {
mCanvas.drawColor(Color.WHITE);
mCanvas.drawPath(mPath, mPaint);
}
} finally {
if (mCanvas != null) {
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
firstX = x;
firstY = y;
break;
case MotionEvent.ACTION_MOVE:
//绘制矩形时,要先清除前一次的结果
mPath.reset();
if (firstX < x && firstY < y) {
//↘方向
mPath.addRect(firstX, firstY, x, y, Path.Direction.CCW);
} else if (firstX > x && firstY > y) {
//↖方向
mPath.addRect(x, y, firstX, firstY, Path.Direction.CCW);
} else if (firstX > x && firstY < y) {
//↙方向
mPath.addRect(x, firstY, firstX, y, Path.Direction.CCW);
} else if (firstX < x && firstY > y) {
//↗方向
mPath.addRect(firstX, y, x, firstY, Path.Direction.CCW);
}
invalidate();
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return true;
}
}
例子源码
Android SurfaceView入门学习
Android视图SurfaceView的实现原理分析
Android中SurfaceView的使用详解
十五、RecycleView的使用
关于RecyclerView的简单使用:列表样式、网格样式、点击item监听和拖拽效果,参考例子源码
十六、 两种序列化的区别
实现Parcelable接口和实现Serializable接口的作用:持久化数据对象,方便数据的读写
Parcelable | Serializable |
---|---|
Parcelable是Android SDK提供的更加高效的序列化方式:读写速度更快,内存占用更小
需要重写 |
Serializable是Java提供的传统序列化方式,不需要重写任何的方法,注意添加一个SerializableID即可,使用方便 |
你可能感兴趣的文章
转载请注明出处: https://www.teachcourse.cn/2634.html ,谢谢支持!