【问题标题】:@GlideOption adding/extending RequestBuilder to customize listener and achieve functional approach in Java 8@GlideOption 添加/扩展 RequestBuilder 以自定义侦听器并在 Java 8 中实现功能方法
【发布时间】:2019-12-31 05:03:04
【问题描述】:

Glide 版本:4.9.0

我尝试自定义 RequestBuilder 类,以获得自定义构建器,在这种情况下,我想自定义 RequestBuilder#addListener 函数以将 @FunctionalInterfaceFunction0<R> 作为参数并适用于 Java 8。

我尝试阅读有关 @GlideExtensions here 的文档以及两者之间的区别:

  1. @GlideOption:用于添加自定义RequestOptions,只要第一个参数是从BaseRequestOptions<*>扩展而来的,在这种情况下RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
  2. @GlideType:肯定是创建不同类型的drawable,没有做太多配置,所以我没有尝试。

由于 RequestListener 没有 @FunctionalInterface 注释,我最终使用 KotlinJava 8 来实现 lambda-like 参数函数。

我创建了一个SafeGlideApp 类来处理与上下文相关的问题using RequestManager inside onCreatechecking if the activity isFinishing

class SafeGlideApp {

    companion object {

        private lateinit var rm: RequestManager
        private lateinit var target: ViewTarget<ImageView, Drawable>

        /**
         * This is mandatory to setup inside onCreate() on any Activity/Fragment
         * @param Activity receive the current activity if not finishing
         */
        @JvmStatic
        fun init(activity: Activity) {
            rm = Glide.with(activity)
        }

        @JvmStatic
        fun destroy() {
            target.clearOnDetach()
            rm.pauseAllRequests()
            rm.clear(target)
        }

        @JvmStatic
        fun with(activity: Activity, safeAction: (RequestManager) -> ViewTarget<ImageView, Drawable>) {
            init(activity)
            activity.safeRunnable {
                check(::rm.isInitialized)
                target = safeAction(rm)
            }
        }
    }
}

这是我想要在 SafeGlideApp 上调用自定义侦听器的函数。

Activity.java

SafeGlideApp.with(this, requestManager -> requestManager
                        .load(url)
                        .listener(
                                Helpers.wrapper(() -> doSomething()),
                                Helpers.wrapper(() -> doSomething())
                        )
                        .into(imageView)
);

为了在 kotlin-java 之间进行互操作,我创建了这个包装器,灵感来自于 stackoverflow 中的每个人。因此,在调用调用中,更加简洁。

Helpers.java

    /**
     * This lambda wrapper function takes nothing as argument and return Unit
     * E.g: f(wrapper(() -> doSomethingVoid()))
     *
     * @param callable
     * @return Unit
     */
    public static Function0<Unit> wrapper(Runnable callable) {
        return () -> {
            callable.run();
            return Unit.INSTANCE;
        };
    }

到目前为止,这是我自定义 RequestBuilder 的方法。

我的通用监听类

CustomListener.kt

fun <T : Any> makeLoadingRequestListener(
        failedAction: (() -> Unit)?,
        readyAction: (() -> Unit)?
): RequestListener<T> = object : RequestListener<T> {
    override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<T>?, isFirstResource: Boolean): Boolean {
        failedAction?.invoke()
        return false
    }

    override fun onResourceReady(resource: T, model: Any?, target: Target<T>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
        readyAction?.invoke()
        return false
    }
}

疑难解答

  • 使用显式类型 Drawable 会产生编译错误:
e: [kapt] An exception occurred: java.lang.IllegalArgumentException:
   @GlideOption methods must take a BaseRequestOptions<?> object as
   their first parameter, but the first parameter in
   MyAppGlideExtension#listener(RequestBuilder, Unit, Unit) is
   RequestBuilder<Drawable>

这是我的扩展类MyGlideExtension.kt

@GlideExtension
open class MyAppGlideExtension private constructor() {

    companion object {

        @JvmStatic
        @GlideOption
        fun listener(
                options: RequestBuilder<Drawable>,
                failedAction: () -> Unit,
                readyAction: () -> Unit
        ): BaseRequestOptions<Drawable> {
            return options.addListener(makeLoadingRequestListener<Drawable>(
                    failedAction = failedAction,
                    readyAction = readyAction
            ))
        }
    }
}

如本期#3552#4030 所述。 FlickrGlideExtension 示例使用 Java。另外,作者声明在 Kotlin 中使用 ? 通配符,实际上 Kotlin 不支持。

  • 我也试过这样的java方法,也有编译错误
