您的位置:首页 > 其它

Glide进阶详解(六)

2017-01-22 21:49 411 查看


Glide 中的回调:Targets

目前为止,我们很方便的使用 Glide 建造者去加载图片到
ImageView
中了。Glide 隐藏了一大堆复杂的在后台的场景。Glide
做了所有的网络请求和处理在后台线程中,一旦结果准备好了之后,切回到 UI 线程然后更新
ImageView


在这篇博客中,我们假定
ImageView
不再是图像的最后一步。我们只要 Bitmap 本身。Glide 提供了一个用
Targets
的简单的方式去接受图片资源的
Bitmap。Targets 是没有任何别的回调,它在 Glide 做完所有的加载和处理之后返回结果。

Glide 提供了各种的 targets 并且每个都有其明确的目的。我们将在接下来的几节中通过使用它们。让我们从
SimpleTarget
开始。


SimpleTarget

看如下代码实例:

private SimpleTarget target = new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
// do something with the bitmap
// for demonstration purposes, let's just set it to an ImageView
imageView1.setImageBitmap( bitmap );
}
};

private void loadImageSimpleTarget() {
Glide
.with( context ) // could be an issue!
.load( eatFoodyImages[0] )
.asBitmap()
.into( target );
}


这段代码的第一部分创建了一个字段对象,声明了一个方法,即一旦 Glide 已加载并处理完图像,它将被调用。这个回调方法传了 Bitmap 作为一个参数。你之后便可以使用这个 Bitmap 对象,无论你要怎样用它。

这段代码的第二部分是我们如何通过 Glide 用 targets:和
ImageView
用法完全相同的!你既可以传一个
Target 也可以传一个
ImageView
参数给
.into()
方法。Glide
自己将会处理并返回结果给任何一个。这里有一些不同的是,我们添加了
.asBitmap()
,它强制 Glide 去返回一个
Bitmap
对象。记住,Glide
也可以加载 Gif 或 video 的。为了防止 target 的冲突(我们需要 Bitmap) 和未知资源在网络背后的 URL(可能是一个 Gif),我们可以调用
.asBitmap()
告诉
Glide 我们需要一个图像。


关注 Targets

除了知道如何实现一个简单版本的 Glide 的 Target 回调系统,你要学会额外两件事。

首先是
SimpleTarget
对象的字段声明。从技术上来说,Java/Android 会允许你在
.into()
方法中去声明
target 的匿名内部类。然而,这大大增加了这样一个可能性:即在 Glide 做完图片请求之前, Android 垃圾回收移除了这个匿名内部类对象。最终这可能会导致一个情况,当图像加载完成了,但是回调再也不会被调用。所请确保你所声明的回调对象是作为一个字段对象的,这样你就可以保护它避免被邪恶的 Android 垃圾回收机制回收 ;-)

第二个关键部分是 Glide 建造者中这行:
.with(context)
。 这里的问题实际是 Glide 的功能:当你传了一个
context,例如是当前应用的 activity,Glide 将会自动停止请求当请求的 activity 已经停止的时候。这整合到了应用的生命周期中通常是非常有帮助的,但是有时工作起来是困难的,如果你的 target 是独立于应用的 activity 生命周期。这里的解决方案是用 application 的 context:
.with(context.getApplicationContext))
。当应用资深完全停止时,Glide
才会杀死这个图片请求。请求记住,再说一次,如果你的请求需要在 activity 生命周期之外去做时,才用下面这样的代码:

private void loadImageSimpleTargetApplicationContext() {
Glide
.with( context.getApplicationContext() ) // safer!
.load( eatFoodyImages[1]
.asBitmap()
.into( target2 );
}



Target 指定尺寸

另一个潜在的问题是,target 没有指明大小。如果你你传一个
ImageView
作为参数给
.into()
,Glide
将会用
ImageView
的大小去限制图像的大小。比如说,如果加载的图片是 1000x1000 像素的,但是
ImageView
只有
250x250 像素,Glide 将会减少图片的尺寸去节省时间和内存。很显然,在和 target 协作的时候并没有这么做,因为我们并没有已知的大小。然而,如果你有一个指定的大小,你可以提高回调。如果你知道这种图片应该要多大,你应该在你的回调声明中指定它以节省一些内存。

private SimpleTarget target2 = new SimpleTarget<Bitmap>( 250, 250 ) {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
imageView2.setImageBitmap( bitmap );
}
};

private void loadImageSimpleTargetApplicationContext() {
Glide
.with( context.getApplicationContext() ) // safer!
.load( eatFoodyImages[1] )
.asBitmap()
.into( target2 );
}



ViewTarget

我们不能直接使用
ImageView
的原因可能是多种多样的。我们已经向你展示如果去接收一个 Bitmap。现在我们要更进一步。假设你有一个 Custom
View。Glide 并不支持加载图片到自定义 view 中,因为并没有方法知道图片应该在哪里被设置。然而,Glide 可以用
ViewTarget
更容易实现。

让我们看一个简单的自定义 View,它继承自
FrameLayout
并内部使用了一个
ImageView
以及覆盖了一个
TextView


public class FutureStudioView extends FrameLayout {
ImageView iv;
TextView tv;

public void initialize(Context context) {
inflate( context, R.layout.custom_view_futurestudio, this );

iv = (ImageView) findViewById( R.id.custom_view_image );
tv = (TextView) findViewById( R.id.custom_view_text );
}

public FutureStudioView(Context context, AttributeSet attrs) {
super( context, attrs );
initialize( context );
}

public FutureStudioView(Context context, AttributeSet attrs, int defStyleAttr) {
super( context, attrs, defStyleAttr );
initialize( context );
}

public void setImage(Drawable drawable) {
iv = (ImageView) findViewById( R.id.custom_view_image );

iv.setImageDrawable( drawable );
}
}


