天天看点

android widget简单开发二之点击事件

这一篇主要是记录,在initialLayout(加载到桌面对应的布局文件),如果布局文件中有按钮,想要实现点击事件,这个时候不再是平常一样了。以下记录单个按钮的点击事件,多个按钮的点击事件,以及点击按钮如何实现跳转客户端。

一、首先,要先简单了解一下两部分知识

(一)关于PendingIntent

1.那么PendingIntent是什么?我的理解是可以让外部程序执行当前程序的意图。

因为PendingIntent持有当前app的context引用,所以,它与Intent的区别有,它可以在外部执行PendingIntent里面的Intent。

Intent是立即执行,PendingIntent不是立刻执行的。

2.如何使用PendingIntent?

可以通过PendingIntent.getActivity(Context context,int RequestCode,Intent intent,int flags),或者getBroadcast()、getService()去获取实例。更多详细的参考以下文章

 《PendingIntent详解》

《PendingIntent详解 》

《Android开发陷阱:利用PendingIntent传递唯一的Intent》

(二)关于RemoteView

RemoteView描述一个view,而这个view是在另外一个进程显示的。它inflate于layout资源文件。并且提供了可以修改过view内容的一些简单基础的操作.

《Android widget 之RemoteView》

二、单个按钮的点击事件

1.在initialLayout中添加一个按钮以及一个ImageView图片,通过点击widget按钮实现更换ImageView的图片,布局文件如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:src="@drawable/preview"/>
    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="改变图片"
        />
</LinearLayout>
           

2.在AppWidget中,先在onUpdate方法中去创建RemoteView以及绑定Id。代码如下

@Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {  RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_layout);
        //分别绑定id
        remoteViews.setOnClickPendingIntent(R.id.btn,getPendingIntent(context,R.id.btn));
        //更新widget
        appWidgetManager.updateAppWidget(appWidgetIds,remoteViews);
}

 private PendingIntent getPendingIntent(Context context, int resID){
        Intent intent = new Intent();
        intent.setClass(context, AppWidget.class);//如果没有这一句,表示匿名的。加上表示是显式的。在单个按钮的时候是没啥区别的,但是多个的时候就有问题了
        intent.setAction("btn.text.com");
        //设置data域的时候,把控件id一起设置进去,
        // 因为在绑定的时候,是将同一个id绑定在一起的,所以哪个控件点击,发送的intent中data中的id就是哪个控件的id
        intent.setData(Uri.parse("id:" + resID));

        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,intent,0);
        return pendingIntent;
    }
           

3.通过上面的操作发送了广播,那么在AppWidget的onReceive方法中就可以去做接收部分的操作了

@Override
    public void onReceive(Context context, Intent intent) {
     if (intent.getAction().equals("btn.text.com")){
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
            Uri data = intent.getData();
            int resId = -1;
            if (data!=null){
                resId = Integer.parseInt(data.getSchemeSpecificPart());
            }
 	 switch (resId){
                case R.id.btn:
                    remoteViews.setImageViewResource(R.id.img, R.drawable.logo);
                    break;
		}
  	 //获得appwidget管理实例,用于管理appwidget以便进行更新操作
         AppWidgetManager manger = AppWidgetManager.getInstance(context);
         // 相当于获得所有本程序创建的appwidget
         ComponentName thisName = new ComponentName(context,AppWidget.class);
         //更新widget
         manger.updateAppWidget(thisName,remoteViews);
}
           

三、多个按钮的点击事件

基本步骤大致相同,关键是在onUpdate方法中,如果说Intent 没有显示调用的话

@Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {  RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_layout);
        //分别绑定id
        remoteViews.setOnClickPendingIntent(R.id.btn,getPendingIntent(context,R.id.btn));//第一个按钮
        remoteViews.setOnClickPendingIntent(R.id.btn2,getPendingIntent(context,R.id.btn2));//第二个按钮

        //更新widget
        appWidgetManager.updateAppWidget(appWidgetIds,remoteViews);
}

 private PendingIntent getPendingIntent(Context context, int resID){
        Intent intent = new Intent();
        //intent.setClass(context, AppWidget.class);//此时这句代码去掉
        intent.setAction("btn.text.com");
        //设置data域的时候,把控件id一起设置进去,
        // 因为在绑定的时候,是将同一个id绑定在一起的,所以哪个控件点击,发送的intent中data中的id就是哪个控件的id
        intent.setData(Uri.parse("id:" + resID));

        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,intent,0);
        return pendingIntent;
    }
           

会发现点击第一个按钮的时候会发生变化,但是点击第二个按钮的时候,接收不到消息。详情参见《Android开发陷阱:利用PendingIntent传递唯一的Intent》、PendingIntent传递值分析。

对了,这边还有个问题需要注意的是,设置data域的时候,即Intent.setData(Uri.parse("id:"+ID)),如果少了“:”冒号的话,在onReceive方法中去解析对应的控件id的时候 Integer.parseInt(data.getSchemeSpecificPart()),会报java.lang.NumberFormatException异常;

四、关于如何点击widget上的按钮跳转到主程序

前面的步骤一致,区别在onReceive方法中的操作

@Override
    public void onReceive(Context context, Intent intent) {
        // 接收到任意广播时触发,在其他方法之前被调用。
        if (intent.getAction().equals("btn.text.com")){
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
            Uri data = intent.getData();
            int resId = -1;
            if (data!=null){
                resId = Integer.parseInt(data.getSchemeSpecificPart());
            }  
switch (resId){
      case R.id.btn3:
                    String flag = "i am comming!";//可以传递一些数据到主客户端
                    Intent startAcIntent = new Intent();
                    startAcIntent.setComponent(new ComponentName("admin.example.com.myapplication","admin.example.com.myapplication.MainActivity"));//第一个是包名,第二个是类所在位置的全称
                    startAcIntent.putExtra("flag",flag);
                    startAcIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    context.startActivity(startAcIntent);
                    break;
            }
   	    //获得appwidget管理实例,用于管理appwidget以便进行更新操作
            AppWidgetManager manger = AppWidgetManager.getInstance(context);
            // 相当于获得所有本程序创建的appwidget
            ComponentName thisName = new ComponentName(context,AppWidget.class);
           //更新
            manger.updateAppWidget(thisName,remoteViews);

        }
     Log.i(TAG, "onReceive: 执行");
        super.onReceive(context, intent);

    }