画布和绘图

来源:互联网 发布:双11淘宝所有商品都打折么 编辑:程序博客网 时间:2024/05/17 21:52

https://developer.android.google.cn/guide/topics/graphics/2d-graphics.html#vector-drawable

画布和绘图

Android框架提供了一套二维绘图API,可让您将自己的自定义图形渲染到画布上或修改现有视图以自定义其外观。您通常以下列方式之一绘制2-D图形:

a.在布局中的View对象上绘制图形或动画。 通过使用此选项,系统的渲染管线处理您的图形 - 您有责任在视图中定义图形。

b.在Canvas对象中绘制图形。 要使用此选项,您将画布传递到适当的类'onDraw(Canvas)方法。您也可以在画布中使用绘图方法。此选项还可以控制任何动画。

 

绘制一个视图是一个不错的选择,当你想绘制简单的图形,不需要动态变化,而不是性能密集型应用程序,如游戏的一部分。例如,当您要在其他静态应用程序中显示静态图形或预定义的动画时,应将图形绘制到视图中。有关更多信息,请参阅Drawables。

 

当您的应用程序需要定期重绘时,绘制画布会更好。应用程序(如视频游戏)应该自己绘制到画布上。但是,有多种方式可以做到这一点:

a.在应用程序的主线程中,您在布局中创建自定义视图组件,调用invalidate(),然后处理onDraw(Canvas)回调。

b.在管理SurfaceView的工作线程中,使用画布的绘图方法。 你不需要调用invalidate()。

 

Draw with a canvas

您可以通过绘制由Canvas类表示的画布来满足需要专门绘制和/或控制图形动画的应用程序的要求。画布可作为绘制图形的实际表面的伪装或界面,您可以对画布执行绘图操作。通过画布,您的应用程序将绘制到位于窗口中的底层Bitmap对象。

 

如果您正在绘制onDraw(Canvas)回调,画布已经提供,您只需要对其进行绘图调用。如果您使用的是SurfaceView对象,则可以从lockCanvas()获取画布。以下两节将讨论这两种情况。

 

如果需要创建一个新的Canvas对象,那么您必须定义将绘图放入窗口所需的底层Bitmap对象。以下代码示例显示了如何从位图设置新的画布:

Bitmap b = Bitmap.createBitmap(100,100, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);

可以通过使用drawBitmap()方法之一在不同的画布中使用位图。但是,我们建议您使用由onDraw(Canvas)回调或lockCanvas()方法提供的画布。有关更多信息,请参阅在SurfaceView上的视图和绘图上绘图。

 

 

Canvas类有自己的绘图方法,包括drawBitmap(),drawRect(),drawText()等等。 您可能使用的其他类也有draw()方法。 例如,您可能有一些Drawable对象要放在画布上。Drawable类具有自己的绘制(Canvas)方法,它将您的画布作为参数。

 

绘制一个视图

如果您的应用程序不需要大量处理或较高的帧速率(例如象棋游戏,蛇游戏或其他慢动画应用),那么您应该考虑使用画布创建自定义视图和绘图View.onDraw(Canvas)回调。最方便的方面是Android框架提供了可以执行绘图操作的预定义画布。

 

要开始,创建一个View的子类,并实现onDraw(Canvas)回调,Android框架调用它来绘制视图。然后通过框架提供的Canvas对象执行绘图操作。

 

Android框架仅在必要时调用onDraw(Canvas)。当您重新绘制应用程序时,您必须通过调用invalidate()来使视图无效。调用invalidate()表示您希望绘制视图。然后,Android会调用您的视图的onDraw(Canvas)方法,尽管呼叫不能保证是即时的。

 

在您的视图的onDraw(Canvas)方法中,调用画布上的绘图方法或者可以将画布作为参数的其他类的方法。一旦您的onDraw()完成,Android框架将使用您的画布绘制由系统处理的位图。

 

注意:要使应用程序主线程以外的线程的视图无效,您必须调用postInvalidate()而不是invalidate()。

 

有关扩展View类的信息,请参阅创建视图类。

 

在SurfaceView上绘图

SurfaceView是View的一个特殊子类,它在视图层次结构中提供了一个专用的绘图面。目标是向应用程序的工作线程提供这个绘图面。这样,应用程序不需要等到系统的视图层次结构准备绘制。相反,具有对SurfaceView对象的引用的工作线程可以以其自己的速度绘制到其自己的画布。

 

首先,您需要创建一个扩展SurfaceView的新类。此类还应实现SurfaceHolder.Callback接口,该接口提供在底层Surface对象中发生的事件,例如创建,更改或损坏时的事件。这些事件让您知道何时可以开始绘图,无论您是否需要根据新的曲面属性进行调整,以及何时停止绘制并可能终止某些任务。扩展SurfaceView的类也是定义工作线程的好地方,它调用画布中的所有绘图过程。

 

