安卓基础第七天(四大组件之BroadcastReceiver,样式主题,国际化,对话框)

BroadcastReceiver简介

Broadcast 是一种广泛运用的在应用程序之间传输信息的机制
BroadcastReceiver 是对发送出来的Broadcast 进行过滤接受并响应的一类组件

简单创建BroadcastReceiver

  • 接收SD卡移除时发送的广播

1.新建一个SDCardUnmountedReceiver 类继承BroadcastReceiver 类当接收到匹配广播之后就会执行onReceive 方法

    public class SDCardUnmountedReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "SDCard 已经被移除!", 0).show();
        }
    }

2.注册SDCardUnmountedReceiver

注册一个广播接收者有两种方式

静态注册:在AndroidManifest.xml 中注册广播
清单文件中声明,需要在其中配置指定接收广播的动作(action),在AndroidManifest.xml 文件中添加如下配置:

        <receiver android:name="com.itheima.broadcastReciver.SDCardUnmountedReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_UNMOUNTED" />
                <data android:scheme="file" >
                </data>
            </intent-filter>
        </receiver>

动态注册:在Java 代码中注册


    public class MainActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            IntentFilter intentFilter = new IntentFilter(
                    "android.intent.action.MEDIA_UNMOUNTED");
            intentFilter.addDataScheme("file");
            registerReceiver(new SDCardUnmountedReceiver(), intentFilter);
            System.out.println("广播接收器已经注册成功。");
        }
    }
  • 两种注册的区别
    代码注册:它不是常驻型广播,也就是说广播跟随程序的生命周期,一旦代码所在进程被杀死,广播接收者就失效

清单文件注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。一旦应用程序被部署到手机,广播接收者就会生效,高版本需要启动过一次才能接收到广播

PS:3.2 版本以上出于安全问题:从未启动过的广播接收程序,默认是接收不到广播的,必须有一个界面,然后通过界面启动一次这个程序。

生命周期

  • 广播接收者也是运行在主线程中,所以在广播接收者的onReceive 方法内不能有耗时的操作,需要放在子线程中做

  • 但是onReceive 的生命周期很短,有可能广播接收者结束,子线程还没有结束,这时广播接收者所在进程很有可能被杀掉,这样子线程就会出问题

  • 所以耗时操作最好放到service 服务中。

  • 广播接收者的生命周期是非常短暂的,在接收到广播的时候创建,onReceive()方法结束之后销毁

Android中常见广播

短信监听

需要的权限
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
清单配置 PS:android 4.2 后废除了此action

        <receiver android:name="com.itheima.smslistener.SmsListenerReceiver">

            <intent-filter >

                <action android:name="android.provider.Telephony.SMS_RECEIVED" />


            </intent-filter>

        </receiver>

代码

public class SmsListenerReceiver extends BroadcastReceiver {
    //当短信到来的时候执行 
    @Override
    public void onReceive(Context context, Intent intent) {
        //获取发送者的号码 和发送内容 
        Object []objects = (Object[]) intent.getExtras().get("pdus");
        for (Object obj : objects) {
            //[1]获取smsmessage实例 
            SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) obj);
            //[2]获取发送短信的内容 
            String messageBody = smsMessage.getMessageBody();
            String address = smsMessage.getOriginatingAddress();
            System.out.println("body:"+messageBody+"-----"+address);            
        }
    }
}

监听系统开机

拦截的广播
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
需要的权限
PS:3.2 以上版本必须加权限,,3.2 以上的版本如果用户没有启动过程序, 接收不到开启启动完成的广播
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

代码

java.lang.RuntimeException: Unable to start receiver com.itheima.bootreceiver.BootReceiver: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

public class BootReceiver extends BroadcastReceiver {

    //当手机重新启动的时候调用 
    @Override
    public void onReceive(Context context, Intent intent) {
        //在这个方法里面开启activity
        Intent intent2 = new Intent(context,MainActivity.class);
        //☆☆☆注意 不能在广播接收者里面直接开启activity  需要添加一个标记 添加一个任务栈的标记 
        intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        
        //开启activity
        context.startActivity(intent2);     
    }
}

监听卸载安装

拦截的广播
<action android:name="android.intent.action.PACKAGE_ADDED"></action>
<action android:name="android.intent.action.PACKAGE_REMOVED"></action>
需要指定scheme: package
<data android:scheme="package"/>
代码

public class AppStateReceiver extends BroadcastReceiver {