@GlideExtension
public final class MyGlideExtension {

    private MyGlideExtension GlideExtension() {
        return this;
    }

    @GlideOption
    public static BaseRequestOptions<?> test(RequestBuilder<?> requestBuilder,
                                             Function0<Unit> failed,
                                             Function0<Unit> ready) {
        return requestBuilder.listener(new RequestListener<Object>() {
            @Override
            public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Object> target, boolean isFirstResource) {
                return false;
            }

            @Override
            public boolean onResourceReady(Object resource, Object model, Target<Object> target, DataSource dataSource, boolean isFirstResource) {
                return false;
            }
        });
    }
}
  • 很遗憾,我不知道如何使用投影来解决这个问题,RequestBuilder#addListener 函数的投影类型禁止
@GlideExtension
open class MyAppGlideExtension private constructor() {

    companion object {

        @JvmStatic
        @GlideOption
        fun listener(
                options: RequestBuilder<*>,
                failedAction: () -> Unit,
                readyAction: () -> Unit
        ): BaseRequestOptions<*> {
            return options.addListener(makeLoadingRequestListener<Object>(
                    failedAction = failedAction,
                    readyAction = readyAction
            ))
        }
    }
}
  • 手动扩展RequestManager & RequestBuilder
class CustomRequestManager(
        glide: Glide, lifecycle: Lifecycle,
        treeNode: RequestManagerTreeNode, context: Context
) : RequestManager(glide, lifecycle, treeNode, context) {

    override fun load(string: String?): CustomRequestBuilder<Drawable> {
        return super.load(string) as CustomRequestBuilder<Drawable>
    }
}

class CustomRequestBuilder<TranscodeType : Any>(
        glide: Glide,
        requestManager: RequestManager,
        transcodeClass: Class<TranscodeType>,
        context: Context
) : RequestBuilder<TranscodeType>(glide, requestManager, transcodeClass, context) {

    fun listener(failedAction: () -> Unit, readyAction: () -> Unit): RequestBuilder<TranscodeType> {
        return addListener(makeLoadingRequestListener<TranscodeType>(
                failedAction = failedAction,
                readyAction = readyAction
        ))
    }
}

但在转换 GlideApp.with(this) as CustomRequestManager 时失败并抛出编译错误:GlideRequest cannot be casted to CustomRequestManager

最后但并非最不重要。

  • 是否有任何方法可以扩展/自定义RequestBuilder 类?
  • 或者这是否可以在不扩展RequestBuilder 的情况下创建一个辅助类来使用 & 包裹RequestListener&lt;R&gt; 接口以使其正常工作?

【问题讨论】:

  • 已编辑:我认为,我不必经历所有这些并想到一个替代选项:1. fork the glide,并创建不同的 GlideExtensions 功能 2. 只需迁移到 kotlin(这将绝对符合我想要的功能方法)3.创建一个包装器方法(静态)来接收一个返回RequestListener&lt;Drawable&gt;的lambda
  • 错误是什么? “我也试过这样的java方法,也有编译错误”
  • @TWiStErRob addListener(RequestListener&lt;capture&lt;?&gt;&gt;) cannot be applied to (RequestListener&lt;Object&gt;)
  • 这很好,我想。听起来注释处理器通过了,但是由于类型错误,您的代码中有错误。

标签: java android kotlin android-glide appglidemodule


【解决方案1】:

我认为你需要从字面上理解错误信息:

    @JvmStatic
    @GlideOption
    fun listener(
            options: RequestBuilder<Drawable>,
            failedAction: () -> Unit,
            readyAction: () -> Unit
    ): BaseRequestOptions<Drawable> {

    @JvmStatic
    @GlideOption
    fun listener(
            options: RequestBuilder<*>, // no explicit type, wildcard only (=== Java's <?>)
            failedAction: () -> Unit,
            readyAction: () -> Unit
    ): BaseRequestOptions<*> {

    @JvmStatic
    @GlideOption
    fun listener(
            options: BaseRequestOptions<*>, // not a subtype, exactly that type
            failedAction: () -> Unit,
            readyAction: () -> Unit
    ): BaseRequestOptions<*> {

然后您可以将options 参数转换为RequestOptions 或生成的类的类型(如有必要)。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-28
  • 1970-01-01
  • 2023-03-24
  • 2021-10-25
  • 1970-01-01
  • 2021-09-24
相关资源
最近更新 更多