廖祜秋 · 更新于 2018-11-28 11:00:42

自定义View

本教程内容来源于:http://fresco-cn.org
采用 知识共享 署名 4.0 国际 许可协议 进行许可

DraweeHolders

总有一些时候,DraweeViews是满足不了需求的,在展示图片的时候,我们还需要展示一些其他的内容,或者支持一些其他的操作。在同一个View里,我们可能会想显示一张或者多张图。

在自定义View中,Fresco 提供了两个类来负责图片的展现:

  • DraweeHolder 单图情况下用。
  • MultiDraweeHolder 多图情况下用。

自定义View需要完成的事情

Android 呈现View对象,只有View对象才能得到一些系统事件的通知。DraweeViews 处理这些事件通知,高效地管理内存。使用DraweeHolder时,你需要自己实现这几个方法。

处理 attach/detach 事件

如果没按照以下步骤实现的话,很可能会引起内存泄露

当图片不再在View上显示时,比如滑动时View滑动到屏幕外,或者不再绘制,图片就不应该再存在在内存中。Drawees 监听这些事情,并负责释放内存。当图片又需要显示时,重新加载。

这些在DraweeView中是自动的,但是在自定义View中,需要我们自己去操作,如下:

DraweeHolder mDraweeHolder;

@Override
public void onDetachedFromWindow() {
  super.onDetachedToWindow();
  mDraweeHolder.onDetach();
}

@Override
public void onStartTemporaryDetach() {
  super.onStartTemporaryDetach();
  mDraweeHolder.onDetach();
}

@Override
public void onAttachedToWindow() {
  super.onAttachedToWindow();
  mDraweeHolder.onAttach();
}

@Override
public void onFinishTemporaryDetach() {
  super.onFinishTemporaryDetach();
  mDraweeHolder.onAttach();
}

处理触摸事件

如果你启用了点击重新加载,在自定义View中,需要这样:

@Override
public boolean onTouchEvent(MotionEvent event) {
  return mDraweeHolder.onTouchEvent(event) || super.onTouchEvent(event);
}

自定义onDraw

Drawable drawable = mDraweeHolder.getHierarchy().getTopLevelDrawable();
drawable.setBounds(...);

否则图片将不会出现

  • 不要向下转换这个Drawable
  • 不要变换这个Drawable

其他应该做的

  • 重写 verifyDrawable:
@Override
protected boolean verifyDrawable(Drawable who) {
  if (who == mDraweeHolder.getHierarchy().getTopLevelDrawable()) {
    return true;
  }
  // 对其他Drawable的验证逻辑
}
  • 确保invalidateDrawable 处理了图片占用的那块区域。

创建 DraweeHolder

这同样需要非常小心和细致

构造函数

我们推荐如下实现构造函数:

  • 重写3个构造函数
  • 在每个构造函数中调用同等签名的父类构造函数,和一个私有的init方法。
  • init方法中执行初始化操作。

即,不要在构造函数中用this来调用另外一个构造。

这样可以保证,不管调用哪个构造,都可以正确地执行初始化流程。然后在init方法中创建holder。

创建 Holder

如果有可能,只在View创建时,创建Drawees。创建DraweeHierarchy开销较大,最好只做一次。

class CustomView extends View {
  DraweeHolder<GenericDraweeHierarchy> mDraweeHolder;

  // constructors following above pattern

  private void init() {
    GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources());
      .set...
      .set...
      .build();
    mDraweeHolder = DraweeHolder.create(hierarchy, context);
  }
}

设置要显示的图片

使用controller builder创建DraweeController,然后调用holder的setController方法,而不是设置给自定义View。

DraweeController controller = Fresco.newControllerBuilder()
    .setUri(uri)
    .setOldController(mDraweeHolder.getController())
    .build();
mDraweeHolder.setController(controller);

MultiDraweeHolder

DraweeHolder相比,MultiDraweeHolderadd, remove, clear 等方法可以操作Drawees。如下:

MultiDraweeHolder<GenericDraweeHierarchy> mMultiDraweeHolder;

private void init() {
  GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources());
    .set...
    .build();
  mMultiDraweeHolder = new MultiDraweeHolder<GenericDraweeHierarchy>();
  mMultiDraweeHolder.add(new DraweeHolder<GenericDraweeHierarchy>(hierarchy, context));
  // repeat for more hierarchies
}

同样,也需要处理系统事件,设置声音等等,就想处理单个DraweeHolder那样。

上一篇: 图片请求 下一篇: 一些陷阱