可绘制对象概览

试用 Compose 方式
Jetpack Compose 是推荐在 Android 设备上使用的界面工具包。了解如何在 Compose 中显示图形。
<ph type="x-smartling-placeholder"></ph> 正在加载图片 →

如果您需要在应用中显示静态图片,可以使用 Drawable 类及其子类绘制形状和 图片。Drawable可以绘制的东西。各种子类有助于处理特定图片 您可以扩展这些场景,以定义您自己的可绘制对象 行为方式独特的应用

除了使用类构造函数之外,还可以通过另外两种方法定义和实例化 Drawable

  • 扩充保存在项目中的图片资源(位图文件)。
  • 扩充用于定义可绘制属性的 XML 资源。

注意: 您可能更喜欢使用矢量可绘制对象,它定义了一个具有一组 以及相关的颜色信息。这样便可使用矢量可绘制对象 可以针对不同的尺寸进行缩放,而不会有损图片质量有关详情,请参阅矢量 可绘制对象概览

通过资源图片创建可绘制对象

您可以通过引用来自应用的图片文件 项目资源。支持的文件类型包括 PNG(首选)、JPG(可接受)、 和 GIF(不推荐)。应用图标、徽标和其他图形(例如使用的图像) 非常适合采用这种方法。

如需使用图片资源,请将您的文件添加到 res/drawable/ 目录进入您的项目后,您就可以引用该图片 从代码或 XML 布局中获取。无论采用哪种方式,都会引用 资源 ID,即不含文件类型扩展名的文件名。对于 例如,请使用 my_image 指代 my_image.png

注意:放置在 res/drawable/ 目录可以使用 在构建期间由 aapt 工具进行无损图像压缩 过程。例如,不需要超过 256 种颜色的真彩色 PNG 可以使用调色板转换为 8 位 PNG。这会生成 但需要的内存更少因此,图片二进制文件 放置到此目录中的所有资源在构建时可能会发生变化。如果您打算阅读 比特流格式的图片,以便将其转换为位图,那么请将您的图片放入 res/raw/ 文件夹,其中 aapt 工具不支持 修改它们。

以下代码段演示了如何构建使用ImageView 从可绘制资源创建的图片并将其添加到布局中:

Kotlin

private lateinit var constraintLayout: ConstraintLayout

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // Instantiate an ImageView and define its properties
    val i = ImageView(this).apply {
        setImageResource(R.drawable.my_image)
        contentDescription = resources.getString(R.string.my_image_desc)

        // set the ImageView bounds to match the Drawable's dimensions
        adjustViewBounds = true
        layoutParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT)
    }

    // Create a ConstraintLayout in which to add the ImageView
    constraintLayout = ConstraintLayout(this).apply {

        // Add the ImageView to the layout.
        addView(i)
    }

    // Set the layout as the content view.
    setContentView(constraintLayout)
}

Java

ConstraintLayout constraintLayout;

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Create a ConstraintLayout in which to add the ImageView
  constraintLayout = new ConstraintLayout(this);

  // Instantiate an ImageView and define its properties
  ImageView i = new ImageView(this);
  i.setImageResource(R.drawable.my_image);
  i.setContentDescription(getResources().getString(R.string.my_image_desc));

  // set the ImageView bounds to match the Drawable's dimensions
  i.setAdjustViewBounds(true);
  i.setLayoutParams(new ViewGroup.LayoutParams(
          ViewGroup.LayoutParams.WRAP_CONTENT,
          ViewGroup.LayoutParams.WRAP_CONTENT));

  // Add the ImageView to the layout and set the layout as the content view.
  constraintLayout.addView(i);
  setContentView(constraintLayout);
}

在其他情况下,您可能需要将图片资源作为 Drawable 对象进行处理,如下所示 示例:

Kotlin

val myImage: Drawable = ResourcesCompat.getDrawable(context.resources, R.drawable.my_image, null)

Java

Resources res = context.getResources();
Drawable myImage = ResourcesCompat.getDrawable(res, R.drawable.my_image, null);

警告:项目中的每个唯一资源 只能维持一种状态,不管有多少个不同的对象, 实例化。例如,如果您实例化同一图片资源中的两个 Drawable 对象,并 更改一个对象的属性(例如 alpha),那么这也会影响 另一个。处理一个图片资源的多个实例时,应改为 直接转换 Drawable 对象,您应该执行 补间 动画

