理解RemoteViews

一、RemoteViews的应用

RemoteViews在实际开发中,主要用在通知栏和桌面小部件的开发过程中。通知栏主要是通过NotificationManager的notify方法来实现的,图除了默认效果外,还可以另外定义布局。桌面小部件是通过AppWidgetProvider来实现的,AppWidgetProvider本质上是一个广播。通知栏和桌面小部件的开发过程中都会用到RemoteViews,他们在更新界面时无法像在activity中那样直接去更新view,这是因为二者的界面都运行在其他进程中,确切来说是在系统的SyestemServer进程。为了跨进程更新界面,RemoteViews提供了一系列的set方法并且这些方法只是view全部方法的子集,另外RemoteViews支持的view类型是有限的。

1.RemoteViews在通知栏中的应用

 Notification notification = new Notification();
            notification.icon = R.mipmap.ic_launcher_round;
            notification.tickerText = "hello world";
            notification.when = System.currentTimeMillis();
            notification.flags = Notification.FLAG_AUTO_CANCEL;
            Intent intent = new Intent(this, LoginActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(this,
                    0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            System.out.println(pendingIntent);
            RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.remote);
            remoteViews.setTextViewText(R.id.msg, "chapter_5: ");
            remoteViews.setImageViewResource(R.id.icon, R.mipmap.ic_launcher);
            PendingIntent openActivity2PendingIntent = PendingIntent.getActivity(this,
                    0, new Intent(this, LoginActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setOnClickPendingIntent(R.id.msg, openActivity2PendingIntent);
            notification.contentView = remoteViews;
            notification.contentIntent = pendingIntent;
            NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
            manager.notify(1, notification);

RemoteViews的使用很简单,重要提供相应的包名和布局文件的id即可以创建一个RemoteViews对象,但是如何更新RemoteViews呢?这一点和更新view有很大不同,更新RemoteViews时,无法直接访问里面的view,必须通过RemoteViews提供的一系列方法来更新view:

  • remoteViews.setTextViewText(R.id.msg, “chapter_5: “);//更新文字
  • remoteViews.setImageViewResource(R.id.icon, R.mipmap.ic_launcher);//更新图片
  • remoteViews.setOnClickPendingIntent(R.id.msg, openActivity2PendingIntent);设置点击事件。

2.RemoteViews在桌面小部件上的应用
桌面小控件的开发步骤:
1、定义小部件界面:
在res/layout下新建XML文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/icon1" />

</LinearLayout>

2.定义小部件配置信息
在res/xml下创建xml文件

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/widget"//初始化的布局
    android:minHeight="84dp"
    android:minWidth="84dp"
    android:updatePeriodMillis="86400000" >//自动更新周期,毫秒为单位

</appwidget-provider>

3.定义小部件的实现类

public class MyAppWidgetProvider extends AppWidgetProvider {
    public static final String TAG = "MyAppWidgetProvider";
    public static final String CLICK_ACTION = "com.ryg.chapter_5.action.CLICK";

    public MyAppWidgetProvider() {
        super();
    }

    @Override
    public void onReceive(final Context context, Intent intent) {
        super.onReceive(context, intent);
        Log.i(TAG, "onReceive : action = " + intent.getAction());

        // 这里判断是自己的action,做自己的事情,比如小工具被点击了要干啥,这里是做一个动画效果
        if (intent.getAction().equals(CLICK_ACTION)) {
            Toast.makeText(context, "clicked it", Toast.LENGTH_SHORT).show();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    Bitmap srcbBitmap = BitmapFactory.decodeResource(
                            context.getResources(), R.drawable.icon1);
                    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
                    for (int i = 0; i < 37; i++) {
                        float degree = (i * 10) % 360;
                        RemoteViews remoteViews = new RemoteViews(context
                                .getPackageName(), R.layout.widget);
                        remoteViews.setImageViewBitmap(R.id.imageView1,
                                rotateBitmap(context, srcbBitmap, degree));
                        Intent intentClick = new Intent();
                        intentClick.setAction(CLICK_ACTION);
                        PendingIntent pendingIntent = PendingIntent
                                .getBroadcast(context, 0, intentClick, 0);
                        remoteViews.setOnClickPendingIntent(R.id.imageView1, pendingIntent);
                        appWidgetManager.updateAppWidget(new ComponentName(
                                context, MyAppWidgetProvider.class),remoteViews);
                        SystemClock.sleep(30);
                    }

                }
            }).start();
        }
    }

    /**
     * 每次窗口小部件被点击更新都调用一次该方法
     */
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
                         int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        Log.i(TAG, "onUpdate");

        final int counter = appWidgetIds.length;
        Log.i(TAG, "counter = " + counter);
        for (int i = 0; i < counter; i++) {
            int appWidgetId = appWidgetIds[i];
            onWidgetUpdate(context, appWidgetManager, appWidgetId);
        }

    }

    /**
     * 窗口小部件更新
     *
     * @param context
     * @param appWidgeManger
     * @param appWidgetId
     */
    private void onWidgetUpdate(Context context,
                                AppWidgetManager appWidgeManger, int appWidgetId) {

        Log.i(TAG, "appWidgetId = " + appWidgetId);
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
                R.layout.widget);

        // "窗口小部件"点击事件发送的Intent广播
        Intent intentClick = new Intent();
        intentClick.setAction(CLICK_ACTION);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
                intentClick, 0);
        remoteViews.setOnClickPendingIntent(R.id.imageView1, pendingIntent);
        appWidgeManger.updateAppWidget(appWidgetId, remoteViews);
    }

    private Bitmap rotateBitmap(Context context, Bitmap srcbBitmap, float degree) {
        Matrix matrix = new Matrix();
        matrix.reset();
        matrix.setRotate(degree);
        Bitmap tmpBitmap = Bitmap.createBitmap(srcbBitmap, 0, 0,
                srcbBitmap.getWidth(), srcbBitmap.getHeight(), matrix, true);
        return tmpBitmap;
    }
}

