如何在Android桌面小部件中动态设置Shape描边颜色?
问题描述
我有一个名为rounded_corner_shape.xml的Drawable文件:
<shape xmlns:android="http://schemas.android.com/apk/res/android" > <stroke android:width="1dp" android:color="TO_BE_SET_DYNAMICALLY" /> <corners android:radius="8dp" /> </shape>
该文件被用于ip_properties.xml布局中:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/ip_property_tv_1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/rounded_corner_shape" /> </LinearLayout>
我希望在RemoteViewsService.RemoteViewsFactory的实现类中动态设置描边颜色,代码框架如下:
class WifiPropertyViewsFactory: RemoteViewsService.RemoteViewsFactory{ // 其他重写方法... override fun getViewAt(position: Int): RemoteViews = RemoteViews(context.packageName, R.layout.ip_properties) .apply { setTextViewText(R.id.ip_property_tv_1, "some data") // 这里需要设置带圆角的背景描边颜色 } }
尝试用RemoteViews.setInt(R.id.ip_property_tv_1, "setBackgroundTint", colorInt)实现,但没有效果,请问该如何解决?
解决方案
setBackgroundTint无法生效的原因是:这个方法是对整个Drawable进行着色,而你的需求是单独修改shape中的描边属性,且RemoteViews本身不支持直接修改已加载的shape内部属性。以下是几种可行的实现方式:
方法一:API 24+ 直接设置动态生成的Drawable
如果你的应用最低兼容API 24及以上,这是最直接的方案:
- 先实现dp转px的工具方法:
fun Int.dpToPx(context: Context): Int { return TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), context.resources.displayMetrics ).toInt() }
- 在
getViewAt中动态创建GradientDrawable(对应你的shape)并设置属性:
override fun getViewAt(position: Int): RemoteViews = RemoteViews(context.packageName, R.layout.ip_properties) .apply { setTextViewText(R.id.ip_property_tv_1, "some data") val gradientDrawable = GradientDrawable().apply { cornerRadius = 8.dpToPx(context).toFloat() setStroke(1.dpToPx(context), colorInt) // colorInt为目标颜色值 } setDrawable(R.id.ip_property_tv_1, "background", gradientDrawable) }
方法二:通过Bitmap间接设置(兼容低版本)
如果需要兼容API 24以下的版本,可将动态生成的Drawable转换成Bitmap再设置为背景:
override fun getViewAt(position: Int): RemoteViews = RemoteViews(context.packageName, R.layout.ip_properties) .apply { setTextViewText(R.id.ip_property_tv_1, "some data") // 生成带圆角和描边的Drawable val gradientDrawable = GradientDrawable().apply { cornerRadius = 8.dpToPx(context).toFloat() setStroke(1.dpToPx(context), colorInt) // 根据TextView预估尺寸设置Drawable大小,避免显示异常 setSize(200.dpToPx(context), 40.dpToPx(context)) } // 转换为Bitmap val bitmap = Bitmap.createBitmap( gradientDrawable.intrinsicWidth, gradientDrawable.intrinsicHeight, Bitmap.Config.ARGB_8888 ) val canvas = Canvas(bitmap) gradientDrawable.setBounds(0, 0, canvas.width, canvas.height) gradientDrawable.draw(canvas) // 设置Bitmap为背景 setImageViewBitmap(R.id.ip_property_tv_1, bitmap) }
方法三:利用TintList覆盖描边颜色
修改shape文件的描边颜色为基础色,再通过tintList覆盖颜色:
- 修改
rounded_corner_shape.xml:
<shape xmlns:android="http://schemas.android.com/apk/res/android" > <stroke android:width="1dp" android:color="@android:color/transparent" /> <corners android:radius="8dp" /> </shape>
- 在RemoteViews中设置背景tint列表:
override fun getViewAt(position: Int): RemoteViews = RemoteViews(context.packageName, R.layout.ip_properties) .apply { setTextViewText(R.id.ip_property_tv_1, "some data") val tintList = ColorStateList.valueOf(colorInt) setColorStateList(R.id.ip_property_tv_1, "backgroundTintList", tintList) }
注意:此方案仅适用于shape只有描边和圆角、无填充色的场景,否则tint会同时影响填充色。
内容的提问来源于stack exchange,提问作者w2sv
相关产品推荐
相关产品推荐

