天天看點

了解RemoteViews一、RemoteViews的應用二、RemoteViews的内部機制三、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,
                    , 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,
                    , 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(, 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 = ; i < ; i++) {
                        float degree = (i * ) % ;
                        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, , intentClick, );
                        remoteViews.setOnClickPendingIntent(R.id.imageView1, pendingIntent);
                        appWidgetManager.updateAppWidget(new ComponentName(
                                context, MyAppWidgetProvider.class),remoteViews);
                        SystemClock.sleep();
                    }

                }
            }).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 = ; 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, ,
                intentClick, );
        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, , ,
                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,
                , new Intent(this, DemoActivity_1.class), PendingIntent.FLAG_UPDATE_CURRENT);
        PendingIntent openActivity2PendingIntent = PendingIntent.getActivity(
                this, , 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);
    }