• 自定义Drawable


    我们看过一些博客文章,讲述了为什么要适时地使用自定义的view,以及它们是如何帮你正确地封装应用代码的。却少有人讨论这类思考如何转变为我们的app中与view无关的其它部分。

    在我开发的应用程序Fragment里,在一些地方我使用自定义的Drawable封装我的逻辑代码,就像使用自定义view一样。代码放在Gist

    用例

    在Fragment中,我们使用了水平方向的滑动条作为一些地方的可选的视图区域。这就意味着中央的图标是被选择的图标,每一项都能够以平滑移动的方式移进移出。由此,我们认为最好展现出一个优雅的过渡效果。

    example

    然而这并非完全必要。我认为这是一个效果,它使得运动更为流畅,并给应用增加了些许品味。我本可以设置多个图像view并单独呈现,但是这却是自定义drawable的最佳使用场景。

    自定义Drawable

    在Android中,Drawable实际上是很接近于View。它们有相似的方法获取布局的边距和边界,并且有可以被覆写的draw方法。在我的例子里,我需要在选中和未选中这两个基于值的drawable中实现平移效果。

    在我们的例子中,我们简单地创建出来包含了其他Drawables(含方向)的Drawable的子类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class RevealDrawable extends Drawable {
      public RevealDrawable(Drawable unselected, Drawable selected, int orientation) {
        this(null, null);
     
        mUnselectedDrawable = unselected;
        mSelectedDrawable = selected;
        mOrientation = orientation;
      }
    }

    接下来,我们需要做的就是设定一个值,用来标明drawable是选中的一栏。恰好Drawable有一个内置函数可以做到这一点,即setLevel(int)。

    一个Drawable的级别是从0到10000的整数值,这仅仅允许Drawable根据一个值来定义它的视图。在我们的例子中,我们可以简单地设定5000作为选中状态,0表示左侧完全未选中,10000表示右侧完全未选中。

    我们要做的就是重写draw(Canvas canvas)方法,根据当前level值裁剪canvas,从而绘制合适的drawable。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    @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();
          }
        }
      }
    }

    就这样,我们仅仅设置了基于滚动位置的水平方向的图标,这事就搞定了。

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

    如果你想看到这份自定义Drawable的源代码,你可以在Github的这里查看。

  • 相关阅读:
    (网络流)ACM Computer Factory --POJ --3436
    (小数化分数)小数化分数2 -- HDU --1717
    (小数化分数)小数化分数2 --HDU --1717
    (网络流 模板 Dinic) Drainage Ditches --POJ --1273
    (网络流 模板 Edmonds-Karp)Drainage Ditches --POJ --1273
    (匹配)Oil Skimming -- hdu --4185
    (匹配 二维建图) Antenna Placement --POJ --3020
    (匹配)Antenna Placement --POJ --3020
    将截断字符串或二进制数据【转】
    C#中Abstract和Virtual 【转】
  • 原文地址:https://www.cnblogs.com/lovewx/p/4478938.html
Copyright © 2020-2023  润新知