4.在AndroidManifest.xml中声明小部件

 <receiver android:name=".MyAppWidgetProvider" >
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/appwidget_provider_info" >
            </meta-data>

            <intent-filter>
                <action android:name="com.ryg.chapter_5.action.CLICK" />//小部件的单击行为
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />//系统规范,必须有
            </intent-filter>
        </receiver>

AppWidgetProvider 中几个方法的调用时机

 @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        //当该窗口小部件第一次添加到桌面时调用该方法,
        //可添加多次但只在地题词调用
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
        //小部件被添加时或者每次小部件更新时都会调用一个该方法
        //小部件更新时机由updatePeriodMillis来指定
        //每个周期小部件都会自动更新一次
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);
        //每删除一次桌面小部件就调用一次
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        //当最后一个该类型的桌面小部件被删除时调用该方法,注意是最后一个。
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        //这是广播的内置方法,用于分发具体的事件给其他方法
    }

3. PendingIntent概述
顾名思义:PendingIntent表示一种处于Pending状态的意图,而Pending状态表示的是一种待定,等待,即将发生的意思,就是说接下来有一个意图将在某个待定的事件发生。可以看出PendingIntent和Intent的区别在于,PendingIntent是在将来某个不确定的时刻发生,而Intent是立即发生。PendingIntent典型的使用场景是给RemoteViews添加点击事件,因为RemoteViews运行在远程进程中,因此RemoteViews不同于普通的view,所以无法直接向view那样通过setOnClickListener方法来设置单击事件。要给RemoteViews设置单击事件,就必须使用PendingIntent,PendingIntent通过send和cancel放来发送和取消特定的待定的Intent。

PendingIntent支持三种特定意图:启动Activity,启动Service和发送广播。

 - getActivity(Context context,int requestCode,Intent intent,int flags)//获得一个PendingIntent,该待定意图发送时,相当于context.startActivity(Intent);
 - getService(Context context,int requestCode,Intent intent,int flags);//获得一个PendingIntent,该待定意图发送时,相当于context.startService(Intent);
 - getBroadcast(Context context,int requestCode,Intent intent,int flags));
 - //获得一个PendingIntent,该待定意图发送时,相当于context.sendBroadcast(Intent);

flags 的类型:

  • FLAG_ONE_SHOT: PendingIntent只能使用一次
  • FLAG_NO_CREATE:不怎么用
  • FLAG_CANCEL_CURRENT:PendingIntent如果已经存在,那么他们都会被cancel,然后系统会创建一个新的PendingIntent
  • FLAG_UPDATE_CURRENT:PendingIntent如果已经存在,那么会被更新,即它们intent中的Extras会被替换成最新的。

二、RemoteViews的内部机制

RemoteViews的只主要作用是在其他进程中显示并更新View界面,其构造方法中有两个参数,一个是当前包名,一个是布局文件

支持的Layout:

  • FrameLayout
  • LinearLayout
  • RelativeLayout
  • GridLayout

支持的View:

  • AnalogClock,Botton,Chronometer,ImageBottom,ImageView,ProgressBar
  • TextView,ViewFlipper,ListView,GridView,StackView,AdapterViewFlipper,ViewStub

三、RemoteViews的意义

 public void onButtonClick(View v) {
        RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_simulated_notification);
        remoteViews.setTextViewText(R.id.msg, "msg from process:" + Process.myPid());
        remoteViews.setImageViewResource(R.id.icon, R.drawable.icon1);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,
                0, new Intent(this, DemoActivity_1.class), PendingIntent.FLAG_UPDATE_CURRENT);
        PendingIntent openActivity2PendingIntent = PendingIntent.getActivity(
                this, 0, new Intent(this, DemoActivity_2.class), PendingIntent.FLAG_UPDATE_CURRENT);
        remoteViews.setOnClickPendingIntent(R.id.item_holder, pendingIntent);
        remoteViews.setOnClickPendingIntent(R.id.open_activity2, openActivity2PendingIntent);
        Intent intent = new Intent(MyConstants.REMOTE_ACTION);
        intent.putExtra(MyConstants.EXTRA_REMOTE_VIEWS, remoteViews);
        sendBroadcast(intent);
    }
public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";

    private LinearLayout mRemoteViewsContent;

    private BroadcastReceiver mRemoteViewsReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            RemoteViews remoteViews = intent
                    .getParcelableExtra(MyConstants.EXTRA_REMOTE_VIEWS);
            if (remoteViews != null) {
                updateUI(remoteViews);
            }
        }
    };

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

    private void initView() {
        mRemoteViewsContent = (LinearLayout) findViewById(R.id.remote_views_content);
        IntentFilter filter = new IntentFilter(MyConstants.REMOTE_ACTION);
        registerReceiver(mRemoteViewsReceiver, filter);
    }

    private void updateUI(RemoteViews remoteViews) {
//        View view = remoteViews.apply(this, mRemoteViewsContent);
        int layoutId = getResources().getIdentifier("layout_simulated_notification", "layout", getPackageName());
        View view = getLayoutInflater().inflate(layoutId, mRemoteViewsContent, false);
        remoteViews.reapply(this, view);
        mRemoteViewsContent.addView(view);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值