以下 XML 代码段展示了如何在 XML 布局中向 ImageView 添加可绘制资源:

<ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/my_image"
        android:contentDescription="@string/my_image_desc" />

如需详细了解如何使用项目资源,请参阅资源

注意:使用图片资源作为可绘制对象的来源时, 确保图像的尺寸适合各种像素密度。如果 图片不正确,它们将被放大以适应,这可能会导致可绘制对象出现伪影。 有关详情,请参阅支持不同的 像素密度

通过 XML 资源创建可绘制对象

如果存在 Drawable 对象,该对象最初并不依赖于 代码或用户互动,那么在 XML 中定义 Drawable 是不错的选择。均匀 如果您希望 Drawable 在用户互动期间更改其属性 应用,则应考虑在 XML 中定义对象,因为您可以在之后修改属性, 该对象已被实例化。

在 XML 中定义 Drawable 后,将文件保存在 res/drawable/ 目录中。以下示例展示了 定义了 TransitionDrawable 资源,该资源继承自 Drawable

<!-- res/drawable/expand_collapse.xml -->
<transition xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/image_expand"/>
    <item android:drawable="@drawable/image_collapse"/>
</transition>

然后,通过调用以下代码来检索和实例化该对象 Resources#getDrawable() 并传递 XML 文件的资源 ID。不限 Drawable 子类 支持 inflate() 方法的应用,可在 XML 中定义并实例化 。

<ph type="x-smartling-placeholder">

每个支持 XML 膨胀的可绘制对象类都使用特定的 XML 属性 帮助定义对象属性以下代码将 TransitionDrawable 并将其设置为 ImageView 对象:

Kotlin

val transition= ResourcesCompat.getDrawable(
        context.resources,
        R.drawable.expand_collapse,
        null
) as TransitionDrawable

val image: ImageView = findViewById(R.id.toggle_image)
image.setImageDrawable(transition)

// Description of the initial state that the drawable represents.
image.contentDescription = resources.getString(R.string.collapsed)

// Then you can call the TransitionDrawable object's methods.
transition.startTransition(1000)

// After the transition is complete, change the image's content description
// to reflect the new state.

Java

Resources res = context.getResources();
TransitionDrawable transition =
    (TransitionDrawable) ResourcesCompat.getDrawable(res, R.drawable.expand_collapse, null);

ImageView image = (ImageView) findViewById(R.id.toggle_image);
image.setImageDrawable(transition);

// Description of the initial state that the drawable represents.
image.setContentDescription(getResources().getString(R.string.collapsed));

// Then you can call the TransitionDrawable object's methods.
transition.startTransition(1000);

// After the transition is complete, change the image's content description
// to reflect the new state.

如需详细了解所支持的 XML 属性,请参阅 。

形状可绘制对象

ShapeDrawable 对象是个不错的选择 需要动态绘制二维图形时。您可以 以编程方式在 ShapeDrawable 对象上绘制基元形状 并应用您的应用所需的样式。

ShapeDrawableDrawable 的子类。因此,您可以使用 ShapeDrawable 任何需要 Drawable 的地方。对于 例如,您可以使用 ShapeDrawable 对象设置背景 方法是将视图传递给视图的 setBackgroundDrawable() 方法。您还可以将形状作为 自定义视图,并将其添加到应用的布局中。

由于 ShapeDrawable 有自己的 draw() 方法,因此您可以创建 View 的子类,用于绘制 ShapeDrawableonDraw() 事件期间发生的事件, 以下代码示例:

Kotlin

class CustomDrawableView(context: Context) : View(context) {
    private val drawable: ShapeDrawable = run {
        val x = 10
        val y = 10
        val width = 300
        val height = 50
        contentDescription = context.resources.getString(R.string.my_view_desc)

        ShapeDrawable(OvalShape()).apply {
            // If the color isn't set, the shape uses black as the default.
            paint.color = 0xff74AC23.toInt()
            // If the bounds aren't set, the shape can't be drawn.
            setBounds(x, y, x + width, y + height)
        }
    }

    override fun onDraw(canvas: Canvas) {
        drawable.draw(canvas)
    }
}

Java

public class CustomDrawableView extends View {
  private ShapeDrawable drawable;

