RemoteViews表示是一個可以在其他的進程中顯示View結構,由於它在其他進程中顯示,因此,我們刷新這個界面需要通過跨進程通信來實現,而RemoteViews 提供了一組基礎的操作用於更新界面。
RemoteViews在Android中的使用場景有兩種:通知欄和桌面小部件。
1.RemoteViews的應用
RemoteViews在實際開發中,主要用在通知欄和桌面小部件的開發過程中。通知欄就是使用NotificationManager的notify方法來實現在通知欄顯示通知信息。使用NotificationManager我們可以使用默認的效果,也可以使用自定義的佈局(提供一個自定義佈局文件)。桌面小部件可以通過AppWidgetProvider來實現的,AppWidgetProvier本質上是使用廣播來實現的。通知欄和桌面小部件的開發過程中都會用到RemoteViews,它們在更新界面的時候,不能像在Activity那樣,直接拿View的對象來更新View的信息。這是因爲這兩個的界面都是運行在系統的SystemServer進程當中,屬於跨進程。爲了跨進程更新界面,RemoteViews提供了一系列set方法,並且這些方法都在View當中定義過,並且RemoteViews支持的View的類型也是有限制的。
1.1 RemoteViews在通知欄上的應用
下面複習一下NotificationManager的使用:
NotificationManager:通知欄的管理類,負責發通知、清除通知等。NotificationManager是一個系統的Service,獲取NotificationManager的對象需要調用Context的getSystemService()方法,如下:
NotificationManager nm = ( Notificationmanager )getSystemService( Context.NOTIFICATION_SERVICE ) ;
通過調用NotificationManager的notify方法,可以讓通知顯示出來,notify方法有2個參數,第一個參數是Notification的Id 這個ID是唯一的,當你的ID的通知已經存在就會覆蓋掉之前的,第二個是Notification的對象。下面來介紹一下Notification.
Notification:是具體的狀態欄通知對象,可以設置icon、文字、提示聲音、震動等參數。
基本參數爲:
- 通知圖標 icon
- 標題和內容 title and expanded message
- 點擊執行的跳轉 PendingIntent對象
可選的設置:
- 狀態欄頂部提示消息 A ticker-text message
- 提示音 An alert sound
- 震動 A vibrate setting
- 燈光 A flashing LED setting
1.創建一個Notification:
Notification notification = new Notification();
notification.icon = R.drawable.ic_launcher;
//通知時在狀態欄顯示的內容
notification.tickerText = "hello_world";
//設置通知的顯示時間
notification.when = System.currentTimeMills();
//意指點擊這個Notification後,立刻取消自身
notification.flags = Notification.FLAG_AUTO_CANCEL;
//通知的默認參數 DEFAULT_SOUND, DEFAULT_VIBRATE, DEFAULT_LIGHTS.
//如果要全部採用默認值, 用 DEFAULT_ALL.
//此處採用默認聲音
notification.defaults = Notification.DEFAULT_SOUND ;
Intengt inetent =new Intent( MainActivity.this , TestActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity( MainActivity.this , 0 ,intent , PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo( MainActivity.this , "TestTitle","TestMessage", pendingIntent);
PendingIntent.FLAG_UPDATE_CURRENT會更新之前PendingIntent的消息,比如,你推送了消息1,並在其中的Intent中putExtra了一個值“ABC”,在未點擊該消息前,繼續推送第二條消息,並在其中的Intent中putExtra了一個值“CBA”,好了,這時候,如果你單擊消息1或者消息2,你會發現,他倆個的Intent中讀取過來的信息都是“CBA”,就是說,第二個替換了第一個的內容
上面的代碼,我們就創建了一個通知,之後,我們是用如下代碼:
nm.notify( 1 , notification);
就可以彈出一個通知了,這個通知的ID 是1,我們可以通過ID 來找到這個通知,實現更新這個通知的信息,也可以取消這條通知。
2.更新Notification
上面我們創建了一條ID爲1 的通知,現在我們需要更新這通知,那麼我們就採用如下:
notification.setLatestEventInfo( MainActivity.this , "TestTitle2","TestMessage",pengdingIntent);
nm.notify( 1 , notification );
這樣就能更新通知的內容,如果我們把上面的1,換成2,那麼就會顯示2條通知,因爲ID不一樣,而我們如果不用之前的notification而是新建了一個Notification的對象,只要ID一樣 也是實現更新的效果。
3.刪除Notification
刪除Notification很簡單,只需要調用NotificationManager的cancel 方法,傳入ID就可以了
4.Notification其他的設置
(1)聲音:聲音可以通過使用如下代碼採用系統默認的聲音:
notification.defaults = Notification.DEFAULT_SOUND;
如果要使用默認的,那麼就要使用到notification的sound屬性了,
也可以使用自定義鈴聲,就需要使用自定義的Uri了
比如使用SD卡的鈴聲如下:
notification.defaults = Uri.parse("file:///sdcard/notification/ringer.mp3");
使用raw目錄的鈴聲如下:
notification.defaults = Uri.parse("android.resource://"+getPackageName()+"/"+R.raw.cat);
使用assets目錄下:
notification.defaults = Uri.parse("file:///android_asset/RELATIVEPATH");
比如使用系統的鈴聲如下:
notification.sound = Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6");
如果default和sound同時出現,那麼sound設置的會無效。
默認情況下,通知的聲音播放一遍就結束,如果需要聲音循環播放,就要在flag上添加參數FLAG_INSISTENT 。這樣生意你回到用戶響應以後才停止,比如下拉狀態欄
notification.flags != notification.FLAG_INSISTENT;
(2)震動:震動
如果是使用默認的震動方式,那麼也可以使用default
notification.defaults |= Notification.DEFAULT_VIBRATE;
當然也可以自己定義震動形式,這個就需要使用Long數組,
long[] vibrate = {0,100,200,300};
notification.vibrate = vibrate;
在上面的Long型數組中,第一個參數是開始振動前的等待時間,第二個是第一次震動的時間,第三個是第二次震動的時間,以此類推,不過沒辦法做到重複震動。
同樣,如果有default 那麼就會採用default。
使用振動器之前我們需要在清單文件裏面聲明權限:
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
(4)閃光
同樣的閃光也是有默認的,
notification.defaults |= Notification.DEFAULT_LIGHTS;
自定義:
notification.ledARGB = 0xff00ff00;
notification.ledOnMS = 300;
notification.ledOffMS = 1000;
notification.flags |= Notification.FLAG_SHOW_LIGHTS;
其中ledARGB 表示燈光顏色、 ledOnMS 亮持續時間、ledOffMS 暗的時間。
注意:這邊的顏色跟設備有關,不是所有的顏色都可以,要看具體設備。
4.使用自定義樣式的通知欄
使用自定義的通知,我們首先要提供一個佈局文件,然後使用RemoteViews來加載這個佈局文件就可以改變通知的樣式,代碼如下:
Notification notification = new Notification();
notification.icon = R.drawable.ic_launcher;
notification.tickerText = "Test Ticker";
notification.when = System.currentTimeMillis();
notification.flags = Notification.FLAG_AUTO_CANCEL;
Intent intent = new Intent( this , TestActivity.class);
PendingIntent pengdingIntent = PendingIntent.getActivity( this , 0, intent , PendingIntent.FLAG_UPDATE_CURRENT );
RemoteViews remoteViews = new RemoteViews( getPackageName(), R.layout.layout_notification);
remoteViews.setTextViewText( R.id.tvTitle,"testTitle");
remoteView.setImageViewResource( R.id.ivNotifyIcon,R.drawable.icon1);
PendingIntent pd = PendingIntent.getActivity( this , 0 , new Intent( this , TestActivity2.class),PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent( R.id.btnShowNotify, pd);
notification.contentView = remoteVIews;
notification.contentIntent = pengdingIntent;
NotificationManager nm = ( NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
manager.notify( 2 , notification);
上面就是使用RemoteViews的代碼。RemoteViews提供了多種構造函數,在這邊,我們採用的是使用包名和佈局ID來創建一個RemoteViews。
從上面的代碼,可以看到,我們在訪問RemoteViews的方法時候,和正常的訪問不一樣,我們並不是通過使用對象的引用來更新RemoteViews中的內容,而是RemoteViews提供了一些封裝過的方法,我們來更新。而如果我們需要在控件上添加點擊事件,可以在setOnClickPendingIntent使用PengdingIntent 實現,而且,RemoteViews也提供了反射機制,使得我們可以通過methodName和一些參數來調用指定ID上的方法(對參數還是有一些限制的)。
5. 其他有用的設置:
flags:
Notification.FLAG_INSISTENT; //讓聲音、振動無限循環,直到用戶響應
Notification.FLAG_AUTO_CANCEL; //通知被點擊後,自動消失
Notification.FLAG_NO_CLEAR; //點擊'Clear'時,不清楚該通知(QQ的通知無法清除,就是用的這個)
上面就是RemoteView在Notification上的應用了,我們也回顧了Notification,下面開始學習RemoteViews在桌面小部件上的應用
1.2 RemoteViews在桌面小部件上的應用
AppWidgetProvider是Android中提供的用於實現桌面小部件的類,它的本質是一個廣播,也就是BrocastReceiver。所以說,AppWidgetProvider可以直接當成一個BrocastReceiver來使用。下面開始學習桌面小部件的開發步驟。
1.2.1 定義小部件界面
首先,我們在res/layout目錄下新建一個佈局文件layout_custom_appwidget.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/ivWidgetImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_field"
/>
</LinearLayout>
1.2.2 定義小部件配置信息
在res/xml下新建custom_appwidget_provider_info.xml
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/layout_custom_appwidget"
android:minWidth="@dimen/appwidget_min_width"
android:minHeight="@dimen/appwiget_min_height"
android:updatePeriodMillis="@integer/widget_updatePeriodMillis"
>
</appwidget-provider>
上面幾個參數的意義很明確,initialLayout指的是小工具使用的初始化佈局文件,minHeight和minWidth 定義小公舉的最小尺寸,updatePeriodMills定義小工具的自動更新週期,毫秒爲單位,每隔一個週期,小工具的自動更新就會觸發。
3.定義小部件的實現類
public class MyAppWidgetProvider extends AppWidgetProvider {
public static final String TAG = "MyAppWidgetProvider";
public static final String CLICK_ACTION = "com.zhenfei.MyAppWidgetProvider.action";
public static final int NOTIFY_ID = 80001;
public MyAppWidgetProvider()
{
super();
}
@SuppressLint("NewApi")
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Log.e(TAG, "onReceive: action=" + intent.getAction() );
if( CLICK_ACTION.endsWith( intent.getAction()))
{
NotificationManager notificationManager =(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(context);
builder.setTicker( context.getResources().getString(R.string.notify_titker_text));
builder.setWhen( System.currentTimeMillis() + 1000 );
builder.setVibrate( new long [] { 0 , 200 , 400 , 600 , 800 } );
builder.setDefaults( Notification.DEFAULT_SOUND );
builder.setSmallIcon( R.drawable.ic_field );
builder.setAutoCancel(true);
Intent mIntent = new Intent(context ,MainActivity2.class );
mIntent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK);
mIntent.setFlags( Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
Log.e(TAG, "getPackageName:" + context.getPackageName() );
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mIntent, 0);
builder.setContentIntent(pendingIntent);
RemoteViews remoteViews = new RemoteViews( context.getPackageName(), R.layout.layout_custom_notification);
remoteViews.setTextViewText(R.id.tvTitle , "有新的信息到了");
remoteViews.setTextViewText( R.id.tvContent, "你到家了嗎");
builder.setContent(remoteViews);
Notification notification = builder.build();
notificationManager.notify(NOTIFY_ID, notification);
}
}
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onDeleted(context, appWidgetIds);
}
@Override
public void onDisabled(Context context) {
// TODO Auto-generated method stub
super.onDisabled(context);
}
@SuppressLint("NewApi")
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Intent intent = new Intent();
intent.setAction(CLICK_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0 , intent, 0 );
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.layout_custom_appwidget);
remoteViews.setOnClickPendingIntent(R.id.ivWidgetImg, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
}
在AndroidManifest.xml中聲明如下:
<receiver
android:name="com.example.notificationdemo.MyAppWidgetProvider"
>
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="com.zhenfei.MyAppWidgetProvider.action"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/custom_appwidget_provider_info"
/>
</receiver>
聲明這個部件類接受兩個廣播,一個是桌面部件更新廣播UPDATE,一個是自定義的按鈕事件廣播。AppWiget主要有:onEnable,onDisabled,onDeleted 和 onReceive這些方法:
- onEnable : 當該窗口小部件第一次添加到桌面的時候調用這個方法,可以添加多次,但是隻在第一次調用。
- onUpdate: 小部件被添加的時候或者每次小部件更新都會調用一次該方法,小部件的更新時機由updatePeriodMillis來指定,每個週期小部件都會自動更新一次。
- onDeleted:每刪除一次桌面小部件就會調用一次。
- onDisable:當最後一個桌面小部件被刪除的時候調用。
- onReceive:這個是廣播的內置方法,用於分發事件。
並且爲它指定了meta-data ,這個meta-data 提供了這個部件的佈局。
上面的代碼實現了一個簡單的桌面小部件,在小部件上面會顯示一個按鈕,點擊以後,這個按鈕就會發送一個廣播,廣播被自己接收,然後我們就會彈出一個通知,點擊通知會進入指定的Activity。桌面小部件在界面更新上都需要使用RemoteViews 不管是小部件的界面初始化還是界面更新都必須依賴它。