而不是直接处理Surface对象,您应该通过SurfaceHolder处理它。在您的SurfaceView对象初始化之后,您可以通过调用getHolder()获取一个SurfaceHolder对象。您应该注册您的SurfaceView对象,通过调用addCallback()来接收来自SurfaceHolder的通知。然后在您的SurfaceView类中实现每个SurfaceHolder.Callback抽象方法。

 

您可以从具有访问SurfaceHolder对象的工作线程绘制到表面画布。每次应用程序需要重新绘制表面时,请在工作线程中执行以下步骤:

1.使用lockCanvas()来检索画布。

2.在画布上执行绘图操作。

3.通过调用unlockCanvasAndPost(Canvas)传递您用于绘图操作的Canvas对象来解锁画布。

 

考虑到您在其上执行的所有绘图操作,表面绘制画布。

 

注意:每次从SurfaceHolder检索画布时,画布的先前状态都将被保留。为了正确地动画您的图形,您必须重绘整个表面。例如,您可以使用drawColor()方法填充颜色或使用drawBitmap()方法设置背景图像来清除画布的上一个状态。否则,您的画布可以显示以前的图纸的痕迹

 

Drawables

Android框架提供了一个用于绘制形状和图像的自定义2D图形库。android.graphics.drawable包包含用于二维绘制的常用类。

 

本节讨论使用可绘制对象绘制图形以及如何使用Drawable类的几个子类的基础知识。有关如何使用drawable进行逐帧动画的信息,请参阅可绘制的动画。

 

Drawable是可以绘制的东西的一般抽象。Android框架提供了一组Drawable的直接和间接子类,您可以在各种场景中使用。您还可以扩展这些类来定义您自己的以可访问的方式执行的可绘制对象。

 

除了使用标准类构造函数之外,还有两种方法来定义和实例化一个Drawable:

a.使用项目中保存的资源映像。

b.使用定义可绘制属性的XML资源。

 

从资源图像创建绘图

您可以通过引用项目资源中的图像文件来将图形添加到您的应用程序。支持的文件类型是PNG(首选),JPG(可接受)和GIF(不鼓励)。应用程序图标,徽标和其他图形,如游戏中使用的图形,非常适合这种技术。

 

要使用图像资源,请将文件添加到项目的res / drawable /目录中。在项目中,您可以从代码或XML布局引用映像资源。无论哪种方式,都使用资源ID,即不带文件扩展名的文件名称。例如,请参阅my_image.png作为my_image。

 

注意:在构建过程中,由aapt工具进行的无损图像压缩可以自动优化放置在res / drawable /目录中的图像资源。例如,不需要256色以上的真彩色PNG可以使用调色板转换为8位PNG。这导致一个质量相同的图像,但是需要更少的记忆。因此,放置在此目录中的图像二进制文件可能在构建时更改。如果您打算将图像作为比特流读取,以将其转换为位图,请将图像放在res / raw /文件夹中,而不是aapt工具不会修改它们。

 

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

LinearLayout mLinearLayout;protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  // Create a LinearLayout in which to add the ImageView  mLinearLayout = new LinearLayout(this);  // Instantiate an ImageView and define its properties  ImageView i = new ImageView(this);  i.setImageResource(R.drawable.my_image);  // set the ImageView bounds to match the Drawable's dimensions  i.setAdjustViewBounds(true);  i.setLayoutParams(new Gallery.LayoutParams(LayoutParams.WRAP_CONTENT,      LayoutParams.WRAP_CONTENT));  // Add the ImageView to the layout and set the layout as the content view  mLinearLayout.addView(i);  setContentView(mLinearLayout);}

 

在其他情况下,您可能希望将图像资源作为Drawable对象处理,如以下示例所示:

Resources res = mContext.getResources();Drawable myImage = res.getDrawable(R.drawable.my_image);

注意:您的项目中的每个唯一资源只能保留一个状态,无论您为其实例化了多少个不同的对象。例如,如果您从同一个图像资源中实例化两个Drawable对象,并更改一个对象的属性(例如alpha),那么它也会影响另一个对象。处理图像资源的多个实例时,而不是直接转换Drawable对象,您应该执行补间动画。

 

 

下面的XML代码片段显示了如何在XML布局中将可绘制的资源添加到ImageView中:

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

有关使用项目资源的更多信息,请参阅资源和资产。

 

从XML资源创建drawables

如果您想要创建一个Drawable对象,它最初不依赖于由代码或用户交互定义的变量,那么在XML中定义Drawable是一个很好的选择。即使您希望Drawable在用户与应用程序交互期间更改其属性,您应该考虑以XML定义对象,因为您可以在实例化之后修改属性。

 