    //当有新的应用被安装  了  或者有应用被卸载 了  这个方法调用 
    @Override
    public void onReceive(Context context, Intent intent) {

        //获取当前广播事件类型 
        String action = intent.getAction();

        //PACKAGE_INSTALL为预留无效action 
        if ("android.intent.action.PACKAGE_INSTALL".equals(action)) {
            System.out.println("应用安装了11111");
        }else if ("android.intent.action.PACKAGE_ADDED".equals(action)) {
            System.out.println("应用安装了22222");
        }else if("android.intent.action.PACKAGE_REMOVED".equals(action)){
            System.out.println("应用卸载了"+intent.getData());
        }

    }

}

监听打电话

拦截的广播:
<action android:name="android.intent.action.NEW_OUTGOING_CALL"></action>
需要的权限:
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>

特殊广播的接收者

比如操作特别频繁的广播事件 ,屏幕的锁屏和解锁, 电池电量的变化
这样的广播接收者在清单文件里面注册无效
报错
Activity com.itheima.screen.MainActivity has leaked IntentReceiver com.itheima.screen.ScreenReceiver@b6486088 that was originally registered here. Are you missing a call to
unregisterReceiver()?

发送自定义广播

无序广播

  • 符号某广播条件的所有广播接收者的特点,无序,无优先级,不可中断,接收者不可传递数据
    BroadcastReceiver trying to return result during a non-ordered broadcast
  • 无序广播使用sendBroadcast 方法来发送
  • 广播时可设置接收者权限,仅当接收者含有权限才能接收
  • 接收者的也可设置发送方权限,只接收含有权限应用的广播
    Receiver 节点增加属性permission
  • 发送广播时,通过intent.setFlags(intent.flag_include_stopped_pakeages),包含从
    未启动过的程序,这样设置,可以让从未启动的接收者也收到广播
    //点击按钮 发送一条无序广播 
    public void click(View v) {
        Intent intent = new  Intent();
        intent.setAction("com.itheima.custom");
        intent.putExtra("name", "新闻联播每天晚上 7点准时开整!!");
        sendBroadcast(intent);//发送无序广播      
    }

有序广播

  • 有序广播使用sendOrderedBroadcast 方法来发送
  • 接收者可以在中定义android:priority 定义优先级,数字越大优先级越高,官方定义范围是:
    1000(最高) ~ -1000(最低),系统默认优先级是0
  • 有序广播可以被拦截或添加数据,优先级高的接收者可以拦截优先级低的
    使用abortBroadcast 方法拦截,setResultExtras(Bundle)传递数据

代码
发送有序广播
sendOrderedBroadcast(intent, null, new FinalReceiver(), null, 1, "习大大给每个村民发了1000斤大米", null);
接收者

public class ProvienceReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //[1]获取发送广播携带的数据
        String content= getResultData();
        //[2]显示结果
        Toast.makeText(context, "省:"+content, 1).show();
        //[2.1]终止广播 
        abortBroadcast();

        //[3]修改数据 
        setResultData("习大大给每个村民发了500斤大");
    }
}

最终接收者,被拦截的广播依然可以接收到,无需注册

public class FinalReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        //[1]获取发送广播携带的数据
        String content= getResultData();

        //[2]显示结果
        Toast.makeText(context, "报告习大大:"+content, 1).show();
    }
}

样式主题

样式

  • Android Style 类似网页设计中的级联样式CSS 设计思路,可以让设计与内容分离,并且可以方便的继承、覆盖、重用
  • 同CSS 一样,样式的引入遵循就近原则,在控件上定义的属性会覆盖被引入的样式中的同一个属性

在工程中res->values->styles.xml 文件定义样式

    <style name="btn_style">
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_width">wrap_content</item>
        <item name="android:textSize">20sp</item>
        <item name="android:textColor">#ff0000</item>
        <item name="android:text">自定义样式</item>
    </style>
    <!-- 继承上一个样式,相同的属性则覆盖父类 -->
    <style name="btn_style_child" parent="btn_style">
        <item name="android:textColor">#0000ff</item>
    </style>

使用个style=”@style/btn_style”属性添加样式

主题

  • 主题的定义与样式的定义相同,都是定义在styles.xml 文件下
  • 不同之处在于主题是作用在Activity 上的
  • 主题通过定义AndroidManifest.xml 文件中的和节点下的”android:theme”属性作用在整个应用或者某个Activity
  • 当主题和样式属性发生冲突时,样式的优先级高于主题
    透明主题:@android:style/Theme.Translucent
    reference–>android–>R.style 中查看文档

国际化

