自定义 Custom Drawables

发布于 2024-09-05 22:52:51 字数 4098 浏览 37 评论 0

我们都看过关于为什么你应该适当的使 自定义 Views 和如何能帮助你正确的封装你的应用程序代码的帖子。但非视图相关的部分如何转化为我们 apps 的其他部分的这种思考方式,我们对此并不非常了解。

在我的应用 Fragment 中,有些地方我使用自定义 Drawables 来封装我的逻辑,就像你在 customView 中做的一样。

用例

在 Fragment 中,有一些使用水平滚动条作为一个选择视图的地方。这意味着该中心图标就是“选中”的图标,整个条目就该平滑的平移进去或平移出。为此,一个好的显示转换将非常棒。

虽然这并非完全必要,但我觉得它是一个能让这个滑动更加流畅并增加一个触摸的类在 app 上的效果。我本可以设置多个 imageviews 并让他们每个独立出来,但这真是使用自定义 drawables 的好地方~

自定义 Drawables

在 Android 里,Drawables 和 Views 实际上非常的相似。他们有相似的方法,例如 padding 和 bounds(layout),并且都有一个可以被重写的 draw 方法。就我而言,我需要能够在一个选中的图片和一个未选中的图片之间进行转换基础上的一个值。

在我们的例子中,我们简单地创建一个包含其他 Drawables(和方向) 的 Drawable 子类.

public class RevealDrawable extends Drawable {
  public RevealDrawable(Drawable unselected, Drawable selected, int orientation) {
    this(null, null);

    mUnselectedDrawable = unselected;
    mSelectedDrawable = selected;
    mOrientation = orientation;
  }
}

接下来,我们需要设定能够与图片选择过程中相关联的值。幸运的是 Drawable 内置了这种类型的事件,setLevel(int).

一个 Drawable 的 level 是介于 0 和 10000 的整数,它只是允许 Drawable 基于一个值去自定义它的 view.在我们的例子中,我们可以定义 5000 作为图片被选择时的状态值,其他没被选中状态值在 5000 左右两侧。 All we need to do now is to override the draw(Canvas canvas) method to draw the appropriate drawable by clipping the canvas based on the current level. 现在我们要做的就是重写 draw(Canvas canvas) 方法,通过基于当前的 level 裁剪画布去绘制相应的图片。

@Override
public void draw(Canvas canvas) {

  // If level == 10000 || level == 0, just draw the unselected image
  int level = getLevel();
  if (level == 10000 || level == 0) {
    mRevealState.mUnselectedDrawable.draw(canvas);
  }

  // If level == 5000 just draw the selected image
  else if (level == 5000) {
    mRevealState.mSelectedDrawable.draw(canvas);
  }

  // Else, draw the transitional version
  else {
    final Rect r = mTmpRect;
    final Rect bounds = getBounds();

    { // Draw the unselected portion
      float value = (level / 5000f) - 1f;
      int w = bounds.width();
      if ((mRevealState.mOrientation & HORIZONTAL) != 0) {
        w = (int) (w * Math.abs(value));
      }
      int h = bounds.height();
      if ((mRevealState.mOrientation & VERTICAL) != 0) {
        h = (int) (h * Math.abs(value));
      }
      int gravity = value < 0 ? Gravity.LEFT : Gravity.RIGHT;
      Gravity.apply(gravity, w, h, bounds, r);

      if (w > 0 && h > 0) {
        canvas.save();
        canvas.clipRect(r);
        mRevealState.mUnselectedDrawable.draw(canvas);
        canvas.restore();
      }
    }

    { // Draw the selected portion
      float value = (level / 5000f) - 1f;
      int w = bounds.width();
      if ((mRevealState.mOrientation & HORIZONTAL) != 0) {
        w -= (int) (w * Math.abs(value));
      }
      int h = bounds.height();
      if ((mRevealState.mOrientation & VERTICAL) != 0) {
        h -= (int) (h * Math.abs(value));
      }
      int gravity = value < 0 ? Gravity.RIGHT : Gravity.LEFT;
      Gravity.apply(gravity, w, h, bounds, r);

      if (w > 0 && h > 0) {
        canvas.save();
        canvas.clipRect(r);
        mRevealState.mSelectedDrawable.draw(canvas);
        canvas.restore();
      }
    }
  }
}

就这样,我们可基于滑动的位置以简单地设置 icon 的 level,结束了~

float offset = getOffestForPosition(recyclerView, position);
if (Math.abs(offset) <= 1f) {
  holder.image.setImageLevel((int) (offset * 5000) + 5000);
} else {
  holder.image.setImageLevel(0);
}

想要看自定义 Drawable 源码,请在 Github 上搜索 Gist here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

等风来

暂无简介

文章
评论
24 人气
更多

推荐作者

梦途

文章 0 评论 0

唐睦州

文章 0 评论 0

且行且努力

文章 0 评论 0

Yiu Peng

文章 0 评论 0

albertliao

文章 0 评论 0

逆夏时光

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文