1

我正在创建一个复合自定义控件。该控件由底部的单个元素和顶部的未知数量的元素组成。这些元素中的每一个的背景都是一个 StateListDrawable,它定义了用于该元素的每个状态的 NinePatchDrawables。

当各个顶部元素更改其宽度以适应其内容时,正下方的底部元素区域也需要更改宽度以适应其内容。顶部元素的位置也可能向左或向右移动。

控制布局模型

上面和下面的图像只是快速的模型,用于传达我需要实现的布局和行为。我不负责设计要使用的实际背景图像,但它们不会是统一的平面灰色。

期望的拉伸行为

我设法通过以下方式解决了这个重新调整大小问题的第一部分:

  • 创建一个扩展 Drawable 的自定义类。
  • 向类传递一个 NinePatchDrawables 数组,定义底部视图的各种潜在区域(例如 left_end、right_end、middle_below_control、middle_below_gap)。
  • 在自定义类中更改每个边界以匹配上面的相关视图' onBoundChange()
  • 将我调整大小的 NinePatches 数组绘制到onDraw().
  • 根据数组中 NinePatches 的填充设置底部视图的组合填充。

但是,此解决方案引发了许多问题:

  • 您需要为底部视图的每个潜在区域提供 StateListDrawables。
  • 您需要为每个 StateListDrawables 中的每个状态提供单独的 NinePatch 图像。
  • 您必须为底部视图设计一个图像,该图像被限制为在其宽度上非常均匀。
  • 你需要护耳器和厚脸皮来应对平面设计师的所有呼喊。

我真正需要实现的是:

  • 底部视图有一个 StateListDrawable。
  • 为每个状态提取单个图像并将其分割成所需的部分。
  • 为每个切片创建一个新的 NinePatchDrawable,应用上面视图中的数据块仅用于宽度。不是身高。
  • 然后按照我上面之前的解决方案重新组合它们。

不幸的是,我们知道,您不能真正使用数据块。它是在编译时从源图像生成的,以创建 NinePatch 二进制文件。

可以在此处找到有关此类主题的更好答案之一。基于此,一个可能的折衷解决方案可能是:

  • 对于每个顶部元素类型,让图形设计师提供一个额外的空白虚拟 NinePatch 源,具有相同的高度和宽度尺寸、相同的宽度填充和补丁定义,但具有他们想要的底部视图的高度填充和补丁定义。
  • getNinePatchChunk()在相关的虚拟 NinePatch 编译资源上使用。
  • 将生成的数据块与相关的底部视图位图切片结合起来,使用NinePatch(Bitmap bitmap, byte[] chunk, String srcName).
  • 忍受平面设计师的肮脏表情,但至少他们已经停止大喊大叫了。

这似乎是一种笨拙的做事方式。有没有更好、更有效的方法来实现所有必需的目标?

注意:当在应用程序中使用时,在滚动视图中可能有很多此自定义控件的实例,因此需要将嵌套布局保持在最低限度。

4

1 回答 1

0

我终于想出了一种方法来避免在上面的折衷解决方案中必要时使用空白的虚拟 NinePatch 源。

基于对类似问题的出色解决方案,我编写了一个实用方法来从 NinePatch 资源中提取填充信息。使用它,我可以从相邻的 NinePatch 区域中提取必要的填充,并将它们应用于我的新切片。希望这对其他人有帮助。

public static Rect getNinePatchPadding(Context context, int drawableId) {
    Rect mPadding = new Rect();
    Bitmap bm = BitmapFactory.decodeResource(context.getResources(), drawableId);
    final byte[] chunk = bm.getNinePatchChunk();
    if (!NinePatch.isNinePatchChunk(chunk)) return null;
    ByteBuffer byteBuffer = ByteBuffer.wrap(chunk).order(ByteOrder.nativeOrder());

    // Skip to padding
    for (int i = 0; i < 12; i++) {
        byteBuffer.get();
    }

    mPadding.left = byteBuffer.getInt();
    mPadding.top = byteBuffer.getInt();
    mPadding.right = byteBuffer.getInt();
    mPadding.bottom = byteBuffer.getInt();

    return mPadding;
}
于 2013-10-03T11:55:44.223 回答