Android核心组件笔记 待整理

核心组件笔记

一、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);
    }
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值