核心组件笔记
一、Activity组件
1、四个名词的理解:
Application:表示一个应用程序(APP)
Activity:表示一个界面(活动),都有生命周期(相对独立的,由系统统一管理,它们可以是同一个
进程也可以不同的进程)
Activity栈:用于管理Activity对象的数据结构(先进后出),Android系统永远显示栈顶的那个Activity
(必须获取焦点),一个Activity栈可以包含多个APP中的Activity
Task:一个Activity栈组成了一个Task(任务),可以有多个任务,当前正在操作的任务为的当前显示任务
2、Activity的生命周期(七个方法):
//在Activity创建时调用,表示当前Activity被创建,我们通常在该方法中初始化UI组件(或其它)
//该方法执行完成后,会调用onStart方法
protected void onCreate(Bundle savedInstanceState);
//在onCreate之后被调用,表示界面可见,但不能交互,在该方法之后调用onResume方法
protected void onStart();
//当一个在stopped状态下的Activity被激活时,会调用该方法重新启动,该方法会调用onStart方法
protected void onRestart();
//创建时在onStart方法之后被调用,表示界面可以交互,获取到焦点,此时Activity进入激活状态
//(resumed),如果当前Activity在paused状态下重新激活(获取焦点)会调用该方法
通常在该方法中可以恢复相关状态值
protected void onResume();
//当前Activity被另一个Activity覆盖还可见时,此方法会被调用,此时Activity进入暂停状态(paused)
//paused状态下的Activity被重新激活,会调用onResume方法,该状态下Activity可能被kills
//通常保存状态值的相关工作
protected void onPause();
//当前Activity被另一个Activity完全覆盖不可见时,此方法会被调用,此时Activity进入停止状态(stopped)
//stopped状态下的Activity被重新激活,会调用onRestart方法,该状态下Activity可能被kills
通常可以释放某些资源
protected void onStop();
//当前Activity被销毁时调用,通常可以在该方法中释放资源,该状态下Activity可能被kills
protected void onDestroy();
面试题:简述Activity的生命周期?
3、在Activity中启动另一个Activity:
通过当前Activity中定义的方法:this.startActivity(Intent intent)
4、Activity之间的传值:
<1> 通过创建一个Bundle对象来封装数据
<2>通过Intent为我们准备好的Bundle
把Bundle对象通过intent.putExtra("data",bundle);
在需要获取参数的Activity中使用
Intent intent = this.getIntent();
intent.getBundleExtra("data")
<3>两个Activity之间传递序列化对象
由于Activity是相对独立的一个程序,是可以在同一个进程中或不同进程中,所以Android使用统一的序列化
对象的方式实现两个Activity之间的对象传递(注意多个进程间通讯传递对象必须要序列化)
public class User implements Serializable{...}
然后使用intent.putExtra(String key,Serializable s);方法实现传递。
<4>使用Parcelable传递对象
<1>要求被传递对象的类实现Parcelable接口
实现Parcelable接口的目的是我们自己来决定对象中的数据如何写入Parcel,和从Parcel中得到数据
<2>然后我们可以使用intent.putExtra(String key,Parcelable p);方法实现传递。
注意:
Serializable和Parcelable传递对象的区别
1、Serializable接口在序列化对象时会产生大量临时变量,用于存储数据、数据类型、类信息,
所以会比较消耗内存。效率较低。可以把对象序列化到文件中,永久保存。是Java中提供的接口。
2、Parcelable接口只会传递数据,所以占用内存较小,效率较高。只能用于对象的传递,是Android提供
的接口。
5、处理Activity的返回结果:
目的是我们在当前Activity中想通过启动另一个Activity来协助完成数据的输入,或者返回数据。
那么我们可以这样做:
<1>启动一个带返回结果的Activity:startActivityForResult(int requsetCode,Intent intent)
requsetCode:表示请求编码(用来唯一标记本次请求的常量)
intent:意图
<2>在被启动的Activity中通过以下方法返回结果:
把要返回的数据存入Intent对象中,然后调用
this.setResult(int resultCode,Intent data); //resultCode可以使用Activity.RESULT_OK来表示成功返回
finish(); //结束Activity
<3>在当前Activity中重写方法
protected void onActivityResult(int requestCode, int resultCode, Intent data) {}
来实现获取返回结果
6、Activity屏幕方向与显示方式:
<1>可以在清单文件AndroidManifest.xml的Activity配置中使用
screenOrientation="portrait" //竖屏
screenOrientation="landscape" //横屏
一般项目中,都指定为竖屏
<2>通过设置Activity的theme样式来改变窗体是否是全屏,或窗体
再或者通过代码设置为全屏
<3>默认Activity方向变化或屏幕改变,会导致Activity重新创建,此时我们应该保存Activity的状态值,
以让用户继续之前的操作。
我们可以重写Activity中的
protected void onSaveInstanceState(Bundle outState) {}
把相关状态值保存到outState中,那Activity重新创建时会把该Bundle对象传入到onCreate方法中
我们就可以在onCreate方法中取值还原状态值
<4>Activity的配置可以通过设置configChanges属性来防止哪种情况下Activity不重新创建,
而是调用 public void onConfigurationChanged(Configuration newConfig) {}
方法来改变一些配置。可以设置为:
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
我们可以在onConfigurationChanged方法中实现横竖屏不同的布局及功能。
注意:通过我们在项目开发中没有特殊横屏需求,那么默认应该强制用户使用竖屏
有特殊需求的情况:
视频播放界面、游戏界面、浏览图片界面
7、SharedPrefenrence保存数据
SharedPrefenrence是android为我们的应用程序提供了一个只有该程序可以访问的文件存取方式
SharedPrefenrence的数据称永久保存在本地:存储路径是:
/data/data/应用程序的主包名(com.moliying.a31_activity/shared_prefs/test.xml)
一般用于存储一个程序中的配置信息
可以通过在onPause方法中保存数据,因为该状态的activity可能被回收
可以通过在onResume()方法中读取数据,因为该状态的activity处理激活状态
或可以在onStop()或onStart()中实现数据的保存数据或读取数据。
注意:
在项目开发中,我们可以使用SharedPrefenrence实现:
1、首次引导页的状态记录
2、自动登录功能
3、系统设置功能
二、Intent组件
是Activity/Service/BrocastReceiver组件实现通讯的桥梁
1、使用Intent的方式有两种:
直接Intent:通过指定组件名称(component)
间接Intent:使用属性匹配的方式(action,category,data,type)
2、Intent的七个属性:
component:组件名称
action:动作(字符串)
category:类别,用于修饰动作(字符串)
data:数据(Uri对象)
type:数据类型
extra:扩展信息(要传递的数据)
flag:标记(启动模式)
3、间接Intent的匹配过程
(1)使用action查找组件
<1>创建Intent对象,通过设置Intent对象的action属性
intent.setAction(action); //action就是一个字符串,
比如:com.moliying.intent.action.MY_ACTION
startActivity(intent);
<2>在需要被匹配成功的Activity组件的清单文件声明中必须使用:
<intent-filter>
<action name="com.moliying.intent.action.MY_ACTION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
注意:在配置Activity的intent-filter时必须添加默认分类
<category android:name="android.intent.category.DEFAULT"/>
(2)action+category查找组件
<1>创建Intent对象,通过设置Intent对象的action属性和category属性
intent.setAction(action); //action就是一个字符串,
//比如:com.moliying.intent.action.MY_ACTION
intent.addCategory(category); //category也是一个字符串来表示
//比如:com.moliying.intent.category.MY_CATEGORY1
//category(类别)可以有多个,在intent内部使用一个集合来存储(ArraySet)
startActivity(intent);
<2>在需要被匹配成功的Activity组件的清单文件声明中必须要有:
对应的action
还要包含所有的category,只要有一个category找不到,就表示匹配失败
(3)action+data+type查找组件
data代表要访问的数据,具体使用一个Uri对象来表示指向数据的地址
(比如网址,文件地址,表示访问数据库的地址(CP))
type代表要访问的数据类型,具体使用一个字符串来表示,比如一个网页类型为:text/html
单独setData()会清除type属性,相返单独setType()会清除data属性
如果要同时设置data和type,使用:setDataAndType方法
注意:
以上5个属性用于查找组件
component属性,通常用于非常明确要访问的组件,并该组件不会提供其它版本,使用直接Intent
可以提高查找组件的效率(灵活差),一般建议访问本程序内的组件使用.
action/category/data/type,间接Intent
最常使用的两个属性是action+data,表示 动作+数据“做什么”
4、extra 属性(略)参考Activity的传值
5、flags属性
表示设置Activity的启动模式:
(1)Intent.FLAG_ACTIVITY_NEW_TASK
在新任务中打开Activity,需要与Activity的清单文件配置中 android:taskAffinity="mytask" 一起使用
例如现在栈1的情况是:A B C(C位于栈顶),C通过intent跳转到D,并且这个Intent添加了FLAG_ACTIVITY_NEW_TASK标记,
如果D这个Activity在Manifest.xml中声明了添加Task affinity,系统首先会查找有没有和D的Task affinity相同的task栈存在,如果存在,就将D压入那个栈,
如果不存在则会新建一个D的affinity的栈将其压入。如果D的taskAffinity默认没有设置,
则会将其压入栈1,变成A B C D,这样就和不加FLAG_ACTIVITY_NEW_TASK标记效果是一样的了。
但如果试图从非Activity的非正常途径启动一个activity,比如从一个service、BroadcastReceiver等中启动一个Activity,则intent要设置Intent.FLAG_ACTIVITY_NEW_TASK标记。
Activity要存在于Activity栈中,而非Activity的途径启动Activity时必然不存在一个Activity的栈,
所以要新建一个Activity栈来存放要启动的Activity。
(2)FLAG_ACTIVITY_CLEAR_TOP:
例如现在的栈情况为A B C D, D此时通过intent跳转到B,如果这个intent设置
FLAG_ACTIVITY_CLEAR_TOP标记,
则栈情况变为:A B。如果没有添加这个标记,则栈的情况将会变为:A B C D B 。
也就是说,如果设置了FLAG_ACTIVITY_CLEAR_TOP标记,并且目标Activity在栈中已存在,则会把位于该目标Activity之上的Activity从栈中弹出销毁。
(3)FLAG_ACTIVITY_NO_HISTORY:
例如现在栈的情况为:A B C 。C通过intent跳转到D,这个intent添加FLAG_ACTIVITY_NO_HISTORY标志,
此时界面显示D的内容,但是它并不会压入栈中。如果按返回键,返回到C,栈的情况是:A B C。如果D中又跳转到E,栈的情况为:A B C E,此时按返回键会回到C,因为D根本就没有被压入栈中。
(3)FLAG_ACTIVITY_SINGLE_TOP:
和Activity的Launch mode的singleTop类似。如果某个intent设置了这个标志,
并且这个intent的目标Activity就是栈顶的Activity,那么将不会新建一个实例压入栈中。
简言之,目标Activity已在栈顶则跳转过去,不在栈顶则在栈顶新建Activity。
比较常用的两种模式:
Intent.FLAG_ACTIVITY_NEW_TASK 在service、brocastReceiver中启动Activity时使用
Intent.FLAG_ACTIVITY_CLEAR_TOP
6、Activity的四种启动模式(在清单文件中配置)
standard: (**)
默认的模式,每次启动会新创建一个Activity对象。
singleTop:(**)
在当前任务栈中,判断栈顶是否为当前的Activity,如果是,就直接使用,
如果不是,再创建新的Activity放入栈顶。
singleTask:(一般使用****)
在当前任务栈中,判断栈里是否存在Activity,如果不存在,创建一个新Activity入栈,
如果存在,会把该Activity之上的所有Activity清除出栈,显示当前Activity。
(该栈中可以添加其他Activity实例)
singleInstance: (创建一个重量级的Activity时,通常会使用 *)
新创建一个任务栈,放入新创建的Activity,该任务栈只允许存在一个Activity实例,
如果已存在,那么会切换到该任务栈。
7、Intent相关应用
/**
* 显示网页
* @param v
*/
public void showWeb(View v){
Uri uri = Uri.parse("http://www.baidu.com");
Intent it=new Intent(Intent.ACTION_VIEW,uri);
startActivity(it);
}
//拨打电话:调用拨号程序
//要使用这个必须在配置文件中加入
//Intent it = new Intent(Intent.ACTION_CALL, uri);
//<uses-permission android:name="android.permission.CALL_PHONE"/>?
public void callClick(View v){
Uri uri = Uri.parse("tel:182345678");
Intent it = new Intent(Intent.ACTION_DIAL, uri);
startActivity(it);
}
//调用发送短信的程序
public void sendSMS(View v){
Intent it = new Intent(Intent.ACTION_VIEW);
it.putExtra("sms_body", "hello");
it.setType("vnd.android-dir/mms-sms");
startActivity(it);
}
//发送短信
public void sendSMS2(View v){
Uri uri = Uri.parse("smsto:0800000123");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
it.putExtra("sms_body", "hello moliying");
startActivity(it);
}
//播放音乐
public void playMusic(View v){
Uri uri = Uri.parse("file:///sdcard/moliying_music/music/晴天.mp3");
Intent it=new Intent(Intent.ACTION_VIEW,uri);
//it.addFlags(it.FLAG_ACTIVITY_NEW_TASK);//非必须选项?
it.setDataAndType(uri, "audio/mp3");
startActivity(it);
}
//Uninstall 程序
public void uninstallClick(View v){
//获取手机中已安装的应用包信息
List<PackageInfo> list = getPackageManager().getInstalledPackages(PackageManager.GET_PERMISSIONS);
for(PackageInfo p:list){
Log.i("packageName",p.packageName);
}
Uri uri = Uri.parse("package:com.moliying.vince.a31_activity");
Intent intent = new Intent(Intent.ACTION_DELETE, uri);
startActivity(intent);
}
//安装APK
public void installAPK(View v){
Uri uri = Uri.fromFile(new File("/sdcard/Download/soguo.apk"));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
startActivity(intent);
}
三、Service组件
一个Service表示一个应用程序组件,可以在后台长时间执行操作,不提供用户界面。
其他应用程序可以启动一个服务,如果用户切换到其它程序,他将继续在后台运行;
另外一个组件可以绑定到一个服务,来执行IPC(进程间通讯)比如,在后台执行网络操作、
音乐播放、文件操作、或者内容提供者。
Service的两种启动方式:
(1)Started
其它组件(Activity)调用 startService()方法来启动Service,通过调用stopService()来停止服务,
或者服务本身调用stopSelf()方法终止服务。
该方式启动的Service生命周期方法为:
onCreate()-->onStartCommand()-->onDestroy()
一个服务被创建后只会存在一个实例,创建时会调用onCreate()方法,之后的每次执行将调用
onStartCommand()方法。
默认情况一个服务运行在UI线程中,如果服务在执行耗时操作,会阻塞UI线程(用户体验差),
此时,我们应该在服务中使用子线程来完成这个操作。
创建一个Service:
public class MyService extends Service{
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
在清单文件中配置Service:
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
enabled:可用的
exported:可以被外部应用访问
问:使用Service和Thread的区别:
服务是系统应用组件,受系统保护,可以长期在后台运行,异常终止会有重启机制,
还可以运行在单独的进程中,只需在清单文件中配置 android:process=":xxxx"
而一个线程只能在指定的进程中执行,终止后无法重新启动,并不受系统保护。
IntentService
由于Service需要我们自己来控制线程和服务的终止,Android为我们提供了一个Service的子类
IntentService,它以对列的结构来依次执行每次任务,并且只会启动一个线程来执行任务,当所有任务执行
完成后,将自动终止服务。
(2)Bound
实现IPC(进程间通讯),因为我们通常会在一个APP中使用多个进程,保正各个进程的独立运行,又能
实现比较方便通讯。
RPC(远程进程通讯)
AIDL(Android 接口定义语言),用于实现IPC通讯定义业务接口的文件,通过该文件会自动生成一个与
接口同名的类:该类的主要是提供接口的存根、网络通讯代理、判断是否是同一个进程通讯等
实现一个IPC示例的步骤:
1、创建AIDL文件,定义相关的业务方法
2、通过AIDL生成的类,实现一个业务方法的具体实现
3、通过Service把业务接口暴露出来,其它程序通过绑定到该服务获取业务对象(代理对象)
4、另一个程序通过绑定服务后,可以直接调用业务方法
bindService() 绑定服务
unBindService() 解除绑定
具体实现:
<1>AIDL文件:类似于接口,但不能使用修饰符
package com.moliying.a33_service;
interface IPerson {
boolean login(String name,String pass);
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
编译后将生成IPerson类
<2>编写一个业务方法实现类:IPersonImpl
public class IPersonImpl extends IPerson.Stub {
@Override
public boolean login(String name, String pass) throws RemoteException {
if("admin".equals(name) && "123".equals(pass)){
return true;
}
return false;
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
<3>定义一个Service类:
public class PersonService extends Service {
private IPersonImpl personImpl;
public PersonService() {
personImpl = new IPersonImpl();
}
@Override
public IBinder onBind(Intent intent) {
return personImpl;
}
@Override
public boolean onUnbind(Intent intent) {
personImpl = null;
return super.onUnbind(intent);
}
}
清单文件中通过使用action使用:
<service
android:name=".PersonService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.moliying.action.PERSON"/>
</intent-filter>
</service>
<4>编写测试:
//参数:intent,绑定成功后的回调接口,标记(绑定并自动创建服务)
bindService(intent,conn, Context.BIND_AUTO_CREATE);
/**
* 绑定服务的回调接口
*/
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//在绑定成功后会被调用
//如果是同一个进程,那么该方法返回业务对象,
//如果不是同一个进程,那么该方法返回代理对象
iPerson = IPerson.Stub.asInterface(service);
Log.i("BoundActivity",iPerson.toString());
isBound = true;
Toast.makeText(BoundActivity.this, "bound success", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
//该方法在正常解绑服务时不会调用,在异常结束时才调用
isBound = false;
}
};
//退出时解除绑定
unbindService(conn);
AIDL文件中支持的类型有:
基本数据类型
String
CharSequence
List<String>
Map<String,Integer>
使用自定义类型:
<1>要求类(Work)实现Parcelable接口(实现进程通讯时传输数据用)
<2>创建Work.aidl 文件声明一个类型:
package com.moliying.service;
parcelable Work;
<3>在使用Work类型的aidl文件中:
import com.moliying.service.Work;
interface IPerson{
Work getWork(); //使用自定义类型
}
服务就是可以在后台一直保持运行
什么事情需要在后台一直运行?下载,定时检查,播放音乐,(started)
如果你要为其它程序提供服务(登录、支付、分享)(bound)
使用Messenger实现IPC:
<1>服务类
public class MessengerService extends Service {
static final int MSG_SAY_HELLO = 1;
//消息处理器
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
final Messenger mMessenger = new Messenger(new IncomingHandler());
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
<2>客户端绑定服务:
public class ActivityMessenger extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;
boolean mBound;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
mBound = false;
}
};
//给服务发送消息
public void sayHello(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
//Activity启动时绑定服务
@Override
protected void onStart() {
super.onStart();
// Bind to the service
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
//Activity停止时解绑服务
@Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
实现Android 实现IPC的两种方式:
1、AIDL(线程不安全,支持并发,实现比较麻烦)
2、Messenger(线程安全(队列),不支持并发,API简洁)
四、广播接收器(BroadcastReceiver)
广播是Android提供的系统级别的应有组件,我们利用广播机制,可以非常方便的处理实时消息
比如:开机、网络状态变化,电量变化,时间变化,短信,等系统都会发出一条广播,如果此时有
对应的广播接收器(BroadcastReceiver),那么会自动触发其onReceive方法
广播类别:
<1>普通广播,无序的,无法终止,所有匹配的接收器都能接收到
<2>有序广播,有序的,优先级高的广播接收器可以终止广播,终止后,优先级低的接收器就接不到了
<3>粘性广播,保留Intent,直到去掉
(1)普通广播
创建 一个广播接收器:
<1>创建 一个类,继承BroadcastReceiver类
public class MyReceiver extends BroadcastReceiver{
public static final String MY_ACTIION = "action.MY_ACTION";
@Override
public void onReceive(Context context, Intent intent) {
}
}
<2>在清单文件中注册:
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="action.MY_ACTION"/>
</intent-filter>
</receiver>
<3>发送广播测试:
context.sendBroadcast(Intent intent);
注册广播接收器的两种方式:
<1>通过清单文件注册(静态注册)
<2>通过代码注册(动态注册)
代码注册:通过在onResume和onPause中注册或解除
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction("action.MY_ACTION");
registerReceiver(myReceiver2,filter);//注册广播接收器
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(myReceiver2);//解除广播注册
}
清单文件注册与代码注册:
<1>优先高的先执行,如果相同,那么代码优先。
<2>代码注册必须在所依赖的界面正常显示时起作用,通常关闭后解除
<3>清单文件注册,在启动后会一直有效,所以相对代码注册范围更大
onReceive方法:会在主线程中执行,该执行不要超过10秒,否则超时异常,不要在该方法中实现弹窗
等操作。一般此方法我们会执行比较简单短小的操作。通常可以配合通知一起使用。
(2)有序广播
发送一个有序广播,广播接收器,将按优先级来接收:优先高的->代码注册->清单文件注册
(-1000~1000)
发送一个有序广播:
//intent,权限(字符串)
sendOrderedBroadcast(intent,null);
//广播接收器:
public class MyReceiver extends BroadcastReceiver{
public static final String MY_ACTIION = "action.MY_ACTION";
@Override
public void onReceive(Context context, Intent intent) {
//为下一个广播传递数据
Bundle data = new Bundle();
data.putString("data","12345");
setResultExtras(data);
Toast.makeText(con text, "MY_ACTIION", Toast.LENGTH_SHORT).show();
//abortBroadcast(); 终止广播(优先级高的终止广播后,优先级低的将无法接收广播)
//拦截
}
}
(3)粘性广播
sendStickyBroadcast(new Intent(MyReceiver.MY_ACTIION));
发送一个粘性广播,需要添加权限:
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
粘性广播会缓存Intent对象,直到对应的接收器启动后接收,比较消耗资源,不建议使用
(4)接收开机启动广播
<1>写一个广播接收器:
public class MyReceiver extends BroadcastReceiver{
public static final String MY_ACTIION = "action.MY_ACTION";
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "开机启动", Toast.LENGTH_SHORT).show();
//启动服务等操作
}
}
<2>在清单文件中注册:
<inter-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<category android:name="android.intent.category.DEFAULT"/>
</inter-filter>
<3>添加权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
(5)接收网络状态变化的广播接收器
<1>定义一个广播接收器:
public class ConnectivityReceiver extends BroadcastReceiver {
public ConnectivityReceiver() {}
@Override
public void onReceive(Context context, Intent intent) {
//获取系统服务(网络连接管理器)
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
//获取当前激活的网络信息
NetworkInfo info = cm.getActiveNetworkInfo();
if(info!=null){
//判断网络类型
switch (info.getType()){
case ConnectivityManager.TYPE_MOBILE:
Toast.makeText(context, "当前网络连接是MOBILE", Toast.LENGTH_SHORT).show();
break;
case ConnectivityManager.TYPE_WIFI:
Toast.makeText(context, "当前网络连接是WIFI", Toast.LENGTH_SHORT).show();
break;
}
}else{
Log.i("ConnectivityReceiver","当前没有网络连接");
}
}
}
<2>在清单文件中注册:
<receiver
android:name=".ConnectivityReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
该action的值:android.net.conn.CONNECTIVITY_CHANGE是在ConnectivityManager类中定义的
<3>添加权限:
访问网络状态:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
(6)接收电量变化的广播
<1>定义一个广播接收器:
public class BatteryReceiver extends BroadcastReceiver {
public BatteryReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
int currLevel=intent.getIntExtra(BatteryManager.EXTRA_LEVEL,0);//当前电量??
int total= intent.getIntExtra(BatteryManager.EXTRA_SCALE,1);//总电量??
int percent=currLevel*100/total;
Toast.makeText(context,"当前电量为:"+percent+"%", Toast.LENGTH_SHORT).show();
}
}
<2>在清单文件中注册:
<receiver
android:name=".BatteryReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BATTERY_CHANGED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
如果要立即获取电量信息,可以:
//获取当前电量
public void getBatteryClick(View v){
//要立即获取电量的,而不是等电量变化的广播,可以使用:
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
Intent intent = registerReceiver(null,filter); //null表示没有接收器
int curr = intent.getIntExtra(BatteryManager.EXTRA_LEVEL,0);
int total = intent.getIntExtra(BatteryManager.EXTRA_SCALE,1);
int percent = curr*100/total;
Toast.makeText(MainActivity.this, "当前电量是:"+percent+"%", Toast.LENGTH_SHORT).show();
}
五、短信
(1)发送短信
SmsManager类:短信管理器对象
通常使用:SmsManager sm = SmsManager.getDefault();
<1>发送短信的代码:
//获取短信服务的管理器
SmsManager smsManager = SmsManager.getDefault();
String content = "查询话费余额";
// smsManager.divideMessage();
PendingIntent sendIntent = PendingIntent.getBroadcast(this,0,new Intent(SEND_ACTION),0);
PendingIntent deliveryIntent = PendingIntent.getBroadcast(this,0,new Intent(DELIVERY_ACTION),0);
//发送短信,参数(电话号码,运营商服务中心的号码null,内容,发送成功回执PI,接收成功回执PI)
smsManager.sendTextMessage("10086",null,content,sendIntent,deliveryIntent);
<2>接收回执的广播接收器
class MySMSReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(SEND_ACTION.equals(action)){
Toast.makeText(context, "短信已送达", Toast.LENGTH_SHORT).show();
}else if(DELIVERY_ACTION.equals(action)){
Toast.makeText(context, "对方已接收", Toast.LENGTH_SHORT).show();
}
}
}
<3>注册广播接收器:
private static final String SEND_ACTION = "send_action";
private static final String DELIVERY_ACTION = "delivery_action";
MySMSReceiver smsReceiver = new MySMSReceiver();
@Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter();
filter.addAction(SEND_ACTION);
filter.addAction(DELIVERY_ACTION);
registerReceiver(smsReceiver,filter);
}
@Override
protected void onStop() {
super.onStop();
unregisterReceiver(smsReceiver);
}
<4>添加发送短信的权限:
<uses-permission android:name="android.permission.SEND_SMS"/>
(2)接收短信
当手机号接到短信后,首先由系统接收,然后系统会发出一条有序广播:
匹配的Action是:
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
添加权限:
<uses-permission android:name="android.permission.RECEIVE_SMS" />
public class SMSReceiver extends BroadcastReceiver {
public SMSReceiver() {}
@Override
public void onReceive(Context context, Intent intent) {
Bundle data = intent.getExtras();
//PDU 是短信的一种协议名称(protocol description unit 协议描述单元)
Object[] pdus = (Object[]) data.get("pdus");//pdus是固定的key值
if(pdus!=null) {
//SmsMessage 短信消息对象
SmsMessage[] smsMessages = new SmsMessage[pdus.length];
String phoneNumber = "";
StringBuilder buf = new StringBuilder(100);
for (int i=0;i<smsMessages.length;i++){
smsMessages[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
smsMessages[i].getDisplayOriginatingAddress();//电话号码
buf.append(smsMessages[i].getDisplayMessageBody());//短信内容
}
Toast.makeText(context, phoneNumber+"-"+buf, Toast.LENGTH_SHORT).show();
}
}
}
六、AlarmManager服务
系统服务(闹钟、定时),可以在指定的某个时间,触发一个操作
获取AlarmManager服务对象通过:
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
am.set()方法来执行一次操作
am.setRepeating方法来重复执行一个操作(需要取消后才停止)
(1)示例:
AlarmManager alarmManager;
PendingIntent operation;
public void startAlarmClick(View view){
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long triggerAlMillis = System.currentTimeMillis()+3000; //3秒以后触发
operation = PendingIntent.getBroadcast(this,0,new Intent(AlarmReceiver.ALARM_ACTION),0);
//只执行一次
//参数(类型,触发时间(毫秒),操作(PendingIntent))
//alarmManager.set(AlarmManager.RTC_WAKEUP,triggerAlMillis,operation);
//重复执行
//参数(类型,触发时间(毫秒),间隔时间,操作(PendingIntent))
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,triggerAlMillis,5000,operation);
}
//停止(取消)闹钟
public void stopAlarmClick(View view){
alarmManager.cancel(operation);
}
public static class AlarmReceiver extends BroadcastReceiver{
public static final String ALARM_ACTION = "alarm_action";
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "get up get up!!!!", Toast.LENGTH_SHORT).show();
}
}
(2)闹钟示例:
<1>通过一个按钮单击触发一次闹钟
public void alarmClick(View v){
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long triggerAlMillis = System.currentTimeMillis()+3000; //3秒以后触发
operation = PendingIntent.getActivity(this,0,new Intent(this,AlarmActivity.class),0);
//只执行一次
//参数(类型,触发时间(毫秒),操作(PendingIntent))
alarmManager.set(AlarmManager.RTC_WAKEUP,triggerAlMillis,operation);
}
<1>创建AlarmActivity,用于在闹钟启动时显示的界面
把该Activity的主题设置为墙纸:
<activity android:name=".AlarmActivity"
android:theme="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
//在onCreate方法中唤醒屏幕,并调用音乐播放器
public class AlarmActivity extends Activity {
private MediaPlayer mp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//唤醒屏幕
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD|
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON|
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
setContentView(R.layout.activity_alarm);
initMediaPlayer();//调用音乐播放器
}
}
初始化音乐播放器,播放音乐:
private void initMediaPlayer(){
mp = new MediaPlayer();
try {
mp.setDataSource("/sdcard/moliying_music/music/霍元甲.mp3");
mp.prepare();//准备,预加载
mp.start();
showDialog();
} catch (IOException e) {
e.printStackTrace();
}
}
再来一次时,需要再次调用闹钟的设置:
public void alarm(){
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long triggerAlMillis = System.currentTimeMillis()+3000; //3秒以后触发
PendingIntent operation = PendingIntent.getActivity(this,0,new Intent(this,AlarmActivity.class),0);
//只执行一次
//参数(类型,触发时间(毫秒),操作(PendingIntent))
alarmManager.set(AlarmManager.RTC_WAKEUP,triggerAlMillis,operation);
}
闹钟响起时,在界面上显示一个弹窗,该方法可以在播放音乐时调用:
private void showDialog(){
//AlertDialog使用android.app.AlertDialog,不用使用V7包下的
//原因是我们已经把当前Activity的主题设置为非V7主题
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("闹钟");
builder.setMessage("闹钟响起");
builder.setPositiveButton("再来一次", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
alarm();
finish();
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
builder.show();
}
退出界面是销毁播放器:
@Override
protected void onDestroy() {
super.onDestroy();
if(mp!=null){
mp.release();//释放
}
}
最后,由于播放器需要读取sdcard文件,所以需要添加访问sdcard的权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
七、Fragments组件
碎片、一部分、在一个Activity中可以由多个Fragment组件,Fragment必须依赖Activity的。
Activity:系统的应用程序组件,占用资源更多更大
Fragment:看作是一个轻量级的Activity,占用的资源更少
在一个Activity界面上Fragment就是其中的一个行为,多个行为共同组合成为一个界面,并且可以相互
替换。
在Activity上添加Fragment的两种方式:
(1)使用布局文件
<1>在Activity的布局文件中:
<fragment
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="com.moliying.fragment.MenuFragment"
android:id="@+id/menu_fragment"/>
<fragment
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="3"
android:name="com.moliying.fragment.ContentFragment"
android:id="@+id/content_fragment"/>
<2>
创建MenuFragment类,继承Fragment
public class MenuFragment extends Fragment{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onPause() {
super.onPause();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.menu_layout,container,false);
return view;
}
}
//menu_layout.xml文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="红色"
android:id="@+id/button4" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="蓝色"
android:id="@+id/button5" />
</LinearLayout>
创建ContentFragment
public class ContentFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onPause() {
super.onPause();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.content_layout,container,false);
return view;
}
}
content_layout布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="not data"
android:textSize="50sp"
android:gravity="center"
android:id="@+id/textView_data"
android:layout_gravity="center_horizontal" />
</LinearLayout>
(2)使用代码
<1>在Activity的布局中定义一个布局容器组件,用于添加Fragment
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.moliying.fragment.Main2Activity">
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:id="@+id/menu_fragment"
android:layout_weight="1"
android:name="com.moliying.fragment.MenuFragment"/>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:id="@+id/content_frameLayout"
android:layout_weight="3"></FrameLayout>
</LinearLayout>
<2>在Activity中创建Fragment对象,并添加到content_frameLayout容器中:
ContentFragment contentFragment = new ContentFragment();
//添加fragment到指定布局
private void add() {
FragmentManager fm = getFragmentManager();
// android.support.v4.app.FragmentManager f = getSupportFragmentManager();
//开始事务
FragmentTransaction ft = fm.beginTransaction();
//添加(容器布局ID,要添加的Fragment)
ft.add(R.id.content_frameLayout,contentFragment);
//fragmentTransaction.replace(R.id.frame_layout,contentFragment);//添加或替换
// fragmentTransaction.remove(contentFragment);//删除
ft.commit();//提交事务
}
(3)在Activity中给Fragment传参
在动态添加一个Fragment到Activity中时,我们可以在创建Fragment对象时为其传参
<1>创建Fragment时通过构造方法传参(会在屏幕变化时数据丢失,不建议使用)
<2>使用静态方法传参,并保存参数
注意:一个Fragment对象必须保留默认构造方法(在系统重新创建时会调用)
一个为Fragment传参的示例:
<1>在Fragment中定义一个静态方法返回一个Fragment实例
//默认无参构造必须保留
//不建议使用带参构造方法(屏幕变化重新创建时会丢失数据)
// public ContentFragment(String data){
// }
public static ContentFragment getInstance(String data){
ContentFragment contentFragment = new ContentFragment();
Bundle args = new Bundle();
args.putString("data",data);
//setArguments 设置 参数
contentFragment.setArguments(args);
return contentFragment;
}
<2>onCreateView中获取参数值实现操作
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.content_layout,container,false);
TextView textView_data = (TextView) view.findViewById(R.id.textView_data);
String data = getArguments().getString("data");
textView_data.setText(data);
return view;
}
(4)处理Fragment返回栈
<1>在添加Fragment时使用:
//把当前Fragment添加到返回栈
fragmentTransaction.addToBackStack(null);
<2>重写键盘按下动作的事件
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//判断用户按下了返回键
if(keyCode==KeyEvent.KEYCODE_BACK){
if(getFragmentManager().getBackStackEntryCount()>0){
getFragmentManager().popBackStack();//出栈
return true;
}
}
return super.onKeyDown(keyCode, event);
}
获取布局文件中定义Fragment组件可以通过以下两个方式:
menuFragment = (MenuFragment) getFragmentManager().findFragmentById(R.id.menu_fragment);
//menuFragment = (MenuFragment) getFragmentManager().findFragmentByTag("tag");
这两个方法都需要在布局文件中定义Fragment时添加id 或 tag 属性
(5)与Activity的交互
<1>宿主:Fragment所依赖的Activity称为Fragment的宿主
<2>Fragment->Activity->Fragment 传值方式
在Fragment类中可以通过getActivity()方法获取其所依赖的Activity对象
这样 我们就可以直接调用Activity中的方法了,但是这种与Activity交互的方式,会导致Fragment
直接依赖一个具体的Activity,在Android设计中,Fragment应该是可以附加到多个Activity上,
以重复使用相同的功能,所以直接依赖一个具体的Activity是一种不好的设计。
那么我们应该怎么办?
让Fragment依赖自己定义的内部接口,该接口需要由宿主来实现,这样的接口我们称回调接口
,所以,哪个Fragment想往外传值,就定义自己的回调接口。
两个Fragment传值示例:
<1>定义两个Fragment,TitleFragment和CityFragment:
public class TitleFragment extends Fragment implements View.OnClickListener{
private Button button_bj,button_sh;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.title_layout,null);
button_bj = (Button) view.findViewById(R.id.button2_bj);
button_sh = (Button) view.findViewById(R.id.button3_sh);
button_bj.setOnClickListener(this);
button_sh.setOnClickListener(this);
return view;
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button2_bj:
//单击事件(需要给另一个Fragment传值)
break;
case R.id.button3_sh:
//单击事件(需要给另一个Fragment传值)
break;
}
}
}
TitleFragment的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:gravity="center_horizontal"
android:background="#f7c1c1"
android:padding="16dp"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="北京"
android:id="@+id/button2_bj" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="上海"
android:id="@+id/button3_sh" />
</LinearLayout>
public class CityFragment extends Fragment {
private TextView textView_city;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.city_layout,null);
textView_city = (TextView) view.findViewById(R.id.textView_city);
return view;
}
}
CityFragment的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="not data"
android:textSize="30sp"
android:gravity="center"
android:id="@+id/textView_city"
android:layout_gravity="center_horizontal" />
</LinearLayout>
<2>在Activity中使用两个Fragment:
public class Main4Activity extends AppCompatActivity{
private TitleFragment titleFragment;
private CityFragment cityFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
titleFragment = (TitleFragment) getFragmentManager().findFragmentById(R.id.title_fragment);
cityFragment = (CityFragment) getFragmentManager().findFragmentById(R.id.city_fragment);
}
}
Main4Activity的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.moliying.fragment.Main4Activity">
<fragment
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/title_fragment"
android:name="com.moliying.fragment.TitleFragment"/>
<fragment
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="5"
android:id="@+id/city_fragment"
android:name="com.moliying.fragment.CityFragment"/>
</LinearLayout>
<3>在TitleFragment类里定义接口,并在按钮的单击事件中调用,该接口由宿主Activity来实现:
public static interface OnSetValueListener{
public void setValue(String value);
}
private OnSetValueListener onSetValueListener;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//必须要求宿主实现
onSetValueListener = (OnSetValueListener) getActivity();
}
public void onClick(View v) {
switch (v.getId()){
case R.id.button2_bj:
onSetValueListener.setValue("北京");
break;
case R.id.button3_sh:
onSetValueListener.setValue("上海");
break;
}
}
<4>宿主Activity实现Fragment的回调接口,并调用其它Fragment的方法把值传过去:
public class Main4Activity extends AppCompatActivity implements TitleFragment.OnSetValueListener{
//....
@Override
public void setValue(String value) {
cityFragment.setCity(value);
}
}
<5>在CityFragment中定义一个setCity方法来接收参数,并设置到TextView组件中
public void setCity(String city){
textView_city.setText(city);
}
(6)PreferenceFragment类的使用
使用PreferenceFragment来实现应用的配置选项(3.0之前使用PreferenceActivity)
PreferenceFragment会自动把选项设置的值保存在应用的shared_prefs下的xml文件中
使用PreferenceFragment实现配置示例:
<1>在res/xml/创建一个xml配置文件:app_options_set.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="通用">
<CheckBoxPreference
android:key="auto_login"
android:title="自动登录"
android:summary="勾选后可自动登录" />
</PreferenceCategory>
<PreferenceCategory
android:title="个人设置">
<EditTextPreference
android:key="nickName"
android:title="昵称"
android:summary="单击设置昵称"
android:dialogTitle="设置昵称" />
<ListPreference
android:key="city"
android:title="选择所在城市"
android:summary="单击选择你所在的城市"
android:entries="@array/city"
android:entryValues="@array/city"
android:dialogTitle="城市列表" />
</PreferenceCategory>
</PreferenceScreen>
列表项在strings.xml文件中定义:
<string-array name="city">
<item>北京市</item>
<item>上海市</item>
<item>广州市</item>
<item>深圳市</item>
<item>保定市</item>
</string-array>
<2>创建 一个Fragment类继承PreferenceFragment
public class OptionSettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//绑定配置文件
addPreferencesFromResource(R.xml.app_option_set);
}
}
<3>在Activity上附加Fragment
(7)ViewPager+Fragment实现应用框架
<1>Activity类的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.moliying.fragmentviewpagerdemo.MainActivity">
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/viewPager"/>
<RadioGroup
android:gravity="center"
android:id="@+id/tab_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/tab_bg"
android:orientation="horizontal">
<RadioButton
android:gravity="center"
android:button="@null"
android:drawableTop="@drawable/tab_home_selector_bg"
android:textColor="@drawable/tab_selector_bg"
android:layout_weight="1"
android:tag="0"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="主页"/>
<RadioButton
android:gravity="center"
android:button="@null"
android:drawableTop="@drawable/tab_discover_selector_bg"
android:textColor="@drawable/tab_selector_bg"
android:layout_weight="1"
android:tag="1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="发现"/>
<RadioButton
android:gravity="center"
android:button="@null"
android:drawableTop="@drawable/tab_personal_selector_bg"
android:textColor="@drawable/tab_selector_bg"
android:layout_weight="1"
android:tag="2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="我"/>
</RadioGroup>
</LinearLayout>
<2>创建三个Fragment及其布局文件
IndexFragment类:
public class IndexFragment extends Fragment {
public static IndexFragment getInstance(){
IndexFragment indexFragment = new IndexFragment();
return indexFragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.index_fragment_layout,null);
return view;
}
}
IndexFragment对应的布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff5b5b">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="index layout"
android:id="@+id/textView"
android:layout_gravity="center_horizontal"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
DiscoverFragment类
public class DiscoverFragment extends Fragment {
public static DiscoverFragment getInstance(){
DiscoverFragment discoverFragment = new DiscoverFragment();
return discoverFragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.discover_fragment_layout,null);
return view;
}
}
DiscoverFragment布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f109c6">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="discover layout"
android:id="@+id/textView"
android:layout_gravity="center_horizontal"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
PersonalFragment类
public class PersonalFragment extends Fragment {
public static PersonalFragment getInstance(){
PersonalFragment personalFragment = new PersonalFragment();
return personalFragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.personal_fragment_layout,null);
return view;
}
}
PersonalFragment的布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#53ba03">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="personal layout"
android:id="@+id/textView"
android:layout_gravity="center_horizontal"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
<3>在Activity中初始化Fragment
private ArrayList<Fragment> fragments = new ArrayList<>();
fragments.add(IndexFragment.getInstance());
fragments.add(DiscoverFragment.getInstance());
fragments.add(PersonalFragment.getInstance());
<4>初始化ViewPager组件,及适配器
viewPager = (ViewPager) findViewById(R.id.viewPager);
MyViewPagerAdapter myViewPagerAdapter = new MyViewPagerAdapter(getSupportFragmentManager(), fragments);
viewPager.setAdapter(myViewPagerAdapter);
MyViewPagerAdapter类:
static class MyViewPagerAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> fragments;
public MyViewPagerAdapter(FragmentManager fm, ArrayList<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
<5>viewPager的事件处理及RadioGroup的单击事件
viewPager事件处理:在Activity类实现事件接口:ViewPager.OnPageChangeListener
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageSelected(int position) {
radioButtons.get(position).setChecked(true);
}
@Override
public void onPageScrollStateChanged(int state) {}
RadioGroup的单击事件:
在Activity类实现事件接口:RadioGroup.OnCheckedChangeListener
初始化时调用:
private ArrayList<RadioButton> radioButtons = new ArrayList<>();
tab_group.setOnCheckedChangeListener(this);
for (int i = 0; i < tab_group.getChildCount(); i++) {
radioButtons.add((RadioButton) tab_group.getChildAt(i));
}
事件方法:
public void onCheckedChanged(RadioGroup group, int checkedId) {
RadioButton rb = (RadioButton) group.findViewById(checkedId);
int index = Integer.parseInt(rb.getTag().toString());
viewPager.setCurrentItem(index);
}
八、ActionBar(标题导航操作栏)
ActionBar从Android3.0开始引入,目的为APP提供一个统一的操作栏,通过显示在上方部分
要求最小版本在11以上
(1)显示和隐藏ActionBar
//V7
ActionBar actionBar = getSupportActionBar();
//api 11
//android.app.ActionBar actionBar = getActionBar();
if(actionBar==null)return;
if(flag){
button_show_hide.setText("隐藏ActionBar");
actionBar.show();
flag = false;
}else{
button_show_hide.setText("显示ActionBar");
actionBar.hide();
flag = true;
}
(2)分裂操作栏
通过设置Activity的 android:uiOptions="splitActionBarWhenNarrow"
修改Activity的主题与父类
(3)添加操作项
添加一个res/menu/actionbar_item.xml
<item android:id="@+id/save"
android:title="保存"
android:orderInCategory="100"
android:icon="@android:drawable/ic_menu_save"
app:showAsAction="always"/>
添加菜单项:
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.actionbar_menu,menu);
return super.onCreateOptionsMenu(menu);
}
菜单项事件处理:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.save:
Toast.makeText(MainActivity.this, "save", Toast.LENGTH_SHORT).show();
break;
case R.id.share:
Toast.makeText(MainActivity.this, "share", Toast.LENGTH_SHORT).show();
break;
case R.id.add:
Toast.makeText(MainActivity.this, "add", Toast.LENGTH_SHORT).show();
break;
}
(4)启用Activity的图标导航
ActionBar actionBar = getSupportActionBar();
if(actionBar!=null){
actionBar.setDisplayShowHomeEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case android.R.id.home:
//监听图标导航事件
Toast.makeText(MainActivity.this, "home", Toast.LENGTH_SHORT).show();
break;
}
}
或者启用图标导航另一个方式是在Activity的配置文件中使用:
android:parentActivityName=".MainActivity"
导航到指定的界面。
(5)添加操作视窗(搜索框)
<1>添加一个选项菜单项:
<item android:id="@+id/search1"
android:title="搜索"
android:icon="@android:drawable/ic_menu_search"
android:orderInCategory="100"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView"/>
<2>加载菜单项
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.search_menu,menu);
MenuItem item = menu.findItem(R.id.search1);
//获取搜索组件,并添加事件处理
SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
//设置查询文本内容的监听器
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
Toast.makeText(Main2Activity.this, query, Toast.LENGTH_SHORT).show();
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
return super.onCreateOptionsMenu(menu);
}
实现搜索功能的另一个解决方案是:
自定义一个搜索按钮,点击后打开一个新的界面,在新的界面上实现一个搜索框及搜索功能,
这个搜索功能就不影响当前界面上显示的内容,搜索完毕后,用户可以导航到原来的界面。
(6)添加一个操作提供器(分享)
<1>添加分享菜单项:
<item android:id="@+id/share"
android:title="分享"
android:icon="@android:drawable/ic_menu_share"
android:orderInCategory="200"
app:showAsAction="ifRoom"
app:actionProviderClass="android.support.v7.widget.ShareActionProvider"/>
<2>在创建菜单的事件方法中添加分享事件:
//----------分享菜单项------------
public boolean onCreateOptionsMenu(Menu menu) {
MenuItem menuItem = menu.findItem(R.id.share);
ShareActionProvider shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menuItem);
shareActionProvider.setShareIntent(getIntent());
}
public Intent getIntent(){
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setData(Uri.parse("/sdcard/xxxxx"));
intent.setType("image/*");
return intent;
}
分享实现方案:
<1>ShareSDK(第三方组件)
(7)导航标签(了解一下)
file:///D:/moliying/docs/guide/topics/ui/actionbar.html
(8)ToolBar
5.0版本提供(API 21)或使用v7包的支持
可以灵话定制菜单栏,全浸式Style
<1>设置style配置文件:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@color/windowBackground</item>
<!--API 21 才可用-->
<!--<item name="android:navigationBarColor"></item>-->
</style>
<2>布局文件使用:
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:id="@+id/toolBar">
</android.support.v7.widget.Toolbar>
<3>Activity中设置
private Toolbar toolbar;
toolbar = (Toolbar) findViewById(R.id.toolBar);
toolbar.setLogo(R.mipmap.ic_menu_home);
toolbar.setTitle("微信");
toolbar.setTitleTextColor(Color.WHITE);
toolbar.setNavigationIcon(R.mipmap.ic_menu_back);
// setActionBar(toolbar);
setSupportActionBar(toolbar);
菜单项的设置与选项菜单一致
九、TelephonyManager
//电话服务管理器的API方法
private void testTelephonyManager() {
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
System.out.println("电话状态 = " + tm.getCallState());
System.out.println("唯一的设备ID = " + tm.getDeviceId());
System.out.println("设备的软件版本号 = " + tm.getDeviceSoftwareVersion());
System.out.println("手机号 = " + tm.getLine1Number());
System.out.println("获取ISO标准的国家码,即国际长途区号 = " + tm.getNetworkCountryIso());
System.out.println("当前使用的网络类型 = " + tm.getNetworkType());
System.out.println("手机类型 = " + tm.getPhoneType());
System.out.println("SIM的状态信息 = " + tm.getSimState());
System.out.println("唯一的用户ID = " + tm.getSubscriberId());
System.out.println("SIM卡的序列号 = " + tm.getSimSerialNumber());
System.out.println("服务商名称 = " + tm.getSimOperatorName());
// tm.listen(new MyPhoneStateListener(),PhoneStateListener.LISTEN_CALL_STATE);
//测试来电显示:直接发送一个广播
sendBroadcast(new Intent(this,PhoneListenerReceiver.class));
}
/**
* 电话服务的监听器
*/
private static class MyPhoneStateListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
switch (state){
case TelephonyManager.CALL_STATE_RINGING:
System.out.println("正在响铃。。。。");
break;
case TelephonyManager.CALL_STATE_IDLE:
System.out.println("挂机状态");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
System.out.println("正在接听电话...");
break;
}
}
}
监听来电显示:
<1>开机启动广播接收器:
/**
* 监听来电显示的广播接收器
*/
public class PhoneListenerReceiver extends BroadcastReceiver {
public PhoneListenerReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
//获取电话管理器对象,并注册监听器
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(new MyPhoneStateListener(context), PhoneStateListener.LISTEN_CALL_STATE);
System.out.println("PhoneListenerReceiver started");
}
static WindowManager wm = null;
private class MyPhoneStateListener extends PhoneStateListener{
private Context context;
TextView textView = null;
public MyPhoneStateListener(Context context){
this.context = context;
}
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
//响铃状态
if(state==TelephonyManager.CALL_STATE_RINGING){
//获取窗体管理器
wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
//设置为一个浮动层
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
//设置为不能触模和没有焦点
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
textView = new TextView(context);
textView.setText("当前来电号码为:"+incomingNumber);
// textView.setBackgroundColor(Color.BLACK);
wm.addView(textView,params);//添加浮动视图
//挂机状态
}else if(state== TelephonyManager.CALL_STATE_IDLE){
if(wm!=null){
wm.removeView(textView);
wm = null;
}
}
}
}
}
十、APP内容共享
(1)共享文本内容
public void shareTextClick(View view){
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT,"亲,忙什么呢,哥正在学Android呢");
intent.setType("text/plain");
startActivity(Intent.createChooser(intent,"发送文本内容"));
}
(2)共享二进制内容(比如图片)
public void shareImageClick(View view){
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
//Environment 环境对象
//Environment.getExternalStoragePublicDirectory(
//Environment.DIRECTORY_PICTURES) //获取sdcard上的对应的Pictures目录
String path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)+"/1.png";
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(path)));
intent.setType("image/png");
startActivity(intent);
// startActivity(Intent.createChooser(intent,"发送图片"));
}
(3)共享多个文件(图片)
public void shareImagesClick(View view){
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
ArrayList<Uri> uris = new ArrayList<>();
String path1 = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)+"/1.png";
String path2 = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)+"/2.png";
uris.add(Uri.fromFile(new File(path1)));
uris.add(Uri.fromFile(new File(path2)));
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM,uris);
intent.setType("image/*");
startActivity(intent);
}
(4)接收共享数据:
自定义一个接收共享数据的Activity:
<1>在清单文件中添加InterFilter配置,用于接收不同的数据类型:
<activity android:name=".ReceiverDataActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
<2>Activity类中处理接收到的数据:
public class ReceiverDataActivity extends AppCompatActivity {
private LinearLayout content_layout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_receiver_data);
content_layout = ((LinearLayout) findViewById(R.id.content_layout));
receiver();
}
private void receiver() {
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
//判断是否为发送一个数据
if(Intent.ACTION_SEND.equals(action) && type!=null){
//数据是否为文本类型
if("text/plain".equals(type)){
handlerSendText(intent);
//数据是否为图片类型
}else if(type.startsWith("image/")){
handlerSendImage(intent);
}
//判断数据是否为多个并为图片类型
}else if(Intent.ACTION_SEND_MULTIPLE.equals(action) && type!=null){
if(type.startsWith("image/")){
handlerSendMultipleImage(intent);
}
}
}
/**
* 处理多个图片内容
* @param intent
*/
private void handlerSendMultipleImage(Intent intent) {
ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
if (imageUris != null) {
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
for (int i=0;i<imageUris.size();i++){
ImageView iv = new ImageView(this);
iv.setMaxHeight(400);
iv.setMaxWidth(400);
iv.setAdjustViewBounds(true);
iv.setImageURI(imageUris.get(i));
content_layout.addView(iv,params);
}
}
}
/**
* 处理图片内容
* @param intent
*/
private void handlerSendImage(Intent intent) {
Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
if (imageUri != null) {
ImageView iv = new ImageView(this);
iv.setMaxHeight(400);
iv.setMaxWidth(400);
iv.setAdjustViewBounds(true);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
iv.setImageURI(imageUri);
content_layout.addView(iv,params);
}
}
/**
* 处理文本内容
* @param intent
*/
private void handlerSendText(Intent intent) {
String data = intent.getStringExtra(Intent.EXTRA_TEXT);
TextView textView_data = new TextView(this);
textView_data.setText(data);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
content_layout.addView(textView_data,params);
}
}
一、Activity组件
1、四个名词的理解:
Application:表示一个应用程序(APP)
Activity:表示一个界面(活动),都有生命周期(相对独立的,由系统统一管理,它们可以是同一个
进程也可以不同的进程)
Activity栈:用于管理Activity对象的数据结构(先进后出),Android系统永远显示栈顶的那个Activity
(必须获取焦点),一个Activity栈可以包含多个APP中的Activity
Task:一个Activity栈组成了一个Task(任务),可以有多个任务,当前正在操作的任务为的当前显示任务
2、Activity的生命周期(七个方法):
//在Activity创建时调用,表示当前Activity被创建,我们通常在该方法中初始化UI组件(或其它)
//该方法执行完成后,会调用onStart方法
protected void onCreate(Bundle savedInstanceState);
//在onCreate之后被调用,表示界面可见,但不能交互,在该方法之后调用onResume方法
protected void onStart();
//当一个在stopped状态下的Activity被激活时,会调用该方法重新启动,该方法会调用onStart方法
protected void onRestart();
//创建时在onStart方法之后被调用,表示界面可以交互,获取到焦点,此时Activity进入激活状态
//(resumed),如果当前Activity在paused状态下重新激活(获取焦点)会调用该方法
通常在该方法中可以恢复相关状态值
protected void onResume();
//当前Activity被另一个Activity覆盖还可见时,此方法会被调用,此时Activity进入暂停状态(paused)
//paused状态下的Activity被重新激活,会调用onResume方法,该状态下Activity可能被kills
//通常保存状态值的相关工作
protected void onPause();
//当前Activity被另一个Activity完全覆盖不可见时,此方法会被调用,此时Activity进入停止状态(stopped)
//stopped状态下的Activity被重新激活,会调用onRestart方法,该状态下Activity可能被kills
通常可以释放某些资源
protected void onStop();
//当前Activity被销毁时调用,通常可以在该方法中释放资源,该状态下Activity可能被kills
protected void onDestroy();
面试题:简述Activity的生命周期?
3、在Activity中启动另一个Activity:
通过当前Activity中定义的方法:this.startActivity(Intent intent)
4、Activity之间的传值:
<1> 通过创建一个Bundle对象来封装数据
<2>通过Intent为我们准备好的Bundle
把Bundle对象通过intent.putExtra("data",bundle);
在需要获取参数的Activity中使用
Intent intent = this.getIntent();
intent.getBundleExtra("data")
<3>两个Activity之间传递序列化对象
由于Activity是相对独立的一个程序,是可以在同一个进程中或不同进程中,所以Android使用统一的序列化
对象的方式实现两个Activity之间的对象传递(注意多个进程间通讯传递对象必须要序列化)
public class User implements Serializable{...}
然后使用intent.putExtra(String key,Serializable s);方法实现传递。
<4>使用Parcelable传递对象
<1>要求被传递对象的类实现Parcelable接口
实现Parcelable接口的目的是我们自己来决定对象中的数据如何写入Parcel,和从Parcel中得到数据
<2>然后我们可以使用intent.putExtra(String key,Parcelable p);方法实现传递。
注意:
Serializable和Parcelable传递对象的区别
1、Serializable接口在序列化对象时会产生大量临时变量,用于存储数据、数据类型、类信息,
所以会比较消耗内存。效率较低。可以把对象序列化到文件中,永久保存。是Java中提供的接口。
2、Parcelable接口只会传递数据,所以占用内存较小,效率较高。只能用于对象的传递,是Android提供
的接口。
5、处理Activity的返回结果:
目的是我们在当前Activity中想通过启动另一个Activity来协助完成数据的输入,或者返回数据。
那么我们可以这样做:
<1>启动一个带返回结果的Activity:startActivityForResult(int requsetCode,Intent intent)
requsetCode:表示请求编码(用来唯一标记本次请求的常量)
intent:意图
<2>在被启动的Activity中通过以下方法返回结果:
把要返回的数据存入Intent对象中,然后调用
this.setResult(int resultCode,Intent data); //resultCode可以使用Activity.RESULT_OK来表示成功返回
finish(); //结束Activity
<3>在当前Activity中重写方法
protected void onActivityResult(int requestCode, int resultCode, Intent data) {}
来实现获取返回结果
6、Activity屏幕方向与显示方式:
<1>可以在清单文件AndroidManifest.xml的Activity配置中使用
screenOrientation="portrait" //竖屏
screenOrientation="landscape" //横屏
一般项目中,都指定为竖屏
<2>通过设置Activity的theme样式来改变窗体是否是全屏,或窗体
再或者通过代码设置为全屏
<3>默认Activity方向变化或屏幕改变,会导致Activity重新创建,此时我们应该保存Activity的状态值,
以让用户继续之前的操作。
我们可以重写Activity中的
protected void onSaveInstanceState(Bundle outState) {}
把相关状态值保存到outState中,那Activity重新创建时会把该Bundle对象传入到onCreate方法中
我们就可以在onCreate方法中取值还原状态值
<4>Activity的配置可以通过设置configChanges属性来防止哪种情况下Activity不重新创建,
而是调用 public void onConfigurationChanged(Configuration newConfig) {}
方法来改变一些配置。可以设置为:
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
我们可以在onConfigurationChanged方法中实现横竖屏不同的布局及功能。
注意:通过我们在项目开发中没有特殊横屏需求,那么默认应该强制用户使用竖屏
有特殊需求的情况:
视频播放界面、游戏界面、浏览图片界面
7、SharedPrefenrence保存数据
SharedPrefenrence是android为我们的应用程序提供了一个只有该程序可以访问的文件存取方式
SharedPrefenrence的数据称永久保存在本地:存储路径是:
/data/data/应用程序的主包名(com.moliying.a31_activity/shared_prefs/test.xml)
一般用于存储一个程序中的配置信息
可以通过在onPause方法中保存数据,因为该状态的activity可能被回收
可以通过在onResume()方法中读取数据,因为该状态的activity处理激活状态
或可以在onStop()或onStart()中实现数据的保存数据或读取数据。
注意:
在项目开发中,我们可以使用SharedPrefenrence实现:
1、首次引导页的状态记录
2、自动登录功能
3、系统设置功能
二、Intent组件
是Activity/Service/BrocastReceiver组件实现通讯的桥梁
1、使用Intent的方式有两种:
直接Intent:通过指定组件名称(component)
间接Intent:使用属性匹配的方式(action,category,data,type)
2、Intent的七个属性:
component:组件名称
action:动作(字符串)
category:类别,用于修饰动作(字符串)
data:数据(Uri对象)
type:数据类型
extra:扩展信息(要传递的数据)
flag:标记(启动模式)
3、间接Intent的匹配过程
(1)使用action查找组件
<1>创建Intent对象,通过设置Intent对象的action属性
intent.setAction(action); //action就是一个字符串,
比如:com.moliying.intent.action.MY_ACTION
startActivity(intent);
<2>在需要被匹配成功的Activity组件的清单文件声明中必须使用:
<intent-filter>
<action name="com.moliying.intent.action.MY_ACTION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
注意:在配置Activity的intent-filter时必须添加默认分类
<category android:name="android.intent.category.DEFAULT"/>
(2)action+category查找组件
<1>创建Intent对象,通过设置Intent对象的action属性和category属性
intent.setAction(action); //action就是一个字符串,
//比如:com.moliying.intent.action.MY_ACTION
intent.addCategory(category); //category也是一个字符串来表示
//比如:com.moliying.intent.category.MY_CATEGORY1
//category(类别)可以有多个,在intent内部使用一个集合来存储(ArraySet)
startActivity(intent);
<2>在需要被匹配成功的Activity组件的清单文件声明中必须要有:
对应的action
还要包含所有的category,只要有一个category找不到,就表示匹配失败
(3)action+data+type查找组件
data代表要访问的数据,具体使用一个Uri对象来表示指向数据的地址
(比如网址,文件地址,表示访问数据库的地址(CP))
type代表要访问的数据类型,具体使用一个字符串来表示,比如一个网页类型为:text/html
单独setData()会清除type属性,相返单独setType()会清除data属性
如果要同时设置data和type,使用:setDataAndType方法
注意:
以上5个属性用于查找组件
component属性,通常用于非常明确要访问的组件,并该组件不会提供其它版本,使用直接Intent
可以提高查找组件的效率(灵活差),一般建议访问本程序内的组件使用.
action/category/data/type,间接Intent
最常使用的两个属性是action+data,表示 动作+数据“做什么”
4、extra 属性(略)参考Activity的传值
5、flags属性
表示设置Activity的启动模式:
(1)Intent.FLAG_ACTIVITY_NEW_TASK
在新任务中打开Activity,需要与Activity的清单文件配置中 android:taskAffinity="mytask" 一起使用
例如现在栈1的情况是:A B C(C位于栈顶),C通过intent跳转到D,并且这个Intent添加了FLAG_ACTIVITY_NEW_TASK标记,
如果D这个Activity在Manifest.xml中声明了添加Task affinity,系统首先会查找有没有和D的Task affinity相同的task栈存在,如果存在,就将D压入那个栈,
如果不存在则会新建一个D的affinity的栈将其压入。如果D的taskAffinity默认没有设置,
则会将其压入栈1,变成A B C D,这样就和不加FLAG_ACTIVITY_NEW_TASK标记效果是一样的了。
但如果试图从非Activity的非正常途径启动一个activity,比如从一个service、BroadcastReceiver等中启动一个Activity,则intent要设置Intent.FLAG_ACTIVITY_NEW_TASK标记。
Activity要存在于Activity栈中,而非Activity的途径启动Activity时必然不存在一个Activity的栈,
所以要新建一个Activity栈来存放要启动的Activity。
(2)FLAG_ACTIVITY_CLEAR_TOP:
例如现在的栈情况为A B C D, D此时通过intent跳转到B,如果这个intent设置
FLAG_ACTIVITY_CLEAR_TOP标记,
则栈情况变为:A B。如果没有添加这个标记,则栈的情况将会变为:A B C D B 。
也就是说,如果设置了FLAG_ACTIVITY_CLEAR_TOP标记,并且目标Activity在栈中已存在,则会把位于该目标Activity之上的Activity从栈中弹出销毁。
(3)FLAG_ACTIVITY_NO_HISTORY:
例如现在栈的情况为:A B C 。C通过intent跳转到D,这个intent添加FLAG_ACTIVITY_NO_HISTORY标志,
此时界面显示D的内容,但是它并不会压入栈中。如果按返回键,返回到C,栈的情况是:A B C。如果D中又跳转到E,栈的情况为:A B C E,此时按返回键会回到C,因为D根本就没有被压入栈中。
(3)FLAG_ACTIVITY_SINGLE_TOP:
和Activity的Launch mode的singleTop类似。如果某个intent设置了这个标志,
并且这个intent的目标Activity就是栈顶的Activity,那么将不会新建一个实例压入栈中。
简言之,目标Activity已在栈顶则跳转过去,不在栈顶则在栈顶新建Activity。
比较常用的两种模式:
Intent.FLAG_ACTIVITY_NEW_TASK 在service、brocastReceiver中启动Activity时使用
Intent.FLAG_ACTIVITY_CLEAR_TOP
6、Activity的四种启动模式(在清单文件中配置)
standard: (**)
默认的模式,每次启动会新创建一个Activity对象。
singleTop:(**)
在当前任务栈中,判断栈顶是否为当前的Activity,如果是,就直接使用,
如果不是,再创建新的Activity放入栈顶。
singleTask:(一般使用****)
在当前任务栈中,判断栈里是否存在Activity,如果不存在,创建一个新Activity入栈,
如果存在,会把该Activity之上的所有Activity清除出栈,显示当前Activity。
(该栈中可以添加其他Activity实例)
singleInstance: (创建一个重量级的Activity时,通常会使用 *)
新创建一个任务栈,放入新创建的Activity,该任务栈只允许存在一个Activity实例,
如果已存在,那么会切换到该任务栈。
7、Intent相关应用
/**
* 显示网页
* @param v
*/
public void showWeb(View v){
Uri uri = Uri.parse("http://www.baidu.com");
Intent it=new Intent(Intent.ACTION_VIEW,uri);
startActivity(it);
}
//拨打电话:调用拨号程序
//要使用这个必须在配置文件中加入
//Intent it = new Intent(Intent.ACTION_CALL, uri);
//<uses-permission android:name="android.permission.CALL_PHONE"/>?
public void callClick(View v){
Uri uri = Uri.parse("tel:182345678");
Intent it = new Intent(Intent.ACTION_DIAL, uri);
startActivity(it);
}
//调用发送短信的程序
public void sendSMS(View v){
Intent it = new Intent(Intent.ACTION_VIEW);
it.putExtra("sms_body", "hello");
it.setType("vnd.android-dir/mms-sms");
startActivity(it);
}
//发送短信
public void sendSMS2(View v){
Uri uri = Uri.parse("smsto:0800000123");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
it.putExtra("sms_body", "hello moliying");
startActivity(it);
}
//播放音乐
public void playMusic(View v){
Uri uri = Uri.parse("file:///sdcard/moliying_music/music/晴天.mp3");
Intent it=new Intent(Intent.ACTION_VIEW,uri);
//it.addFlags(it.FLAG_ACTIVITY_NEW_TASK);//非必须选项?
it.setDataAndType(uri, "audio/mp3");
startActivity(it);
}
//Uninstall 程序
public void uninstallClick(View v){
//获取手机中已安装的应用包信息
List<PackageInfo> list = getPackageManager().getInstalledPackages(PackageManager.GET_PERMISSIONS);
for(PackageInfo p:list){
Log.i("packageName",p.packageName);
}
Uri uri = Uri.parse("package:com.moliying.vince.a31_activity");
Intent intent = new Intent(Intent.ACTION_DELETE, uri);
startActivity(intent);
}
//安装APK
public void installAPK(View v){
Uri uri = Uri.fromFile(new File("/sdcard/Download/soguo.apk"));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
startActivity(intent);
}
三、Service组件
一个Service表示一个应用程序组件,可以在后台长时间执行操作,不提供用户界面。
其他应用程序可以启动一个服务,如果用户切换到其它程序,他将继续在后台运行;
另外一个组件可以绑定到一个服务,来执行IPC(进程间通讯)比如,在后台执行网络操作、
音乐播放、文件操作、或者内容提供者。
Service的两种启动方式:
(1)Started
其它组件(Activity)调用 startService()方法来启动Service,通过调用stopService()来停止服务,
或者服务本身调用stopSelf()方法终止服务。
该方式启动的Service生命周期方法为:
onCreate()-->onStartCommand()-->onDestroy()
一个服务被创建后只会存在一个实例,创建时会调用onCreate()方法,之后的每次执行将调用
onStartCommand()方法。
默认情况一个服务运行在UI线程中,如果服务在执行耗时操作,会阻塞UI线程(用户体验差),
此时,我们应该在服务中使用子线程来完成这个操作。
创建一个Service:
public class MyService extends Service{
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
在清单文件中配置Service:
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
enabled:可用的
exported:可以被外部应用访问
问:使用Service和Thread的区别:
服务是系统应用组件,受系统保护,可以长期在后台运行,异常终止会有重启机制,
还可以运行在单独的进程中,只需在清单文件中配置 android:process=":xxxx"
而一个线程只能在指定的进程中执行,终止后无法重新启动,并不受系统保护。
IntentService
由于Service需要我们自己来控制线程和服务的终止,Android为我们提供了一个Service的子类
IntentService,它以对列的结构来依次执行每次任务,并且只会启动一个线程来执行任务,当所有任务执行
完成后,将自动终止服务。
(2)Bound
实现IPC(进程间通讯),因为我们通常会在一个APP中使用多个进程,保正各个进程的独立运行,又能
实现比较方便通讯。
RPC(远程进程通讯)
AIDL(Android 接口定义语言),用于实现IPC通讯定义业务接口的文件,通过该文件会自动生成一个与
接口同名的类:该类的主要是提供接口的存根、网络通讯代理、判断是否是同一个进程通讯等
实现一个IPC示例的步骤:
1、创建AIDL文件,定义相关的业务方法
2、通过AIDL生成的类,实现一个业务方法的具体实现
3、通过Service把业务接口暴露出来,其它程序通过绑定到该服务获取业务对象(代理对象)
4、另一个程序通过绑定服务后,可以直接调用业务方法
bindService() 绑定服务
unBindService() 解除绑定
具体实现:
<1>AIDL文件:类似于接口,但不能使用修饰符
package com.moliying.a33_service;
interface IPerson {
boolean login(String name,String pass);
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
编译后将生成IPerson类
<2>编写一个业务方法实现类:IPersonImpl
public class IPersonImpl extends IPerson.Stub {
@Override
public boolean login(String name, String pass) throws RemoteException {
if("admin".equals(name) && "123".equals(pass)){
return true;
}
return false;
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
<3>定义一个Service类:
public class PersonService extends Service {
private IPersonImpl personImpl;
public PersonService() {
personImpl = new IPersonImpl();
}
@Override
public IBinder onBind(Intent intent) {
return personImpl;
}
@Override
public boolean onUnbind(Intent intent) {
personImpl = null;
return super.onUnbind(intent);
}
}
清单文件中通过使用action使用:
<service
android:name=".PersonService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.moliying.action.PERSON"/>
</intent-filter>
</service>
<4>编写测试:
//参数:intent,绑定成功后的回调接口,标记(绑定并自动创建服务)
bindService(intent,conn, Context.BIND_AUTO_CREATE);
/**
* 绑定服务的回调接口
*/
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//在绑定成功后会被调用
//如果是同一个进程,那么该方法返回业务对象,
//如果不是同一个进程,那么该方法返回代理对象
iPerson = IPerson.Stub.asInterface(service);
Log.i("BoundActivity",iPerson.toString());
isBound = true;
Toast.makeText(BoundActivity.this, "bound success", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
//该方法在正常解绑服务时不会调用,在异常结束时才调用
isBound = false;
}
};
//退出时解除绑定
unbindService(conn);
AIDL文件中支持的类型有:
基本数据类型
String
CharSequence
List<String>
Map<String,Integer>
使用自定义类型:
<1>要求类(Work)实现Parcelable接口(实现进程通讯时传输数据用)
<2>创建Work.aidl 文件声明一个类型:
package com.moliying.service;
parcelable Work;
<3>在使用Work类型的aidl文件中:
import com.moliying.service.Work;
interface IPerson{
Work getWork(); //使用自定义类型
}
服务就是可以在后台一直保持运行
什么事情需要在后台一直运行?下载,定时检查,播放音乐,(started)
如果你要为其它程序提供服务(登录、支付、分享)(bound)
使用Messenger实现IPC:
<1>服务类
public class MessengerService extends Service {
static final int MSG_SAY_HELLO = 1;
//消息处理器
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
final Messenger mMessenger = new Messenger(new IncomingHandler());
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
<2>客户端绑定服务:
public class ActivityMessenger extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;
boolean mBound;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
mBound = false;
}
};
//给服务发送消息
public void sayHello(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
//Activity启动时绑定服务
@Override
protected void onStart() {
super.onStart();
// Bind to the service
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
//Activity停止时解绑服务
@Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
实现Android 实现IPC的两种方式:
1、AIDL(线程不安全,支持并发,实现比较麻烦)
2、Messenger(线程安全(队列),不支持并发,API简洁)
四、广播接收器(BroadcastReceiver)
广播是Android提供的系统级别的应有组件,我们利用广播机制,可以非常方便的处理实时消息
比如:开机、网络状态变化,电量变化,时间变化,短信,等系统都会发出一条广播,如果此时有
对应的广播接收器(BroadcastReceiver),那么会自动触发其onReceive方法
广播类别:
<1>普通广播,无序的,无法终止,所有匹配的接收器都能接收到
<2>有序广播,有序的,优先级高的广播接收器可以终止广播,终止后,优先级低的接收器就接不到了
<3>粘性广播,保留Intent,直到去掉
(1)普通广播
创建 一个广播接收器:
<1>创建 一个类,继承BroadcastReceiver类
public class MyReceiver extends BroadcastReceiver{
public static final String MY_ACTIION = "action.MY_ACTION";
@Override
public void onReceive(Context context, Intent intent) {
}
}
<2>在清单文件中注册:
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="action.MY_ACTION"/>
</intent-filter>
</receiver>
<3>发送广播测试:
context.sendBroadcast(Intent intent);
注册广播接收器的两种方式:
<1>通过清单文件注册(静态注册)
<2>通过代码注册(动态注册)
代码注册:通过在onResume和onPause中注册或解除
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction("action.MY_ACTION");
registerReceiver(myReceiver2,filter);//注册广播接收器
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(myReceiver2);//解除广播注册
}
清单文件注册与代码注册:
<1>优先高的先执行,如果相同,那么代码优先。
<2>代码注册必须在所依赖的界面正常显示时起作用,通常关闭后解除
<3>清单文件注册,在启动后会一直有效,所以相对代码注册范围更大
onReceive方法:会在主线程中执行,该执行不要超过10秒,否则超时异常,不要在该方法中实现弹窗
等操作。一般此方法我们会执行比较简单短小的操作。通常可以配合通知一起使用。
(2)有序广播
发送一个有序广播,广播接收器,将按优先级来接收:优先高的->代码注册->清单文件注册
(-1000~1000)
发送一个有序广播:
//intent,权限(字符串)
sendOrderedBroadcast(intent,null);
//广播接收器:
public class MyReceiver extends BroadcastReceiver{
public static final String MY_ACTIION = "action.MY_ACTION";
@Override
public void onReceive(Context context, Intent intent) {
//为下一个广播传递数据
Bundle data = new Bundle();
data.putString("data","12345");
setResultExtras(data);
Toast.makeText(con text, "MY_ACTIION", Toast.LENGTH_SHORT).show();
//abortBroadcast(); 终止广播(优先级高的终止广播后,优先级低的将无法接收广播)
//拦截
}
}
(3)粘性广播
sendStickyBroadcast(new Intent(MyReceiver.MY_ACTIION));
发送一个粘性广播,需要添加权限:
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
粘性广播会缓存Intent对象,直到对应的接收器启动后接收,比较消耗资源,不建议使用
(4)接收开机启动广播
<1>写一个广播接收器:
public class MyReceiver extends BroadcastReceiver{
public static final String MY_ACTIION = "action.MY_ACTION";
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "开机启动", Toast.LENGTH_SHORT).show();
//启动服务等操作
}
}
<2>在清单文件中注册:
<inter-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<category android:name="android.intent.category.DEFAULT"/>
</inter-filter>
<3>添加权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
(5)接收网络状态变化的广播接收器
<1>定义一个广播接收器:
public class ConnectivityReceiver extends BroadcastReceiver {
public ConnectivityReceiver() {}
@Override
public void onReceive(Context context, Intent intent) {
//获取系统服务(网络连接管理器)
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
//获取当前激活的网络信息
NetworkInfo info = cm.getActiveNetworkInfo();
if(info!=null){
//判断网络类型
switch (info.getType()){
case ConnectivityManager.TYPE_MOBILE:
Toast.makeText(context, "当前网络连接是MOBILE", Toast.LENGTH_SHORT).show();
break;
case ConnectivityManager.TYPE_WIFI:
Toast.makeText(context, "当前网络连接是WIFI", Toast.LENGTH_SHORT).show();
break;
}
}else{
Log.i("ConnectivityReceiver","当前没有网络连接");
}
}
}
<2>在清单文件中注册:
<receiver
android:name=".ConnectivityReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
该action的值:android.net.conn.CONNECTIVITY_CHANGE是在ConnectivityManager类中定义的
<3>添加权限:
访问网络状态:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
(6)接收电量变化的广播
<1>定义一个广播接收器:
public class BatteryReceiver extends BroadcastReceiver {
public BatteryReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
int currLevel=intent.getIntExtra(BatteryManager.EXTRA_LEVEL,0);//当前电量??
int total= intent.getIntExtra(BatteryManager.EXTRA_SCALE,1);//总电量??
int percent=currLevel*100/total;
Toast.makeText(context,"当前电量为:"+percent+"%", Toast.LENGTH_SHORT).show();
}
}
<2>在清单文件中注册:
<receiver
android:name=".BatteryReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BATTERY_CHANGED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
如果要立即获取电量信息,可以:
//获取当前电量
public void getBatteryClick(View v){
//要立即获取电量的,而不是等电量变化的广播,可以使用:
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
Intent intent = registerReceiver(null,filter); //null表示没有接收器
int curr = intent.getIntExtra(BatteryManager.EXTRA_LEVEL,0);
int total = intent.getIntExtra(BatteryManager.EXTRA_SCALE,1);
int percent = curr*100/total;
Toast.makeText(MainActivity.this, "当前电量是:"+percent+"%", Toast.LENGTH_SHORT).show();
}
五、短信
(1)发送短信
SmsManager类:短信管理器对象
通常使用:SmsManager sm = SmsManager.getDefault();
<1>发送短信的代码:
//获取短信服务的管理器
SmsManager smsManager = SmsManager.getDefault();
String content = "查询话费余额";
// smsManager.divideMessage();
PendingIntent sendIntent = PendingIntent.getBroadcast(this,0,new Intent(SEND_ACTION),0);
PendingIntent deliveryIntent = PendingIntent.getBroadcast(this,0,new Intent(DELIVERY_ACTION),0);
//发送短信,参数(电话号码,运营商服务中心的号码null,内容,发送成功回执PI,接收成功回执PI)
smsManager.sendTextMessage("10086",null,content,sendIntent,deliveryIntent);
<2>接收回执的广播接收器
class MySMSReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(SEND_ACTION.equals(action)){
Toast.makeText(context, "短信已送达", Toast.LENGTH_SHORT).show();
}else if(DELIVERY_ACTION.equals(action)){
Toast.makeText(context, "对方已接收", Toast.LENGTH_SHORT).show();
}
}
}
<3>注册广播接收器:
private static final String SEND_ACTION = "send_action";
private static final String DELIVERY_ACTION = "delivery_action";
MySMSReceiver smsReceiver = new MySMSReceiver();
@Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter();
filter.addAction(SEND_ACTION);
filter.addAction(DELIVERY_ACTION);
registerReceiver(smsReceiver,filter);
}
@Override
protected void onStop() {
super.onStop();
unregisterReceiver(smsReceiver);
}
<4>添加发送短信的权限:
<uses-permission android:name="android.permission.SEND_SMS"/>
(2)接收短信
当手机号接到短信后,首先由系统接收,然后系统会发出一条有序广播:
匹配的Action是:
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
添加权限:
<uses-permission android:name="android.permission.RECEIVE_SMS" />
public class SMSReceiver extends BroadcastReceiver {
public SMSReceiver() {}
@Override
public void onReceive(Context context, Intent intent) {
Bundle data = intent.getExtras();
//PDU 是短信的一种协议名称(protocol description unit 协议描述单元)
Object[] pdus = (Object[]) data.get("pdus");//pdus是固定的key值
if(pdus!=null) {
//SmsMessage 短信消息对象
SmsMessage[] smsMessages = new SmsMessage[pdus.length];
String phoneNumber = "";
StringBuilder buf = new StringBuilder(100);
for (int i=0;i<smsMessages.length;i++){
smsMessages[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
smsMessages[i].getDisplayOriginatingAddress();//电话号码
buf.append(smsMessages[i].getDisplayMessageBody());//短信内容
}
Toast.makeText(context, phoneNumber+"-"+buf, Toast.LENGTH_SHORT).show();
}
}
}
六、AlarmManager服务
系统服务(闹钟、定时),可以在指定的某个时间,触发一个操作
获取AlarmManager服务对象通过:
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
am.set()方法来执行一次操作
am.setRepeating方法来重复执行一个操作(需要取消后才停止)
(1)示例:
AlarmManager alarmManager;
PendingIntent operation;
public void startAlarmClick(View view){
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long triggerAlMillis = System.currentTimeMillis()+3000; //3秒以后触发
operation = PendingIntent.getBroadcast(this,0,new Intent(AlarmReceiver.ALARM_ACTION),0);
//只执行一次
//参数(类型,触发时间(毫秒),操作(PendingIntent))
//alarmManager.set(AlarmManager.RTC_WAKEUP,triggerAlMillis,operation);
//重复执行
//参数(类型,触发时间(毫秒),间隔时间,操作(PendingIntent))
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,triggerAlMillis,5000,operation);
}
//停止(取消)闹钟
public void stopAlarmClick(View view){
alarmManager.cancel(operation);
}
public static class AlarmReceiver extends BroadcastReceiver{
public static final String ALARM_ACTION = "alarm_action";
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "get up get up!!!!", Toast.LENGTH_SHORT).show();
}
}
(2)闹钟示例:
<1>通过一个按钮单击触发一次闹钟
public void alarmClick(View v){
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long triggerAlMillis = System.currentTimeMillis()+3000; //3秒以后触发
operation = PendingIntent.getActivity(this,0,new Intent(this,AlarmActivity.class),0);
//只执行一次
//参数(类型,触发时间(毫秒),操作(PendingIntent))
alarmManager.set(AlarmManager.RTC_WAKEUP,triggerAlMillis,operation);
}
<1>创建AlarmActivity,用于在闹钟启动时显示的界面
把该Activity的主题设置为墙纸:
<activity android:name=".AlarmActivity"
android:theme="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
//在onCreate方法中唤醒屏幕,并调用音乐播放器
public class AlarmActivity extends Activity {
private MediaPlayer mp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//唤醒屏幕
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD|
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON|
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
setContentView(R.layout.activity_alarm);
initMediaPlayer();//调用音乐播放器
}
}
初始化音乐播放器,播放音乐:
private void initMediaPlayer(){
mp = new MediaPlayer();
try {
mp.setDataSource("/sdcard/moliying_music/music/霍元甲.mp3");
mp.prepare();//准备,预加载
mp.start();
showDialog();
} catch (IOException e) {
e.printStackTrace();
}
}
再来一次时,需要再次调用闹钟的设置:
public void alarm(){
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long triggerAlMillis = System.currentTimeMillis()+3000; //3秒以后触发
PendingIntent operation = PendingIntent.getActivity(this,0,new Intent(this,AlarmActivity.class),0);
//只执行一次
//参数(类型,触发时间(毫秒),操作(PendingIntent))
alarmManager.set(AlarmManager.RTC_WAKEUP,triggerAlMillis,operation);
}
闹钟响起时,在界面上显示一个弹窗,该方法可以在播放音乐时调用:
private void showDialog(){
//AlertDialog使用android.app.AlertDialog,不用使用V7包下的
//原因是我们已经把当前Activity的主题设置为非V7主题
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("闹钟");
builder.setMessage("闹钟响起");
builder.setPositiveButton("再来一次", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
alarm();
finish();
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
builder.show();
}
退出界面是销毁播放器:
@Override
protected void onDestroy() {
super.onDestroy();
if(mp!=null){
mp.release();//释放
}
}
最后,由于播放器需要读取sdcard文件,所以需要添加访问sdcard的权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
七、Fragments组件
碎片、一部分、在一个Activity中可以由多个Fragment组件,Fragment必须依赖Activity的。
Activity:系统的应用程序组件,占用资源更多更大
Fragment:看作是一个轻量级的Activity,占用的资源更少
在一个Activity界面上Fragment就是其中的一个行为,多个行为共同组合成为一个界面,并且可以相互
替换。
在Activity上添加Fragment的两种方式:
(1)使用布局文件
<1>在Activity的布局文件中:
<fragment
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="com.moliying.fragment.MenuFragment"
android:id="@+id/menu_fragment"/>
<fragment
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="3"
android:name="com.moliying.fragment.ContentFragment"
android:id="@+id/content_fragment"/>
<2>
创建MenuFragment类,继承Fragment
public class MenuFragment extends Fragment{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onPause() {
super.onPause();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.menu_layout,container,false);
return view;
}
}
//menu_layout.xml文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="红色"
android:id="@+id/button4" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="蓝色"
android:id="@+id/button5" />
</LinearLayout>
创建ContentFragment
public class ContentFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onPause() {
super.onPause();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.content_layout,container,false);
return view;
}
}
content_layout布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="not data"
android:textSize="50sp"
android:gravity="center"
android:id="@+id/textView_data"
android:layout_gravity="center_horizontal" />
</LinearLayout>
(2)使用代码
<1>在Activity的布局中定义一个布局容器组件,用于添加Fragment
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.moliying.fragment.Main2Activity">
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:id="@+id/menu_fragment"
android:layout_weight="1"
android:name="com.moliying.fragment.MenuFragment"/>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:id="@+id/content_frameLayout"
android:layout_weight="3"></FrameLayout>
</LinearLayout>
<2>在Activity中创建Fragment对象,并添加到content_frameLayout容器中:
ContentFragment contentFragment = new ContentFragment();
//添加fragment到指定布局
private void add() {
FragmentManager fm = getFragmentManager();
// android.support.v4.app.FragmentManager f = getSupportFragmentManager();
//开始事务
FragmentTransaction ft = fm.beginTransaction();
//添加(容器布局ID,要添加的Fragment)
ft.add(R.id.content_frameLayout,contentFragment);
//fragmentTransaction.replace(R.id.frame_layout,contentFragment);//添加或替换
// fragmentTransaction.remove(contentFragment);//删除
ft.commit();//提交事务
}
(3)在Activity中给Fragment传参
在动态添加一个Fragment到Activity中时,我们可以在创建Fragment对象时为其传参
<1>创建Fragment时通过构造方法传参(会在屏幕变化时数据丢失,不建议使用)
<2>使用静态方法传参,并保存参数
注意:一个Fragment对象必须保留默认构造方法(在系统重新创建时会调用)
一个为Fragment传参的示例:
<1>在Fragment中定义一个静态方法返回一个Fragment实例
//默认无参构造必须保留
//不建议使用带参构造方法(屏幕变化重新创建时会丢失数据)
// public ContentFragment(String data){
// }
public static ContentFragment getInstance(String data){
ContentFragment contentFragment = new ContentFragment();
Bundle args = new Bundle();
args.putString("data",data);
//setArguments 设置 参数
contentFragment.setArguments(args);
return contentFragment;
}
<2>onCreateView中获取参数值实现操作
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.content_layout,container,false);
TextView textView_data = (TextView) view.findViewById(R.id.textView_data);
String data = getArguments().getString("data");
textView_data.setText(data);
return view;
}
(4)处理Fragment返回栈
<1>在添加Fragment时使用:
//把当前Fragment添加到返回栈
fragmentTransaction.addToBackStack(null);
<2>重写键盘按下动作的事件
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//判断用户按下了返回键
if(keyCode==KeyEvent.KEYCODE_BACK){
if(getFragmentManager().getBackStackEntryCount()>0){
getFragmentManager().popBackStack();//出栈
return true;
}
}
return super.onKeyDown(keyCode, event);
}
获取布局文件中定义Fragment组件可以通过以下两个方式:
menuFragment = (MenuFragment) getFragmentManager().findFragmentById(R.id.menu_fragment);
//menuFragment = (MenuFragment) getFragmentManager().findFragmentByTag("tag");
这两个方法都需要在布局文件中定义Fragment时添加id 或 tag 属性
(5)与Activity的交互
<1>宿主:Fragment所依赖的Activity称为Fragment的宿主
<2>Fragment->Activity->Fragment 传值方式
在Fragment类中可以通过getActivity()方法获取其所依赖的Activity对象
这样 我们就可以直接调用Activity中的方法了,但是这种与Activity交互的方式,会导致Fragment
直接依赖一个具体的Activity,在Android设计中,Fragment应该是可以附加到多个Activity上,
以重复使用相同的功能,所以直接依赖一个具体的Activity是一种不好的设计。
那么我们应该怎么办?
让Fragment依赖自己定义的内部接口,该接口需要由宿主来实现,这样的接口我们称回调接口
,所以,哪个Fragment想往外传值,就定义自己的回调接口。
两个Fragment传值示例:
<1>定义两个Fragment,TitleFragment和CityFragment:
public class TitleFragment extends Fragment implements View.OnClickListener{
private Button button_bj,button_sh;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.title_layout,null);
button_bj = (Button) view.findViewById(R.id.button2_bj);
button_sh = (Button) view.findViewById(R.id.button3_sh);
button_bj.setOnClickListener(this);
button_sh.setOnClickListener(this);
return view;
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button2_bj:
//单击事件(需要给另一个Fragment传值)
break;
case R.id.button3_sh:
//单击事件(需要给另一个Fragment传值)
break;
}
}
}
TitleFragment的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:gravity="center_horizontal"
android:background="#f7c1c1"
android:padding="16dp"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="北京"
android:id="@+id/button2_bj" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="上海"
android:id="@+id/button3_sh" />
</LinearLayout>
public class CityFragment extends Fragment {
private TextView textView_city;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.city_layout,null);
textView_city = (TextView) view.findViewById(R.id.textView_city);
return view;
}
}
CityFragment的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="not data"
android:textSize="30sp"
android:gravity="center"
android:id="@+id/textView_city"
android:layout_gravity="center_horizontal" />
</LinearLayout>
<2>在Activity中使用两个Fragment:
public class Main4Activity extends AppCompatActivity{
private TitleFragment titleFragment;
private CityFragment cityFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
titleFragment = (TitleFragment) getFragmentManager().findFragmentById(R.id.title_fragment);
cityFragment = (CityFragment) getFragmentManager().findFragmentById(R.id.city_fragment);
}
}
Main4Activity的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.moliying.fragment.Main4Activity">
<fragment
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/title_fragment"
android:name="com.moliying.fragment.TitleFragment"/>
<fragment
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="5"
android:id="@+id/city_fragment"
android:name="com.moliying.fragment.CityFragment"/>
</LinearLayout>
<3>在TitleFragment类里定义接口,并在按钮的单击事件中调用,该接口由宿主Activity来实现:
public static interface OnSetValueListener{
public void setValue(String value);
}
private OnSetValueListener onSetValueListener;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//必须要求宿主实现
onSetValueListener = (OnSetValueListener) getActivity();
}
public void onClick(View v) {
switch (v.getId()){
case R.id.button2_bj:
onSetValueListener.setValue("北京");
break;
case R.id.button3_sh:
onSetValueListener.setValue("上海");
break;
}
}
<4>宿主Activity实现Fragment的回调接口,并调用其它Fragment的方法把值传过去:
public class Main4Activity extends AppCompatActivity implements TitleFragment.OnSetValueListener{
//....
@Override
public void setValue(String value) {
cityFragment.setCity(value);
}
}
<5>在CityFragment中定义一个setCity方法来接收参数,并设置到TextView组件中
public void setCity(String city){
textView_city.setText(city);
}
(6)PreferenceFragment类的使用
使用PreferenceFragment来实现应用的配置选项(3.0之前使用PreferenceActivity)
PreferenceFragment会自动把选项设置的值保存在应用的shared_prefs下的xml文件中
使用PreferenceFragment实现配置示例:
<1>在res/xml/创建一个xml配置文件:app_options_set.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="通用">
<CheckBoxPreference
android:key="auto_login"
android:title="自动登录"
android:summary="勾选后可自动登录" />
</PreferenceCategory>
<PreferenceCategory
android:title="个人设置">
<EditTextPreference
android:key="nickName"
android:title="昵称"
android:summary="单击设置昵称"
android:dialogTitle="设置昵称" />
<ListPreference
android:key="city"
android:title="选择所在城市"
android:summary="单击选择你所在的城市"
android:entries="@array/city"
android:entryValues="@array/city"
android:dialogTitle="城市列表" />
</PreferenceCategory>
</PreferenceScreen>
列表项在strings.xml文件中定义:
<string-array name="city">
<item>北京市</item>
<item>上海市</item>
<item>广州市</item>
<item>深圳市</item>
<item>保定市</item>
</string-array>
<2>创建 一个Fragment类继承PreferenceFragment
public class OptionSettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//绑定配置文件
addPreferencesFromResource(R.xml.app_option_set);
}
}
<3>在Activity上附加Fragment
(7)ViewPager+Fragment实现应用框架
<1>Activity类的布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.moliying.fragmentviewpagerdemo.MainActivity">
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/viewPager"/>
<RadioGroup
android:gravity="center"
android:id="@+id/tab_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/tab_bg"
android:orientation="horizontal">
<RadioButton
android:gravity="center"
android:button="@null"
android:drawableTop="@drawable/tab_home_selector_bg"
android:textColor="@drawable/tab_selector_bg"
android:layout_weight="1"
android:tag="0"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="主页"/>
<RadioButton
android:gravity="center"
android:button="@null"
android:drawableTop="@drawable/tab_discover_selector_bg"
android:textColor="@drawable/tab_selector_bg"
android:layout_weight="1"
android:tag="1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="发现"/>
<RadioButton
android:gravity="center"
android:button="@null"
android:drawableTop="@drawable/tab_personal_selector_bg"
android:textColor="@drawable/tab_selector_bg"
android:layout_weight="1"
android:tag="2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="我"/>
</RadioGroup>
</LinearLayout>
<2>创建三个Fragment及其布局文件
IndexFragment类:
public class IndexFragment extends Fragment {
public static IndexFragment getInstance(){
IndexFragment indexFragment = new IndexFragment();
return indexFragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.index_fragment_layout,null);
return view;
}
}
IndexFragment对应的布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff5b5b">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="index layout"
android:id="@+id/textView"
android:layout_gravity="center_horizontal"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
DiscoverFragment类
public class DiscoverFragment extends Fragment {
public static DiscoverFragment getInstance(){
DiscoverFragment discoverFragment = new DiscoverFragment();
return discoverFragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.discover_fragment_layout,null);
return view;
}
}
DiscoverFragment布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f109c6">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="discover layout"
android:id="@+id/textView"
android:layout_gravity="center_horizontal"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
PersonalFragment类
public class PersonalFragment extends Fragment {
public static PersonalFragment getInstance(){
PersonalFragment personalFragment = new PersonalFragment();
return personalFragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.personal_fragment_layout,null);
return view;
}
}
PersonalFragment的布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#53ba03">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="personal layout"
android:id="@+id/textView"
android:layout_gravity="center_horizontal"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
<3>在Activity中初始化Fragment
private ArrayList<Fragment> fragments = new ArrayList<>();
fragments.add(IndexFragment.getInstance());
fragments.add(DiscoverFragment.getInstance());
fragments.add(PersonalFragment.getInstance());
<4>初始化ViewPager组件,及适配器
viewPager = (ViewPager) findViewById(R.id.viewPager);
MyViewPagerAdapter myViewPagerAdapter = new MyViewPagerAdapter(getSupportFragmentManager(), fragments);
viewPager.setAdapter(myViewPagerAdapter);
MyViewPagerAdapter类:
static class MyViewPagerAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> fragments;
public MyViewPagerAdapter(FragmentManager fm, ArrayList<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
<5>viewPager的事件处理及RadioGroup的单击事件
viewPager事件处理:在Activity类实现事件接口:ViewPager.OnPageChangeListener
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageSelected(int position) {
radioButtons.get(position).setChecked(true);
}
@Override
public void onPageScrollStateChanged(int state) {}
RadioGroup的单击事件:
在Activity类实现事件接口:RadioGroup.OnCheckedChangeListener
初始化时调用:
private ArrayList<RadioButton> radioButtons = new ArrayList<>();
tab_group.setOnCheckedChangeListener(this);
for (int i = 0; i < tab_group.getChildCount(); i++) {
radioButtons.add((RadioButton) tab_group.getChildAt(i));
}
事件方法:
public void onCheckedChanged(RadioGroup group, int checkedId) {
RadioButton rb = (RadioButton) group.findViewById(checkedId);
int index = Integer.parseInt(rb.getTag().toString());
viewPager.setCurrentItem(index);
}
八、ActionBar(标题导航操作栏)
ActionBar从Android3.0开始引入,目的为APP提供一个统一的操作栏,通过显示在上方部分
要求最小版本在11以上
(1)显示和隐藏ActionBar
//V7
ActionBar actionBar = getSupportActionBar();
//api 11
//android.app.ActionBar actionBar = getActionBar();
if(actionBar==null)return;
if(flag){
button_show_hide.setText("隐藏ActionBar");
actionBar.show();
flag = false;
}else{
button_show_hide.setText("显示ActionBar");
actionBar.hide();
flag = true;
}
(2)分裂操作栏
通过设置Activity的 android:uiOptions="splitActionBarWhenNarrow"
修改Activity的主题与父类
(3)添加操作项
添加一个res/menu/actionbar_item.xml
<item android:id="@+id/save"
android:title="保存"
android:orderInCategory="100"
android:icon="@android:drawable/ic_menu_save"
app:showAsAction="always"/>
添加菜单项:
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.actionbar_menu,menu);
return super.onCreateOptionsMenu(menu);
}
菜单项事件处理:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.save:
Toast.makeText(MainActivity.this, "save", Toast.LENGTH_SHORT).show();
break;
case R.id.share:
Toast.makeText(MainActivity.this, "share", Toast.LENGTH_SHORT).show();
break;
case R.id.add:
Toast.makeText(MainActivity.this, "add", Toast.LENGTH_SHORT).show();
break;
}
(4)启用Activity的图标导航
ActionBar actionBar = getSupportActionBar();
if(actionBar!=null){
actionBar.setDisplayShowHomeEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case android.R.id.home:
//监听图标导航事件
Toast.makeText(MainActivity.this, "home", Toast.LENGTH_SHORT).show();
break;
}
}
或者启用图标导航另一个方式是在Activity的配置文件中使用:
android:parentActivityName=".MainActivity"
导航到指定的界面。
(5)添加操作视窗(搜索框)
<1>添加一个选项菜单项:
<item android:id="@+id/search1"
android:title="搜索"
android:icon="@android:drawable/ic_menu_search"
android:orderInCategory="100"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView"/>
<2>加载菜单项
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.search_menu,menu);
MenuItem item = menu.findItem(R.id.search1);
//获取搜索组件,并添加事件处理
SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
//设置查询文本内容的监听器
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
Toast.makeText(Main2Activity.this, query, Toast.LENGTH_SHORT).show();
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
return super.onCreateOptionsMenu(menu);
}
实现搜索功能的另一个解决方案是:
自定义一个搜索按钮,点击后打开一个新的界面,在新的界面上实现一个搜索框及搜索功能,
这个搜索功能就不影响当前界面上显示的内容,搜索完毕后,用户可以导航到原来的界面。
(6)添加一个操作提供器(分享)
<1>添加分享菜单项:
<item android:id="@+id/share"
android:title="分享"
android:icon="@android:drawable/ic_menu_share"
android:orderInCategory="200"
app:showAsAction="ifRoom"
app:actionProviderClass="android.support.v7.widget.ShareActionProvider"/>
<2>在创建菜单的事件方法中添加分享事件:
//----------分享菜单项------------
public boolean onCreateOptionsMenu(Menu menu) {
MenuItem menuItem = menu.findItem(R.id.share);
ShareActionProvider shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menuItem);
shareActionProvider.setShareIntent(getIntent());
}
public Intent getIntent(){
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setData(Uri.parse("/sdcard/xxxxx"));
intent.setType("image/*");
return intent;
}
分享实现方案:
<1>ShareSDK(第三方组件)
(7)导航标签(了解一下)
file:///D:/moliying/docs/guide/topics/ui/actionbar.html
(8)ToolBar
5.0版本提供(API 21)或使用v7包的支持
可以灵话定制菜单栏,全浸式Style
<1>设置style配置文件:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@color/windowBackground</item>
<!--API 21 才可用-->
<!--<item name="android:navigationBarColor"></item>-->
</style>
<2>布局文件使用:
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:id="@+id/toolBar">
</android.support.v7.widget.Toolbar>
<3>Activity中设置
private Toolbar toolbar;
toolbar = (Toolbar) findViewById(R.id.toolBar);
toolbar.setLogo(R.mipmap.ic_menu_home);
toolbar.setTitle("微信");
toolbar.setTitleTextColor(Color.WHITE);
toolbar.setNavigationIcon(R.mipmap.ic_menu_back);
// setActionBar(toolbar);
setSupportActionBar(toolbar);
菜单项的设置与选项菜单一致
九、TelephonyManager
//电话服务管理器的API方法
private void testTelephonyManager() {
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
System.out.println("电话状态 = " + tm.getCallState());
System.out.println("唯一的设备ID = " + tm.getDeviceId());
System.out.println("设备的软件版本号 = " + tm.getDeviceSoftwareVersion());
System.out.println("手机号 = " + tm.getLine1Number());
System.out.println("获取ISO标准的国家码,即国际长途区号 = " + tm.getNetworkCountryIso());
System.out.println("当前使用的网络类型 = " + tm.getNetworkType());
System.out.println("手机类型 = " + tm.getPhoneType());
System.out.println("SIM的状态信息 = " + tm.getSimState());
System.out.println("唯一的用户ID = " + tm.getSubscriberId());
System.out.println("SIM卡的序列号 = " + tm.getSimSerialNumber());
System.out.println("服务商名称 = " + tm.getSimOperatorName());
// tm.listen(new MyPhoneStateListener(),PhoneStateListener.LISTEN_CALL_STATE);
//测试来电显示:直接发送一个广播
sendBroadcast(new Intent(this,PhoneListenerReceiver.class));
}
/**
* 电话服务的监听器
*/
private static class MyPhoneStateListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
switch (state){
case TelephonyManager.CALL_STATE_RINGING:
System.out.println("正在响铃。。。。");
break;
case TelephonyManager.CALL_STATE_IDLE:
System.out.println("挂机状态");
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
System.out.println("正在接听电话...");
break;
}
}
}
监听来电显示:
<1>开机启动广播接收器:
/**
* 监听来电显示的广播接收器
*/
public class PhoneListenerReceiver extends BroadcastReceiver {
public PhoneListenerReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
//获取电话管理器对象,并注册监听器
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(new MyPhoneStateListener(context), PhoneStateListener.LISTEN_CALL_STATE);
System.out.println("PhoneListenerReceiver started");
}
static WindowManager wm = null;
private class MyPhoneStateListener extends PhoneStateListener{
private Context context;
TextView textView = null;
public MyPhoneStateListener(Context context){
this.context = context;
}
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
//响铃状态
if(state==TelephonyManager.CALL_STATE_RINGING){
//获取窗体管理器
wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
//设置为一个浮动层
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
//设置为不能触模和没有焦点
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
textView = new TextView(context);
textView.setText("当前来电号码为:"+incomingNumber);
// textView.setBackgroundColor(Color.BLACK);
wm.addView(textView,params);//添加浮动视图
//挂机状态
}else if(state== TelephonyManager.CALL_STATE_IDLE){
if(wm!=null){
wm.removeView(textView);
wm = null;
}
}
}
}
}
十、APP内容共享
(1)共享文本内容
public void shareTextClick(View view){
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT,"亲,忙什么呢,哥正在学Android呢");
intent.setType("text/plain");
startActivity(Intent.createChooser(intent,"发送文本内容"));
}
(2)共享二进制内容(比如图片)
public void shareImageClick(View view){
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
//Environment 环境对象
//Environment.getExternalStoragePublicDirectory(
//Environment.DIRECTORY_PICTURES) //获取sdcard上的对应的Pictures目录
String path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)+"/1.png";
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(path)));
intent.setType("image/png");
startActivity(intent);
// startActivity(Intent.createChooser(intent,"发送图片"));
}
(3)共享多个文件(图片)
public void shareImagesClick(View view){
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
ArrayList<Uri> uris = new ArrayList<>();
String path1 = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)+"/1.png";
String path2 = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)+"/2.png";
uris.add(Uri.fromFile(new File(path1)));
uris.add(Uri.fromFile(new File(path2)));
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM,uris);
intent.setType("image/*");
startActivity(intent);
}
(4)接收共享数据:
自定义一个接收共享数据的Activity:
<1>在清单文件中添加InterFilter配置,用于接收不同的数据类型:
<activity android:name=".ReceiverDataActivity">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
<2>Activity类中处理接收到的数据:
public class ReceiverDataActivity extends AppCompatActivity {
private LinearLayout content_layout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_receiver_data);
content_layout = ((LinearLayout) findViewById(R.id.content_layout));
receiver();
}
private void receiver() {
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
//判断是否为发送一个数据
if(Intent.ACTION_SEND.equals(action) && type!=null){
//数据是否为文本类型
if("text/plain".equals(type)){
handlerSendText(intent);
//数据是否为图片类型
}else if(type.startsWith("image/")){
handlerSendImage(intent);
}
//判断数据是否为多个并为图片类型
}else if(Intent.ACTION_SEND_MULTIPLE.equals(action) && type!=null){
if(type.startsWith("image/")){
handlerSendMultipleImage(intent);
}
}
}
/**
* 处理多个图片内容
* @param intent
*/
private void handlerSendMultipleImage(Intent intent) {
ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
if (imageUris != null) {
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
for (int i=0;i<imageUris.size();i++){
ImageView iv = new ImageView(this);
iv.setMaxHeight(400);
iv.setMaxWidth(400);
iv.setAdjustViewBounds(true);
iv.setImageURI(imageUris.get(i));
content_layout.addView(iv,params);
}
}
}
/**
* 处理图片内容
* @param intent
*/
private void handlerSendImage(Intent intent) {
Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
if (imageUri != null) {
ImageView iv = new ImageView(this);
iv.setMaxHeight(400);
iv.setMaxWidth(400);
iv.setAdjustViewBounds(true);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
iv.setImageURI(imageUri);
content_layout.addView(iv,params);
}
}
/**
* 处理文本内容
* @param intent
*/
private void handlerSendText(Intent intent) {
String data = intent.getStringExtra(Intent.EXTRA_TEXT);
TextView textView_data = new TextView(this);
textView_data.setText(data);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
content_layout.addView(textView_data,params);
}
}