使用RemoteViews的Android通知 - 与RemoteViews布局相关联的活动

11

我一直在研究如何使用RemoteView创建自定义布局的通知。

到目前为止,我已经能够创建一个通知,并且将contentViewbigContentView指向包含自定义布局xml的RemoteView。但是,当创建这个RemoteView时,与自定义布局相关联的Activity并没有被启动。

我已经仔细检查了我的布局xml文件,在其中看起来有正确的Activity类名:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="0dp"
    android:paddingLeft="0dp"
    android:paddingRight="0dp"
    android:paddingTop="0dp"
    tools:context=".LLMNotificationActivity" >

..... the rest are standard layout items: images, buttons and text

</RelativeLayout>

在清单文件中,在主应用程序主活动之后,还会添加通知活动:

<activity
    android:name=".LLMNotificationActivity"
    android:label="@string/app_name">
    <intent-filter>
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

当通知使用RemoteView作为其内容时,我期望这个RemoteView将启动连接到其布局定义的活动。但是似乎并没有。

以下是我在主应用程序Activity中创建通知的方法:

protected void startNoti() {
    if( noti!=null ) return;

    Context context = getApplicationContext();  

    RemoteViews contentView = new RemoteViews(context.getPackageName(),R.layout.activity_noti1);

    Notification.Builder notibuilder = new Notification.Builder(context);
    notibuilder.setContentTitle(" ");
    notibuilder.setContentText(" ");
    notibuilder.setSmallIcon(R.drawable.ic_launcher);
    notibuilder.setOngoing(true);

    manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    noti = notibuilder.build();

    noti.contentView = contentView;

    manager.notify(NOTIFICATION_ID, noti);  
}

LLMNotificationActivity 活动类像往常一样被定义:

public class LLMNotificationActivity extends Activity {
    .... etc.... constructor, some button on-click handlers, nothing spectacular...
}

请问有没有人能告诉我我错过了什么或者我是否误解了RemoteView的作用?我的理解是,RemoteView一旦创建,应该会调用与其布局相关联的活动。或者,我错过了某些API,可以明确地设置RemoteView的意图吗?

到目前为止我所发现的只有设置内容Intent,它基本上只是在用户触摸通知后启动一个Activity。我要找的是处理自定义布局通知中某些UI元素的触摸,而不是无论用户在通知表面点击哪里都会启动一个Activity

例如,如果我在RemoteView中有3个图标(即ImageView),我希望能够处理每个图标上的触摸事件。我想象不出这不可能,因为如果不行的话,那么使用RemoteView在通知中有什么意义呢?


1
XML中的tools清单属性仅影响工具(例如,在基于声明的tools:context的情况下查看布局预览时提供正确的主题),在运行时没有任何影响。 - ianhanniballake
感谢您指出。XML是由我的IDE自动生成的。 - Siniša
3个回答

9

您需要通过setOnClickPendingIntent将活动与远程视图关联,以便启动以下活动...您可以在RemoteView中设置任何布局ID以进行单击。

 Intent intent = new Intent(context, YourActivity.class);
 PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,   PendingIntent.FLAG_UPDATE_CURRENT);
 RemoteViews removeWidget = new RemoteViews(context.getPackageName(), R.layout.your_layout);
 removeWidget.setOnClickPendingIntent(R.id.layout_id, pendingIntent);

在使用RelativeLayout时,请为其提供+id/layout_id

