如何在 Widget 中使用 Glide 更新 ImageView contentDescription

How to update ImageView contentDescription with Glide in a Widget

我有一个 Android 主屏幕小部件,其布局基本上是 ImageView:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_ultraviolet_widget_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true"
    android:focusable="true"
    android:gravity="center">

    <ImageView
        android:id="@+id/iv_ultraviolet_widget_image"
        android:layout_width="@dimen/widget_size"
        android:layout_height="@dimen/widget_size"
        android:src="@drawable/ic_uvi_loading" />

</RelativeLayout>

ImageView 将随时间更新为“当前”值,使用以下代码用图像(可绘制对象)表示:

/**
* Updates ultraviolet index widget view.
*/
@JvmStatic
private fun updateWidgetViews(context: Context, appWidgetId: Int, @DrawableRes drawableId: Int) {
    val widgetView = RemoteViews(context.packageName, R.layout.ultraviolet_widget)

    // Set an Intent to launch MainActivity when clicking on the widget
    val intent = Intent(context, MainActivity::class.java)
    val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
    widgetView.setOnClickPendingIntent(R.id.rl_ultraviolet_widget_layout, pendingIntent)

    val appWidgetTarget = AppWidgetTarget(context, R.id.iv_ultraviolet_widget_image, widgetView, appWidgetId)

    Glide
        .with(context)
        .asBitmap() // This is needed because AppWidgetTarget is a Bitmap target. See: https://github.com/bumptech/glide/issues/2717#issuecomment-351791721
        .transition(BitmapTransitionOptions.withCrossFade())
        .load(drawableId)
        .into(appWidgetTarget)

    pushWidgetUpdate(context, appWidgetId, widgetView)
}

/**
* Pushes update for a given widget.
*/
@JvmStatic
fun pushWidgetUpdate(context: Context, appWidgetId: Int, remoteViews: RemoteViews) {
    val manager = AppWidgetManager.getInstance(context)
    // Remember to check always for null in platform types (types ended in !).
    // See: 
    manager?.let {
        manager.updateAppWidget(appWidgetId, remoteViews)
        Timber.d("Widget %d was updated", appWidgetId)
    }
}

我的问题是如何同时更新小部件 ImageView 中的 contentDescription 属性 以及每次更新的描述并使用 Glide,以便我的小部件更易于访问并且它可以与 TalkBack 一起使用。我已经搜索过了,但我还没有找到 Glide 和远程视图的任何示例。

提前致谢,

哈维

您可以在加载图片时为 Glide 添加监听器。因此,当加载图像成功时,您更新 imageView 的 contentDescription。

您可以在此处阅读如何向 Gilde 添加侦听器:https://github.com/codepath/android_guides/wiki/Displaying-Images-with-the-Glide-Library#loading-errors

GlideApp.with(context)
    .load("http://via.placeholder.com/300.png")
    .placeholder(R.drawable.placeholder)
    .error(R.drawable.imagenotfound)
    .listener(new RequestListener<Drawable>() {
        @Override
        public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
            // log exception
            Log.e("TAG", "Error loading image", e);
            return false; // important to return false so the error placeholder can be placed
        }

        @Override
        public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
            // load image success, update the contentDescription here
            return false;
        }
    })
    .into(ivImg);

感谢@Nguyễn Quang Thọ 的帮助,我找到了解决方案。首先,我在 Kotlin 中创建了一个数据 class(一个元组)用于保存可绘制对象及其关联的字符串:

data class UltravioletImageContents(@DrawableRes val drawableId: Int, val contentDescription: String)

其次,我使用 Glide 结果侦听器添加了 contentDescription 更新并将更改推送到我的 updateWidgetViews 函数中的小部件。

@JvmStatic
private fun updateWidgetViews(context: Context, appWidgetId: Int, ultravioletImageContents: UltravioletImageContents) {
    val widgetView = RemoteViews(context.packageName, R.layout.ultraviolet_widget)

    // Set an Intent to launch MainActivity when clicking on the widget
    val intent = Intent(context, MainActivity::class.java)
    val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
    widgetView.setOnClickPendingIntent(R.id.rl_ultraviolet_widget_layout, pendingIntent)

    // Set image
    val appWidgetTarget = AppWidgetTarget(context, R.id.iv_ultraviolet_widget_image, widgetView, appWidgetId)
    Glide
        .with(context)
        .asBitmap() // This is needed because AppWidgetTarget is a Bitmap target. See: https://github.com/bumptech/glide/issues/2717#issuecomment-351791721
        .transition(BitmapTransitionOptions.withCrossFade())
        .load(ultravioletImageContents.drawableId)
        .listener(object : RequestListener<Bitmap> {
            override fun onLoadFailed(e: GlideException?, model: Any?, target: com.bumptech.glide.request.target.Target<Bitmap>?, isFirstResource: Boolean): Boolean {
                Timber.e(e, "Drawable cannot be loaded and set in widget image view")
                return false // Important to return false so the error placeholder can be placed
            }

            override fun onResourceReady(resource: Bitmap?, model: Any?, target: com.bumptech.glide.request.target.Target<Bitmap>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
                // Set contentDescription in order to enable accessibility and be usable with TalkBack
                widgetView.setContentDescription(R.id.iv_ultraviolet_widget_image, ultravioletImageContents.contentDescription)

                pushWidgetUpdate(context, appWidgetId, widgetView)

                return false
            }
        })
        .into(appWidgetTarget)
}

最后,我想指出 pushWidgetUpdate 已移入 onResourceReady,因为我认为在没有图像的情况下更新我的小部件没有意义(但是,也许,我遗漏了一些东西)。