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
都自动帮你做好了。太棒了!
相关文章推荐
- 多进程编程的优缺点
- ArrayList和LinkedList的深层次理解
- BZOJ 1054: [HAOI2008]移动玩具 BFS, Hash
- 博为峰Java技术文章 ——JavaSE Swing SpringLayout布局管理器I
- MUTT+MSMTP利用163服务器发送邮件
- 日文
- [故事]女博士在京辛酸买房记:同学想读博吗?先买个房吧
- Rectangles
- ActiveMQ入门实例
- 0122程序练习
- mysql5.7.14安装配置方法实操教程
- Glide进阶详解(五)
- 《Angular2初步涉略》
- 排队打水问题
- Log4j 日志环境搭建
- 【UML】用例图
- CodeForces - 527C Glass Carving (二分+set) 多维矩形切割问题
- CodeForces 59B
- Glide进阶详解(四)
- Codeforces 755D-PolandBall and Polygon