如果您需要在用户单击通知时启动活动,则必须使用PendingIntent,如下所示...

    NotificationCompat.Builder mBuilder =
            new NotificationCompat.Builder(this)
                    .setSmallIcon(R.drawable.ic_launcher)
                    .setContentTitle("title")
                    .setContent(mRemoteControl);
    Intent notificationIntent = new Intent(this, YourActivity.class);
    PendingIntent contentIntent = PendingIntent.getActivity(
            this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    mBuilder.setContentIntent(contentIntent);
    mNM.notify(1000,mBuilder.build());

对于3个按钮,您需要创建一个自定义RemoteView并使用PendingIntent。代码如下:

这里是我在我的一个媒体播放器应用中使用的自定义远程视图。它有三个按钮来处理点击事件。
public class RemoveControlWidget extends RemoteViews
{
private final Context mContext;

public static final String ACTION_PLAY = "com.mediabook.app.ACTION_PLAY";

public static final String ACTION_PREVIOUS = "com.mediabook.app.ACTION_PREVIOUS";

public static final String ACTION_NEXT = "com.mediabook.app.ACTION_NEXT";

public RemoveControlWidget(Context context , String packageName, int layoutId)
{
    super(packageName, layoutId);
    mContext = context;
    Intent intent = new Intent(ACTION_PLAY);
    PendingIntent pendingIntent = PendingIntent.getService(mContext.getApplicationContext(),100,
            intent,PendingIntent.FLAG_UPDATE_CURRENT);
    setOnClickPendingIntent(R.id.play_control,pendingIntent);
    setOnClickPendingIntent(R.id.pause_control,pendingIntent);
    intent = new Intent(ACTION_PREVIOUS);
    pendingIntent = PendingIntent.getService(mContext.getApplicationContext(),101,
            intent,PendingIntent.FLAG_UPDATE_CURRENT);
    setOnClickPendingIntent(R.id.previous_control,pendingIntent);
    intent = new Intent(ACTION_NEXT);
    pendingIntent = PendingIntent.getService(mContext.getApplicationContext(),102,
            intent,PendingIntent.FLAG_UPDATE_CURRENT);
    setOnClickPendingIntent(R.id.next_control,pendingIntent);
}
}

这是否意味着将活动与用于 RemoteViews 的布局相关联不可能? - Siniša
请检查我的更新答案。希望这是你要找的。 - Libin
谢谢,但正如我所解释的,我不想在通知点击时启动活动。我正在看你的第二部分,会尽快回复评论。 - Siniša
请检查我的更新答案。使用第一部分。您可以将活动与远程视图关联。希望这符合您的要求。 - Libin
我已经接受了您的答案,确切地说是使用自定义 RemoteViews 类的最后部分。不过,我添加了一个广播而非服务,并使我的扩展 RemoteViews 类侦听特定广播消息 - 这样我就可以将所有 UI 应用程序逻辑保留在一个对象实例中,而不是运行/访问服务。这也是因为当用户单击某些选项时,我需要对 UI 进行更改。基本上,我的通知现在就像一个小活动 :-) 感谢您宝贵的意见! - Siniša

2

您只需要像这样将setContentIntent添加到您的Notification.Builder中:

Intent i = new Intent(context, YourLaunchedActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, 0);
Notification.Builder notibuilder = new Notification.Builder(context);
notibuilder.setContentIntent(pendingIntent);

现在,当您点击通知时,YourLanchedActivity将被启动。

0

您可以通过以下方式在远程视图上实现挂起意图的单击,还请注意,如果您正在尝试从远程视图中获取活动,则在挂起意图中使用getAcitvity,例如:

//i will create a simple method//
       //use return getActivity cause getting the bottom Activity
private static PendingIntent getActivityBottom(Context context) {
    clearNotification(context);

    Intent intentStartActivity = new Intent(context, BotomNavViewActivity.class);
    intentStartActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    //intentStartActivity.addFlags(/*Intent.FLAG_ACTIVITY_CLEAR_TASK |*/ Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
    return PendingIntent.getActivity(
            context,
            2,
            intentStartActivity,
            PendingIntent.FLAG_UPDATE_CURRENT);
}

public static void clearNotification(Context context) {
    NotificationManager notificationManager = (NotificationManager) context
            .getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.cancel(NOTIFICATION_ID);
}
 

现在,如果您想获取您的服务类,请在挂起意图中使用getService。

private static PendingIntent stopPlayer(Context context){
    Intent serviceIntent = new Intent(context, MusicService.class);
    
           serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
           context.stopService(serviceIntent);
           mContentView.setImageViewResource(R.id.notif_play, R.drawable.ic_play);
        

    return PendingIntent.getService(
            context,
            2,
            serviceIntent,
            PendingIntent.FLAG_UPDATE_CURRENT);
}

像这样使用上述方法

  //perfoam action on notification to go to activity
    mContentView.setOnClickPendingIntent(R.id.notif_small,getActivityBottom(context));

    //for service
    mContentView.setOnClickPendingIntent(R.id.notif_play,stopPlayer(context));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接