  public CustomDrawableView(Context context) {
    super(context);

    int x = 10;
    int y = 10;
    int width = 300;
    int height = 50;
    setContentDescription(context.getResources().getString(
            R.string.my_view_desc));

    drawable = new ShapeDrawable(new OvalShape());
    // If the color isn't set, the shape uses black as the default.
    drawable.getPaint().setColor(0xff74AC23);
    // If the bounds aren't set, the shape can't be drawn.
    drawable.setBounds(x, y, x + width, y + height);
  }

  protected void onDraw(Canvas canvas) {
    drawable.draw(canvas);
  }
}

您可以使用代码示例中的 CustomDrawableView 类 就像使用任何其他自定义视图一样。例如,您可以 以编程方式将其添加到应用中的 activity,如下所示 示例:

Kotlin

private lateinit var customDrawableView: CustomDrawableView

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    customDrawableView = CustomDrawableView(this)

    setContentView(customDrawableView)
}

Java

CustomDrawableView customDrawableView;

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  customDrawableView = new CustomDrawableView(this);

  setContentView(customDrawableView);
}

如果您想在 XML 布局中使用自定义视图,则 CustomDrawableView 类必须替换 View(Context, AttributeSet) 构造函数,系统会在该类 从 XML 扩充而来。以下示例展示了如何声明 XML 布局中的 CustomDrawableView

<com.example.shapedrawable.CustomDrawableView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />

ShapeDrawable 类与许多其他类一样 android.graphics.drawable 软件包中的可绘制对象类型,可让您 使用公共方法定义对象的各种属性。一些示例 例如透明度、色彩滤镜 抖动、不透明度和颜色

您还可以使用 XML 资源定义基元可绘制形状。有关 信息,请参阅 中的形状可绘制对象 可绘制资源类型。

NinePatch 可绘制对象

NinePatchDrawable 图形是 可用作视图背景的可拉伸位图图像。Android 设备 自动调整图形大小以适应视图内容。一个 NinePatch 图像示例是标准 Android 按钮——按钮必须拉伸以适应各种长度的字符串。答 NinePatch 图形是标准 PNG 图片,包含一个额外的 1 像素边框。 必须使用 9.png 扩展名保存在 res/drawable/ 目录中。

使用边框定义图片的可拉伸区域和静态区域。 您可以通过绘制一个(或多个)1 像素宽的部分来指定可拉伸部分 边框左侧和顶部的黑线(其他边框像素) 应为完全透明或白色)。您可以设置任意数量的可拉伸部分 。可拉伸部分的相对尺寸保持不变, 最大的部分始终是最大的部分。

您还可以定义图片的可选可绘制部分(实际上, 添加内边距线)。如果 View 对象将 NinePatch 图形设置为其背景 然后指定视图的文本,它会拉伸自身,以便所有文本 仅占据由右侧和底部线条指定的区域(如果有的话)。 如果没有添加内边距行,Android 会使用左侧和顶部的行 定义这个可绘制区域

为了明确区分各线条,左侧和顶部的线条定义 允许复制图片的哪些像素 图片。底线和右边线定义了图片中的相对区域, 视图的内容会被占用。

图 1 显示了定义按钮的 NinePatch 图形示例:

可拉伸区域的图片
和内边距框

图 1:NinePatch 图形示例 用于定义按钮的

此 NinePatch 图形定义了一个可拉伸区域,该区域左侧和顶部均位于右侧 绘制区域包含底部和右侧的线条。在上面的图片中 灰色虚线表示图片被复制的区域 以便拉伸图像底部图片中的粉色矩形标识 允许视图内容的区域。如果内容 系统就会拉伸图片,使其适合这个区域

Draw 9-patch 工具提供 使用 WYSIWYG(所见即所得)图形创建 NinePatch 图像的便捷方式 编辑器。如果您针对可拉伸区域定义的区域 区域因像素而产生的绘制伪影的风险 复制。

以下示例布局 XML 演示了如何添加 NinePatch 图形 几个按钮NinePatch 图像将保存到 res/drawable/my_button_background.9.png

<Button android:id="@+id/tiny"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerInParent="true"
        android:text="Tiny"
        android:textSize="8sp"
        android:background="@drawable/my_button_background"/>

<Button android:id="@+id/big"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerInParent="true"
        android:text="Biiiiiiig text!"
        android:textSize="30sp"
        android:background="@drawable/my_button_background"/>