你不能使用常规的 Glide 的方法
.into()
,因为我们的自定义 view 并不继承自
ImageView
。因此,我们必须创建一个
ViewTarget
,并用
.into()
方法:

private void loadImageViewTarget() {
FutureStudioView customView = (FutureStudioView) findViewById( R.id.custom_view );

viewTarget = new ViewTarget<FutureStudioView, GlideDrawable>( customView ) {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
this.view.setImage( resource.getCurrent() );
}
};

Glide
.with( context.getApplicationContext() ) // safer!
.load( eatFoodyImages[2] )
.into( viewTarget );
}


在 target 回调方法中,我们使用我们创建的方法
setImage(Drawable drawable)
在自定义
view 类中去设置图片。另外确保你注意到我们必须在
ViewTarget
的构造函数中传递我们自定义 view 作为参数:
new
ViewTarget<FutureStudioView, GlideDrawable>(customView)


这应该涵盖了所有你需要的自定义 view。你也可以在回调中添加额外的工作。如,我们可以分析传入的 Bitmap 的主要的颜色并设置十六进制值给
TextView
。但我们相信你应该已经有一些想法了。


加载图片到 Notifications



通知栏图标对用户来说是重要的上下文。用 NotificationCompat.Builder 来直接设置大的通知图片,但是图像必须以
Bitmap 的形式。如果图片在手机上已经是可用的,这并没什么问题。然而,如果图片斌不在设备上并且需要从网上加载的话,使用标准的方式来处理就变得不可能了。

让 Glide 来做吧。上篇博客中,我们看了如何用
SimpleTarget
将图片以 Bitmap 的形式下载下来。理论上说,你可以利用这种方式去加载图片到你的通知栏中。但这并不是必须的,因为
Glide 提供了一个更加方便舒适的方式:
NotificationTarget


NotificationTarget

所以,让我们来看代码。现在你知道 Glide target 是如何工作的了,因此我们不会再去用它了。为了显示一张大图片在通知栏,你可以使用
RemoteViews
并显示一个自定义的通知栏。



我们自定义的通知栏比较简单:

<?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:background="@android:color/white"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="2dp">
<ImageView
android:id="@+id/remoteview_notification_icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginRight="2dp"
android:layout_weight="0"
android:scaleType="centerCrop"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/remoteview_notification_headline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textSize="12sp"/>
<TextView
android:id="@+id/remoteview_notification_short_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:paddingBottom="2dp"
android:singleLine="true"
android:textSize="14sp"
android:textStyle="bold"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>


下面的代码用了上面的布局文件为我们创建了一个自定义通知。

final RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.remoteview_notification);

rv.setImageViewResource(R.id.remoteview_notification_icon, R.mipmap.future_studio_launcher);

rv.setTextViewText(R.id.remoteview_notification_headline, "Headline");
rv.setTextViewText(R.id.remoteview_notification_short_message, "Short Message");

// build notification
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.mipmap.future_studio_launcher)
.setContentTitle("Content Title")
.setContentText("Content Text")
.setContent(rv)
.setPriority( NotificationCompat.PRIORITY_MIN);

final Notification notification = mBuilder.build();

// set big content view for newer androids
if (android.os.Build.VERSION.SDK_INT >= 16) {
notification.bigContentView = rv;
}

NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(NOTIFICATION_ID, notification);


这个代码片段为我们创建了三个重要的对象,
notification
RemoteViews
以及常量
NOTIFICATION_ID。我们会需要这些去创建一个通知 target。

private NotificationTarget notificationTarget;

...

notificationTarget = new NotificationTarget(
context,
rv,
R.id.remoteview_notification_icon,
notification,
NOTIFICATION_ID);


最后,我们要调用 Glide,正如我们之前博客所做的,将 target 作为
.into()
的参数。

Glide
.with( context.getApplicationContext() ) // safer!
.load( eatFoodyImages[3] )
.asBitmap()
.into( notificationTarget );



App Widgets

让我们来看另一个 Glide target。 应用小部件一直以来都是 Android 的一部分。如果你的 App 提供了小部件并且包含图像,这部分应该会让你感兴趣的。 Glide 的AppWidgetTarget 能显著的让你非常简单的实现。

来看看一个简单的
AppWidgetProvider
实例:

public class FSAppWidgetProvider extends AppWidgetProvider {

private AppWidgetTarget appWidgetTarget;

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {

RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.custom_view_futurestudio);

appWidgetTarget = new AppWidgetTarget( context, rv, R.id.custom_view_image, appWidgetIds );

Glide
.with( context.getApplicationContext() ) // safer!
.load( GlideExampleActivity.eatFoodyImages[3] )
.asBitmap()
.into( appWidgetTarget );

pushWidgetUpdate(context, rv);
}

public static void pushWidgetUpdate(Context context, RemoteViews rv) {
ComponentName myWidget = new ComponentName(context, FSAppWidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(myWidget, rv);
}
}


几行重要的代码声明了
appWidgetTarget
对象以及 Glide 的建造者。这里的好处是,你不需要去定制
AppWidgetTarget
并重写任何
AppWidgetTarget
方法。Glide
都自动帮你做好了。太棒了!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: