如果您需要在应用中显示静态图片,可以使用 Drawable
类及其子类绘制形状和图片。Drawable
是可绘制内容的一般抽象。各种子类有助于特定的图片场景,您也可以扩展这些子类,以定义自己的可绘制对象,并且这些对象的行为方式非常独特。
除了使用类构造函数之外,还可以通过另外两种方法定义和实例化 Drawable
:
- 扩充保存在项目中的图片资源(位图文件)。
- 扩充用于定义可绘制属性的 XML 资源。
注意:您可能更喜欢使用矢量可绘制对象,它通过一组点、线条和曲线以及相关颜色信息定义图片。这样,矢量可绘制对象就能针对不同的大小进行缩放,而不会有损图片质量。如需了解详情,请参阅矢量可绘制对象概览。
通过资源图片创建可绘制对象
您可以通过引用项目资源中的图片文件向应用添加图形。支持的文件类型包括 PNG(首选)、JPG(可接受)和 GIF(不建议)。此方法非常适合使用此方法。
如需使用图片资源,请将文件添加到项目的 res/drawable/
目录中。进入项目后,您可以从代码或 XML 布局中引用图片资源。无论采用哪种方式,都可以使用资源 ID 来引用资源 ID,资源 ID 是不带文件类型扩展名的文件名。例如,将 my_image.png
引用为 my_image
。
注意:在构建过程中,aapt
工具可能会使用无损图片压缩功能自动优化位于 res/drawable/
目录中的图片资源。例如,可以通过调色板将不需要超过 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/
目录中。以下示例展示了定义从 Drawable
继承的 TransitionDrawable
资源的 XML:
<!-- 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 来检索并实例化该对象。任何支持 inflate()
方法的 Drawable
子类均可在 XML 中定义并由您的应用实例化。
每个支持 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
对象上绘制基元形状,并应用应用所需的样式。
ShapeDrawable
是 Drawable
的子类。因此,您可以在需要 Drawable
的位置使用 ShapeDrawable
。例如,您可以使用 ShapeDrawable
对象设置视图背景,只需将其传递给视图的 setBackgroundDrawable()
方法即可。您还可以将形状绘制为自己的自定义视图,并将其添加到应用中的布局中。
由于 ShapeDrawable
有自己的 draw()
方法,因此您可以创建 View
的子类,用于在 onDraw()
事件期间绘制 ShapeDrawable
对象,如以下代码示例所示:
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" />
与 android.graphics.drawable
软件包中的许多其他可绘制对象类型一样,ShapeDrawable
类可让您使用公共方法定义对象的各种属性。您可能需要调整的一些示例属性包括 Alpha 透明度、颜色滤镜、抖动、不透明度和颜色。
您还可以使用 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_width
和 layout_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 标记名称,并通过类属性指定完全限定的类名称。此方法可同时用于顶级公共类和公共静态内部类:<drawable xmlns:android="http://schemas.android.com/apk/res/android" class="com.myapp.MyTopLevelClass$MyDrawable" android:color="#ffff0000" />
向可绘制对象添加色调
对于 Android 5.0(API 级别 21)及更高版本,您可为位图和定义为 Alpha 蒙版的位图和九宫格着色。您可以使用颜色资源或解析为颜色资源(例如 ?android:attr/colorPrimary
)的主题属性为其着色。通常,您只需创建一次这些资源,然后自动为其着色以与您的主题相符。
您可以使用 setTint()
方法对 BitmapDrawable
、NinePatchDrawable
或 VectorDrawable
对象着色。您还可以使用 android:tint
和 android:tintMode
属性在布局中设置色调颜色和模式。
从图片中萃取突出颜色
Android 支持库包含 Palette
类,可让您从图片中提取突出颜色。您可以将可绘制对象作为 Bitmap
加载,并将其传递给 Palette
以获取其颜色。如需了解详情,请参阅使用 Palette API 选择颜色。