请注意,layout_widthlayout_height 属性设为 wrap_content,让按钮整齐地显示。 文本。

图 2 显示了通过 XML 和 NinePatch 图像渲染的两个按钮 如上图所示请注意按钮的宽度和高度如何随文本变化, 而背景图片会拉伸以适应该屏幕。

“小和”的图片
正常大小的按钮

图 2:使用 XML 呈现的按钮 以及一个 NinePatch 图形

自定义可绘制对象

如果您想创建一些自定义可绘制对象,可以通过扩展 Drawable 类(或其任何子类)来实现。

最重要的实现方法是 draw(Canvas) 因为这提供了 Canvas 对象,您在提供 您的绘图说明。

以下代码展示了 Drawable 的一个简单子类 :

Kotlin

class MyDrawable : Drawable() {
    private val redPaint: Paint = Paint().apply { setARGB(255, 255, 0, 0) }

    override fun draw(canvas: Canvas) {
        // Get the drawable's bounds
        val width: Int = bounds.width()
        val height: Int = bounds.height()
        val radius: Float = Math.min(width, height).toFloat() / 2f

        // Draw a red circle in the center
        canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, redPaint)
    }

    override fun setAlpha(alpha: Int) {
        // This method is required
    }

    override fun setColorFilter(colorFilter: ColorFilter?) {
        // This method is required
    }

    override fun getOpacity(): Int =
        // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE
        PixelFormat.OPAQUE
}

Java

public class MyDrawable extends Drawable {
    private final Paint redPaint;

    public MyDrawable() {
        // Set up color and text size
        redPaint = new Paint();
        redPaint.setARGB(255, 255, 0, 0);
    }

    @Override
    public void draw(Canvas canvas) {
        // Get the drawable's bounds
        int width = getBounds().width();
        int height = getBounds().height();
        float radius = Math.min(width, height) / 2;

        // Draw a red circle in the center
        canvas.drawCircle(width/2, height/2, radius, redPaint);
    }

    @Override
    public void setAlpha(int alpha) {
        // This method is required
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        // This method is required
    }

    @Override
    public int getOpacity() {
        // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE
        return PixelFormat.OPAQUE;
    }
}

然后,您可以将可绘制对象添加到任意位置,例如添加到 ImageView,如下所示:

Kotlin

val myDrawing = MyDrawable()
val image: ImageView = findViewById(R.id.imageView)
image.setImageDrawable(myDrawing)
image.contentDescription = resources.getString(R.string.my_image_desc)

Java

MyDrawable mydrawing = new MyDrawable();
ImageView image = findViewById(R.id.imageView);
image.setImageDrawable(mydrawing);
image.setContentDescription(getResources().getString(R.string.my_image_desc));

在 Android 7.0(API 级别 24)及更高版本中,您还可以定义自定义可绘制对象的实例 与 XML 相关联:

  • 将完全限定类名用作 XML 元素名称。对于此方法,自定义的 可绘制对象类必须是顶级公共类:
    <com.myapp.MyDrawable xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="#ffff0000" />
    
  • 使用 drawable 作为 XML 标记名称并指定完全限定类 name。此方法可用于公共顶级类和 公共静态内部类:
    <drawable xmlns:android="http://schemas.android.com/apk/res/android"
        class="com.myapp.MyTopLevelClass$MyDrawable"
        android:color="#ffff0000" />
    
。 <ph type="x-smartling-placeholder">

向可绘制对象添加色调

在 Android 5.0(API 级别 21)及更高版本中,您可以对定义为以下元素的位图和九宫格着色: alpha 蒙版。您可以使用颜色资源或解析为颜色的主题属性为其着色 资源(例如 ?android:attr/colorPrimary)。这类素材资源通常由您自己 并自动为其上色以符合您的主题。

您可以为 BitmapDrawableNinePatchDrawableVectorDrawable 着色 与 setTint() 方法搭配使用。您可以 还使用 android:tintandroid:tintMode 属性。

从图片中萃取突出颜色

Android 支持库包含 Palette 类,可让您从图片中提取突出颜色。 您可以将可绘制对象作为 Bitmap 加载,并将其传递给 Palette 以访问其颜色。 有关详情,请参阅选择颜色 Palette API