在使用XML定义Drawable之后,将该文件保存在项目的res / drawable /目录中。以下示例显示了定义TransitionDrawable资源的XML,该资源继承自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来检索和实例化对象。任何支持inflate()方法的Drawable子类可以在XML中定义,并由您的应用程序实例化。支持XML通膨的每个可绘制类都使用特定的XML属性来帮助定义对象属性。以下代码实例化TransitionDrawable并将其设置为ImageView对象的内容:

Resources res = mContext.getResources();TransitionDrawable transition =    (TransitionDrawable) res.getDrawable(R.drawable.expand_collapse);ImageView image = (ImageView) findViewById(R.id.toggle_image);image.setImageDrawable(transition);// Then you can call the TransitionDrawable object's methodstransition.startTransition(1000);

有关支持的XML属性的更多信息,请参阅上面列出的类

Shape drawables

当您想要动态绘制二维图形时,ShapeDrawable对象可以是一个很好的选择。您可以在ShapeDrawable对象上以编程方式绘制原始形状,并应用您的应用程序需要的样式。

 

ShapeDrawable是Drawable的一个子类。 因此,您可以在预期的Drawable的任何地方使用ShapeDrawable。 例如,您可以使用ShapeDrawable对象通过将视图传递给视图的setBackgroundDrawable()方法来设置视图的背景。 您还可以将自己的形状绘制为自己的自定义视图,并将其添加到应用程序中的布局。

 

因为ShapeDrawable有自己的draw()方法,所以可以在onDraw()事件期间创建一个绘制ShapeDrawable对象的View子类,如下面的代码示例所示:

public class CustomDrawableView extends View {  private ShapeDrawable mDrawable;  public CustomDrawableView(Context context) {    super(context);    int x = 10;    int y = 10;    int width = 300;    int height = 50;    mDrawable = new ShapeDrawable(new OvalShape());    // If the color isn't set, the shape uses black as the default.    mDrawable.getPaint().setColor(0xff74AC23);    // If the bounds aren't set, the shape can't be drawn.    mDrawable.setBounds(x, y, x + width, y + height);  }  protected void onDraw(Canvas canvas) {    mDrawable.draw(canvas);  }}

 

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

CustomDrawableView mCustomDrawableView;protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  mCustomDrawableView = new CustomDrawableView(this);  setContentView(mCustomDrawableView);}

 

如果要在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包中的许多其他可绘制类型一样,允许您使用公共方法定义对象的各种属性。您可能需要调整的一些示例属性包括Alpha透明度,颜色过滤器,抖动,不透明度和颜色。

 

您还可以使用XML资源定义原始的可绘制形状。有关更多信息,请参阅可绘制资源类型中的形状Drawable。

 

NinePatch可绘制

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

 

使用边框来定义图像的可拉伸和静态区域。您可以通过在边框的左侧和顶部绘制一个(或多个)1像素宽的黑线(其他边框像素应完全透明或白色)来指示可伸缩部分。您可以拥有尽可能多的伸缩部分。可拉伸部分的相对尺寸保持不变,所以最大部分总是保持最大。

 

您还可以通过在右侧绘制一条线和底部的一条线来定义图像的可选绘制部分(有效地,填充线)。如果View对象将NinePatch图形设置为其背景,然后指定视图的文本,则它将自动展开,以使所有文本仅占用右侧和底部指定的区域(如果包含)。如果不包括填充行,Android将使用左侧和上侧的行来定义此可绘制区域。

 

为了澄清两条线之间的区别,左侧和上边界线定义允许复制图像的哪些像素以便拉伸图像。底部和右侧的行定义图像内允许占据的内容的相对区域。

 

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

这个NinePatch图形定义了一个可拉伸区域,左侧和顶部线条以及具有底部和右侧线条的可绘制区域。在顶部图像中,虚线灰色线标识要复制的图像的区域以拉伸图像。底部图像中的粉红色矩形标识允许视图内容的区域。如果内容不适合该区域,则图像被拉伸以使其适合。

 

Draw 9补丁工具使用WYSIWYG图形编辑器提供了一种非常方便的创建NinePatch图像的方法。如果您为可伸缩区域定义的区域有可能由于像素复制而产生绘图工件,那么它甚至会引发警告。

 

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

<Button 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 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图像呈现的两个按钮。请注意按钮的宽度和高度如何随着文字而变化,背景图像会延伸以适应它。

 

矢量绘图

可绘制的矢量是在XML文件中定义的矢量图形,作为一组点,线和曲线以及相关联的颜色信息。 Android框架提供了VectorDrawable和AnimatedVectorDrawable类,它们支持矢量图形作为可绘制的资源。

 

针对Android版本低于5.0(API级别21)的应用程序可以使用支持库版本23.2或更高版本来获得矢量绘图和动画矢量绘图的支持。有关使用向量可绘制类的更多信息,请参阅Vector Drawable。