You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android通知中实现图片随进度条进度同步移动功能

Android通知实现图片随ProgressBar进度同步移动

需求

实现Android通知中的图片(iv_car1、iv_car2)随ProgressBar进度变化同步移动,效果类似SeekBar的滑块。

自定义通知布局

普通视图布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <!-- Title and message body -->
    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="PickUp in 6 minutes"
        android:textColor="@color/black"
        android:textSize="12sp"
        android:textStyle="bold"
        tools:ignore="HardcodedText" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ProgressBar
            android:id="@+id/progressBar1"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:progressTint="@color/black"
            android:layout_centerVertical="true"
            android:max="100"
            android:progress="50" />

        <ImageView
            android:id="@+id/iv_car1"
            android:layout_width="30dp"
            android:layout_height="20dp"
            android:layout_centerVertical="true"
            android:src="@drawable/icon_car3" />
    </RelativeLayout>

</LinearLayout>

注:移除包裹ImageView的冗余LinearLayout,避免干扰偏移计算

可展开视图布局

<?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="wrap_content"
    android:orientation="vertical"
    android:paddingVertical="@dimen/_5sdp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginVertical="@dimen/_5sdp"
            android:text="PickUp in 6 minutes"
            android:textColor="@color/black"
            android:textSize="12sp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/message"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Meet at the pickup point for the ride you have booked."
            android:textColor="@color/black"
            android:textSize="10sp" />

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <ProgressBar
                android:id="@+id/progressBar2"
                style="?android:attr/progressBarStyleHorizontal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:progressTint="@color/black"
                android:layout_centerVertical="true"
                android:max="100"
                android:progress="50" />

            <ImageView
                android:id="@+id/iv_car2"
                android:layout_width="30dp"
                android:layout_height="20dp"
                android:layout_centerVertical="true"
                android:src="@drawable/icon_car3" />
        </RelativeLayout>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/ride_custome_notification_bg"
        android:orientation="vertical">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center">

            <ImageView
                android:layout_width="@dimen/_100sdp"
                android:layout_height="@dimen/_40sdp"
                android:layout_gravity="center"
                android:layout_marginVertical="@dimen/_5sdp"
                android:src="@drawable/car_photo" />

            <ImageView
                android:id="@+id/user_image"
                android:layout_width="@dimen/_50sdp"
                android:layout_centerVertical="true"
                android:layout_height="@dimen/_50sdp"
                android:src="@drawable/profile" />
        </RelativeLayout>

        <TextView
            android:id="@+id/numberPlate"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginVertical="@dimen/_2sdp"
            android:gravity="center"
            android:text="GAK 7522"
            android:textSize="12sp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/carModel"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center"
            android:text="Grey Toyota corola"
            android:textSize="10sp" />
    </LinearLayout>

</LinearLayout>

注:移除包裹ImageView的冗余LinearLayout,确保margin设置生效

原代码问题分析

原实现存在以下问题:

  1. 进度偏移计算逻辑错误:progress * (density+30)的计算方式完全不符合ProgressBar宽度比例,导致图片移动距离与进度不匹配。
  2. 主线程阻塞:Thread.sleep(1000)会阻塞主线程,引发ANR风险。
  3. 布局层级冗余:ImageView被包裹在LinearLayout中,干扰margin的正确生效。

修正后的实现代码

fun notificationTest() {
    val channelId = "ride_driver_info"
    val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(
            channelId,
            "Ride Driver Info",
            NotificationManager.IMPORTANCE_LOW
        )
        notificationManager.createNotificationChannel(channel)
    }

    val remoteViews = RemoteViews(packageName, R.layout.ride_notification_custome_layout)
    val expandableContent = RemoteViews(packageName, R.layout.ride_notification_expand_custome_layout)
    
    // 设置圆角头像
    val sourceBitmap = BitmapFactory.decodeResource(resources, R.drawable.profile)
    val roundedBitmap = getRoundedBitmap(sourceBitmap)
    expandableContent.setImageViewBitmap(R.id.user_image, roundedBitmap)

    val notificationBuilder = NotificationCompat.Builder(this, channelId)
        .setSmallIcon(R.drawable.app_logo2)
        .setCustomContentView(remoteViews)
        .setCustomBigContentView(expandableContent)
        .setPriority(NotificationCompat.PRIORITY_LOW)
        .setAutoCancel(true)
        .setOngoing(true)

    var progress = 0
    val maxProgress = 100
    // 使用Coroutine实现非阻塞式进度更新
    CoroutineScope(Dispatchers.IO).launch {
        while (progress <= maxProgress) {
            // 更新进度条
            remoteViews.setProgressBar(R.id.progressBar1, maxProgress, progress, false)
            expandableContent.setProgressBar(R.id.progressBar2, maxProgress, progress, false)

            // 计算图片偏移:基于屏幕宽度比例,减去图片自身宽度的一半,避免图片超出进度条范围
            val screenWidth = resources.displayMetrics.widthPixels
            val progressRatio = progress.toFloat() / maxProgress
            // 减去图片宽度的一半,让图片中心与进度位置对齐
            val offsetPx = (screenWidth * progressRatio) - (30 * resources.displayMetrics.density / 2)
            val offsetDp = offsetPx / resources.displayMetrics.density

            // 更新图片margin
            remoteViews.setViewLayoutMargin(R.id.iv_car1, RemoteViews.MARGIN_START, offsetDp.toInt(), TypedValue.COMPLEX_UNIT_DIP)
            expandableContent.setViewLayoutMargin(R.id.iv_car2, RemoteViews.MARGIN_START, offsetDp.toInt(), TypedValue.COMPLEX_UNIT_DIP)

            // 更新通知
            notificationManager.notify(1, notificationBuilder.build())

            progress += 10
            delay(1000) // 非阻塞延迟
        }
    }
}

// 圆角图片工具方法
private fun getRoundedBitmap(bitmap: Bitmap): Bitmap {
    val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(output)
    val paint = Paint()
    val rect = Rect(0, 0, bitmap.width, bitmap.height)
    val rectF = RectF(rect)
    paint.isAntiAlias = true
    canvas.drawRoundRect(rectF, bitmap.width.toFloat()/2, bitmap.height.toFloat()/2, paint)
    paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
    canvas.drawBitmap(bitmap, rect, rect, paint)
    return output
}

关键修正点

  1. 正确的偏移计算:基于屏幕宽度和进度比例计算偏移量,同时减去图片宽度的一半,让图片中心与进度位置对齐,避免超出进度条范围。
  2. 非阻塞更新:使用Coroutine的delay替代Thread.sleep,避免阻塞主线程。
  3. 简化布局层级:移除包裹ImageView的LinearLayout,确保margin设置正确生效。
  4. 动态构建通知:每次更新通过NotificationBuilder重新构建通知,确保RemoteViews的修改能正确应用。

内容的提问来源于stack exchange,提问作者Umair Asim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 04:54:51