Training—Best Practices for User Experience & UI Part 2
接下来介绍怎么使用Notification。
首先需要一个NotificationCompat.Builder
,并使用 A small icon, set by setSmallIcon(),A title, set by setContentTitle(),Detail text, set by setContentText()。
除此之外,一般还会为这个通知设置一个Action,即打开一个Activity,通过一个包含了intent的PendingIntent来启动。
Intent resultIntent = new Intent(this, ResultActivity.class); ... // Because clicking the notification opens a new ("special") activity, there's // no need to create an artificial back stack. PendingIntent resultPendingIntent = PendingIntent.getActivity( this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT );
并通过下面的方法来绑定:
PendingIntent resultPendingIntent;
...
mBuilder.setContentIntent(resultPendingIntent);
做完上面的操作之后,接下来就是现实通知了:
NotificationCompat.Builder mBuilder; ... // Sets an ID for the notification int mNotificationId = 001; // Gets an instance of the NotificationManager service NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); // Builds the notification and issues it. mNotifyMgr.notify(mNotificationId, mBuilder.build());
首先获得一个NotificationManager实例,然后使用notify方法,参数是ID以及Notification对象(通过build方法返回)。
从通知启动一个Activity有两种情况,一种是Regular activity ,另外一种是 Special activity 。声明完相关的Activity之后就能用下面的方法创建ACtivity了。
Intent resultIntent = new Intent(this, ResultActivity.class); TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); // Adds the back stack stackBuilder.addParentStack(ResultActivity.class); // Adds the Intent to the top of the stack stackBuilder.addNextIntent(resultIntent); // Gets a PendingIntent containing the entire back stack PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); ... NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setContentIntent(resultPendingIntent); NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.notify(id, builder.build());
注意这里使用了addParentStack和addNextIntent,在之前所讲的是直接用addNextIntentWithParentStack。
接下来讲讲Special activity,Special activity的特别在于不需要back stack,进一步说明就是它就是一个Activity,没有什么需要后退的。
首先进行相关申明:
<activity android:name=".ResultActivity" ... android:launchMode="singleTask" android:taskAffinity="" android:excludeFromRecents="true"> </activity> ...
android:name="activityclass"
- The activity's fully-qualified class name.
android:taskAffinity=""
- Combined with the
FLAG_ACTIVITY_NEW_TASK
flag that you set in code, this ensures that thisActivity
doesn't go into the application's default task. Any existing tasks that have the application's default affinity are not affected.android:excludeFromRecents="true"
- Excludes the new task from Recents, so that the user can't accidentally navigate back
接下来代码里体现:
// Instantiate a Builder object. NotificationCompat.Builder builder = new NotificationCompat.Builder(this); // Creates an Intent for the Activity Intent notifyIntent = new Intent(new ComponentName(this, ResultActivity.class)); // Sets the Activity to start in a new, empty task notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // Creates the PendingIntent PendingIntent notifyIntent = PendingIntent.getActivity( this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT ); // Puts the PendingIntent into the notification builder builder.setContentIntent(notifyIntent); // Notifications are issued by sending them to the // NotificationManager system service. NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Builds an anonymous Notification object from the builder, and // passes it to the NotificationManager mNotificationManager.notify(id, builder.build());
其实主要就是新建一个back stack 给他,而如果要做到这一个的话就需要通过相关声明以及设置intent的setFlags。
更新通知
只需要在ID那里写上需要更新的通知ID即可。
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Sets an ID for the notification, so it can be updated int notifyID = 1; mNotifyBuilder = new NotificationCompat.Builder(this) .setContentTitle("New Message") .setContentText("You've received new messages.") .setSmallIcon(R.drawable.ic_notify_status) numMessages = 0; // Start of a loop that processes data and then notifies the user ... mNotifyBuilder.setContentText(currentText) .setNumber(++numMessages); // Because the ID remains unchanged, the existing notification is // updated. mNotificationManager.notify( notifyID, mNotifyBuilder.build()); ...
而关于删除通知的有:
Notifications remain visible until one of the following happens:
- The user dismisses the notification either individually or by using "Clear All" (if the notification can be cleared).
- The user touches the notification, and you called
setAutoCancel()
when you created the notification.- You call
cancel()
for a specific notification ID. This method also deletes ongoing notifications.- You call
cancelAll()
, which removes all of the notifications you previously issued.
用户操作的就不说了,在生成notification的时候你设置了setAutoCancel()那么用户点击之后notification就会自动消失。
你也可以使用cancel()或者cancelAll(),这个方法由notificationManage拥有。
接下来说一说比较特别的notification,有点像自定义的那种,叫做big view。实现也比较容易。
// Sets up the Snooze and Dismiss action buttons that will appear in the // big view of the notification. Intent dismissIntent = new Intent(this, PingService.class); dismissIntent.setAction(CommonConstants.ACTION_DISMISS); PendingIntent piDismiss = PendingIntent.getService(this, 0, dismissIntent, 0); Intent snoozeIntent = new Intent(this, PingService.class); snoozeIntent.setAction(CommonConstants.ACTION_SNOOZE); PendingIntent piSnooze = PendingIntent.getService(this, 0, snoozeIntent, 0); // Constructs the Builder object. NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.ic_stat_notification) .setContentTitle(getString(R.string.notification)) .setContentText(getString(R.string.ping)) .setDefaults(Notification.DEFAULT_ALL) // requires VIBRATE permission /* * Sets the big view "big text" style and supplies the * text (the user's reminder message) that will be displayed * in the detail area of the expanded notification. * These calls are ignored by the support library for * pre-4.1 devices. */ .setStyle(new NotificationCompat.BigTextStyle() .bigText(msg)) .addAction (R.drawable.ic_stat_dismiss, getString(R.string.dismiss), piDismiss) .addAction (R.drawable.ic_stat_snooze, getString(R.string.snooze), piSnooze);
接下来教你怎么在通知栏里显示进度,先直接上代码:
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mBuilder = new NotificationCompat.Builder(this); mBuilder.setContentTitle("Picture Download") .setContentText("Download in progress") .setSmallIcon(R.drawable.ic_notification); // Start a lengthy operation in a background thread new Thread( new Runnable() { @Override public void run() { int incr; // Do the "lengthy" operation 20 times for (incr = 0; incr <= 100; incr+=5) { // Sets the progress indicator to a max value, the // current completion percentage, and "determinate" // state mBuilder.setProgress(100, incr, false); // Displays the progress bar for the first time. mNotifyManager.notify(0, mBuilder.build()); // Sleeps the thread, simulating an operation // that takes time try { // Sleep for 5 seconds Thread.sleep(5*1000); } catch (InterruptedException e) { Log.d(TAG, "sleep failure"); } } // When the loop is finished, updates the notification mBuilder.setContentText("Download complete") // Removes the progress bar .setProgress(0,0,false); mNotifyManager.notify(ID, mBuilder.build()); } } // Starts the thread by calling the run() method in its Runnable ).start();
看了代码应该就很清楚了,值得一提的是记得在下载完成之后给出相关提示,取消显示进度使用 setProgress(0, 0, false).。
记得setProgress的第三个参数,如果为true则表示是不确定的,例如安装进度等,如果是false则表明是确定的,例如下载进度。
接下来介绍怎么使用Search View
首先要有一个布局文件:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/search" android:title="@string/search_title" android:icon="@drawable/ic_search" android:showAsAction="collapseActionView|ifRoom" android:actionViewClass="android.widget.SearchView" /> </menu>
关于布局的,如果你已经有自己的布局文件了那么添加item标签的内容就行了。比较特别的属性就是collapsibleActionView
,设置该属性能当你需要搜索的时候让搜索框占满整个actionbar,不需要的时候就缩回去。
然后代码
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.options_menu, menu); return true; }
除此之外我们还需要一个searchable.xml
<?xml version="1.0" encoding="utf-8"?> <searchable xmlns:android="http://schemas.android.com/apk/res/android" android:label="@string/app_name" android:hint="@string/search_hint" />
至少要有label 。
然后在Activity里或者application里:
<activity ... > ... <meta-data android:name="android.app.searchable" android:resource="@xml/searchable" /> </activity>
android:name="android.app.searchable"是必须的。
当然还需要代码
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.options_menu, menu); // Associate searchable configuration with the SearchView SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView(); searchView.setSearchableInfo( searchManager.getSearchableInfo(getComponentName())); return true; }
接下来需要一个专门搜索的Activity
<activity android:name=".SearchResultsActivity" ... > ... <intent-filter> <action android:name="android.intent.action.SEARCH" /> </intent-filter> ... </activity>
然后写一个Activity
public class SearchResultsActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { ... handleIntent(getIntent()); } @Override protected void onNewIntent(Intent intent) { ... handleIntent(intent); } private void handleIntent(Intent intent) { if (Intent.ACTION_SEARCH.equals(intent.getAction())) { String query = intent.getStringExtra(SearchManager.QUERY); //use the query to search your data somehow } } ... }