Internationalization太长也简称为I18N
Android 可以通过资源文件非常方便的实现程序的国际化。
res/目录下
1.创建名称为values-zh-rCN、values-zh-rTW 和values-en-rUS的文件夹
2.然后在每个文件夹中创建一个对应的strings.xml 文件,并在该文件中定义对应语言的字符串即可
3.图片也可以进行国际化,创建名称类似drawable-zh-rCN,drawable-zh-rTW 这样的文件夹,将图片资源存放在文件夹下即可

帧动画

Drawable Animation(Frame Animation):帧动画,就像GIF 图片,通过一系列Drawable 依次显示来模拟动画的效果。

Android 中的常见对话框

Android 中常用的对话框有:
通知对话框、列表对话框、单选对话框、多选对话框以及进度对话框。
通知对话框、列表对话框、单选以及多选对话框由AlertDialog.Builder创建
进度对话框由ProgressDialog 创建。
代码

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    //点击按钮 弹出一个普通对话框 
    public void click1(View v){

        //通过builder 构建器来构造 
        AlertDialog.Builder  builder = new Builder(this);
        builder.setTitle("警告");
        builder.setMessage("世界上最遥远的距离是没有网络 ");
        builder.setPositiveButton("确定", new OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                System.out.println("点击了确定按钮");
            }
        });
        builder.setNegativeButton("取消", new OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                System.out.println("点击了取消按钮 ");
            }
        });

        //最后一步 一定要记得  和Toast 一样  show出来 
        builder.show();

    }

    //点击按钮 弹出一个单选对话框 
        public void click2(View v){

            //通过builder 构建器来构造 
            AlertDialog.Builder  builder = new Builder(this);
            builder.setTitle("请选择您喜欢的课程");

            final String items[] = {"Android","ios","c","C++","html","C#"};

            //-1代表没有条目被选中
            builder.setSingleChoiceItems(items, -1, new OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {

                    //[1]把选择的条目给取出来   
                    String item = items[which];

                    Toast.makeText(getApplicationContext(), item, 1).show();

                    //[2]把对话框关闭
                    dialog.dismiss();                   
                }
            });

            //最后一步 一定要记得  和Toast 一样  show出来 
            builder.show();

        }

        //点击按钮 弹出一个对选对话框 
                public void click3(View v){

                    //通过builder 构建器来构造 
                    AlertDialog.Builder  builder = new Builder(this);
                    builder.setTitle("请选择您喜欢吃的水果");

                    final String items[] = {"香蕉","黄瓜","哈密瓜","西瓜","梨","柚子","榴莲"};
                    final boolean[] checkedItems={true,false,false,false,false,false,true}; 
                    builder.setMultiChoiceItems(items, checkedItems, new OnMultiChoiceClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which, boolean isChecked) {

                        }
                    });

                    builder.setPositiveButton("确定", new OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {

                            //把选中的 条目的数据给我取出来  

                            StringBuffer sb = new StringBuffer();
                            for (int i = 0; i < checkedItems.length; i++) {

                                //判断一下 选中的 
                                if (checkedItems[i]) {

                                    String fruit = items[i];
                                    sb.append(fruit + "  ");
                                }

                            }
                            Toast.makeText(getApplicationContext(), sb.toString(), 1).show();
                            //关闭对话框
                            dialog.dismiss();

                        }
                    });

                    //最后一步 一定要记得  和Toast 一样  show出来 
                    builder.show();
                }

        //点击按钮 弹出一个进度条对话框       
        public void click4(View v){
            final ProgressDialog dialog = new ProgressDialog(this);
            dialog.setTitle("正在玩命加载ing");
            //设置一下进度条的样式 
            dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            //最后一步一定要记得show 出来 
            dialog.show();
            //创建一个子线程  
            new Thread(){public void run() {

                //设置进度条最大值 
                dialog.setMax(100);

                //设置当前进度 
                for (int i = 0; i <=100 ; i++) {

                    dialog.setProgress(i);
                    //睡眠一会
                    SystemClock.sleep(50);

                }
                //关闭对话框 
                dialog.dismiss();

            };}.start();
        }
}

activity.this与getApplicationContext()区别

  • 一般情况:可以用activity.this.代替getApplicationContext()

  • activity,弹出对话框时,生命周期方法:onpause()、onResume()方法不会执行因为对话框是activity 的一部分。

所以:构造对话框的Builder(context),这个context 是指定这个对话框挂载在哪个activity 上只能是activity.this获取的是子类的activity,当前activity context

不能是getApplicationContext(),整个应用程序的上下文

因为getApplicationContext 获取的是父类的activity而子类有的东西,父类不一定有。

猜你喜欢

转载自blog.csdn.net/opopopwqwqwq/article/details/79343582
今日推荐