Android Kotlin如何设置全屏通知背景?仿Spotify样式
如何实现类似Spotify/SoundCloud的全屏通知背景?
我想要设置如示例图所示的通知全屏背景,但当前实现的效果如结果图所示。
现有实现尝试
最初我尝试用setLargeIcon来设置背景,但图片显示模糊:
val notification = androidx.core.app.NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle(data.name) .setContentText(data.author) .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentIntent(dataAction.intent) .setPriority(androidx.core.app.NotificationCompat.PRIORITY_HIGH) .setVisibility(androidx.core.app.NotificationCompat.VISIBILITY_PUBLIC) .setColorized(true) .setCategory(AUDIO_SERVICE) .setOngoing(true) .setLargeIcon(largeBitmap)// 尝试用这个设置通知全屏背景 .setStyle( NotificationCompat.MediaStyle().setMediaSession(mediaSessionCompat.sessionToken) ) .addAction(R.drawable.ic_previous, "Previous", dataAction.previous) .addAction(R.drawable.ic_pause, "Play", dataAction.play) .addAction(R.drawable.ic_next, "Next", dataAction.next) .addAction(R.drawable.ic_exit, "Exit", dataAction.exit) .build() startForeground(1, notification)
起初我以为是图片质量问题,但把图片放到ImageView或者用BigPictureStyle测试时,图片显示清晰。我用setLargeIcon是因为这是我找到的唯一能设置通知背景的方法,但实际效果模糊。
自定义布局尝试(Android 12/13)
之后我尝试用自定义布局,但通知直接不显示:
val contentView = RemoteViews(packageName, R.layout.notification_layout) notification.setStyle(NotificationCompat.DecoratedMediaCustomViewStyle()) notification.setCustomContentView(contentView)
自定义布局文件notification_layout.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/mainLayoutNotification" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="3dp"> <ImageView android:id="@+id/imageBackground" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginEnd="10dp" android:src="@drawable/testimg" /> </LinearLayout>
补充信息
我测试了2048x2048分辨率的图片,转换Bitmap的代码:
val withDrawable = BitmapFactory.decodeResource(resources, R.drawable.testimg)
也尝试从音频文件获取内嵌封面:
val path ="/storage/emulated/0/Download/Ahhhhhhh/Glichery - Sea Of Problems.mp3" val withEmbededImage = getAudioFileAlbumArt(path) fun getAudioFileAlbumArt(filePath: String): Bitmap? { val retriever = MediaMetadataRetriever() try { retriever.setDataSource(filePath) val rawArt = retriever.embeddedPicture if (rawArt != null) { return BitmapFactory.decodeByteArray(rawArt, 0, rawArt.size) } } catch (e: Exception) { e.printStackTrace() } finally { retriever.release() } return null }
但设置为LargeIcon后背景还是模糊。
解决方案
1. 用MediaStyle原生方法设置背景(推荐)
AndroidX的NotificationCompat.MediaStyle提供了setBackgroundBitmap()方法,这才是设置媒体通知全屏背景的正确方式,setLargeIcon只是设置通知左侧的小图标,并非背景。修改代码如下:
val notification = androidx.core.app.NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle(data.name) .setContentText(data.author) .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentIntent(dataAction.intent) .setPriority(NotificationCompat.PRIORITY_HIGH) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setColorized(true) .setCategory(NotificationCompat.CATEGORY_TRANSPORT) // 必须用这个分类,系统才会识别为媒体通知 .setOngoing(true) .setStyle( NotificationCompat.MediaStyle() .setMediaSession(mediaSessionCompat.sessionToken) .setBackgroundBitmap(largeBitmap) // 设置全屏背景图 ) .addAction(R.drawable.ic_previous, "Previous", dataAction.previous) .addAction(R.drawable.ic_pause, "Play", dataAction.play) .addAction(R.drawable.ic_next, "Next", dataAction.next) .addAction(R.drawable.ic_exit, "Exit", dataAction.exit) .build() startForeground(1, notification)
注意:
- 背景图推荐使用2:1比例(如1080x540),或高度控制在256dp-400dp之间,避免拉伸模糊。
- 必须设置
CATEGORY_TRANSPORT分类,否则系统不会应用背景样式。
2. 修复自定义布局不显示问题
如果坚持用自定义布局,需调整以下几点:
- 根布局高度不要用
wrap_content,改为固定高度(如256dp):<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/mainLayoutNotification" android:layout_width="match_parent" android:layout_height="256dp" android:padding="3dp"> - 给
ImageView添加scaleType避免拉伸:<ImageView android:id="@+id/imageBackground" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@drawable/testimg" /> - 确保
smallIcon正确设置,这是通知显示的必要条件。
3. 优化Bitmap解码质量
解码图片时禁止系统自动缩放,保证原始分辨率:
// 解码资源图片 val options = BitmapFactory.Options() options.inScaled = false options.inPreferredConfig = Bitmap.Config.ARGB_8888 val withDrawable = BitmapFactory.decodeResource(resources, R.drawable.testimg, options) // 解码内嵌封面 fun getAudioFileAlbumArt(filePath: String): Bitmap? { val retriever = MediaMetadataRetriever() try { retriever.setDataSource(filePath) val rawArt = retriever.embeddedPicture if (rawArt != null) { val options = BitmapFactory.Options() options.inScaled = false options.inPreferredConfig = Bitmap.Config.ARGB_8888 return BitmapFactory.decodeByteArray(rawArt, 0, rawArt.size, options) } } catch (e: Exception) { e.printStackTrace() } finally { retriever.release() } return null }
内容的提问来源于stack exchange,提问作者Nguyễn Minh Khoa
相关产品推荐
相关产品推荐

