Android 图形和动画
Android 提供了丰富多样的框架来支持 2D 图形和动画。 本主题介绍这些框架,并讨论如何创建自定义图形和动画,以便在 Xamarin.Android 应用程序中使用。
概述
尽管在传统上具有有限功率的设备上运行,但最高评级的移动应用程序通常具有复杂的用户体验 (UX),并且具有高质量的图形和动画,可提供直观、响应迅速、动态的感觉。 随着移动应用程序越来越复杂,用户已经开始期待越来越多的来自应用程序。
幸运的是,现代移动平台具有非常强大的框架,用于创建复杂的动画和自定义图形,同时保持易用性。 这使开发人员能够以很少的努力添加丰富的交互性。
Android 中的 UI API 框架大致分为两类:图形和动画。
图形进一步拆分为执行 2D 和 3D 图形的不同方法。 3D 图形通过许多内置框架(如 OpenGL ES(特定于移动版本的 OpenGL))和第三方框架(如 MonoGame(与 XNA 工具包兼容的跨平台工具包))提供。 虽然 3D 图形不在本文的范围内,但我们将检查内置的 2D 绘图技术。
Android 提供了两个不同的 API,用于创建 2D 图形。 一种是高级声明性方法,另一种是编程性低级别 API:
可绘制资源 – 这些资源用于以编程方式或(更通常)的方式创建自定义图形,方法是在 XML 文件中嵌入绘图说明。 可绘制资源通常定义为 XML 文件,其中包含用于 Android 呈现 2D 图形的说明或操作。
画布 – 这是一个低级别 API,涉及直接在基础位图上进行绘制。 它提供非常精细的控制显示的内容。
除了这些 2D 图形技术之外,Android 还提供多种不同的方法来创建动画:
可绘制动画 – Android 还支持称为“可绘制动画”的逐帧动画。 这是最简单的动画 API。 Android 按顺序加载和显示可绘制资源(非常类似于卡通)。
视图动画 – 视图动画是 Android 中的原始动画 API,可在 Android 的所有版本中使用。 此 API 受到限制,因为它只能处理 View 对象,并且只能对这些视图执行简单的转换。 视图动画通常在
/Resources/anim
文件夹中的 XML 文件中定义。属性动画 – Android 3.0 引入了一组名为“属性动画”的新动画 API。 这些新 API 引入了一个可扩展且灵活的系统,可用于对任何对象的属性进行动画处理,而不仅仅是 View 对象。 这种灵活性允许将动画封装在不同的类中,以便更轻松地共享代码。
视图动画更适用于必须支持旧版 Android 3.0 API (API 级别 11) 的应用程序。 否则,应用程序应出于上述原因使用较新的属性动画 API。
所有这些框架都是可行的选项,但尽可能优先使用属性动画,因为它是一个更灵活的 API。 属性动画允许将动画逻辑封装在不同的类中,使代码共享更加轻松,并简化代码维护。
辅助功能
图形和动画有助于使 Android 应用具有吸引力,并有趣地使用;但是,请务必记住,某些交互是通过屏幕阅读器、备用输入设备或辅助缩放进行的。 此外,某些交互可能没有音频功能。
如果应用在设计时具有辅助功能,则这些应用更可用:在用户界面中提供提示和导航帮助,并确保 UI 的图片元素有文本内容或说明。
有关如何利用 Android 辅助功能 API 的详细信息,请参阅 Google 辅助功能指南。
2D 图形
可绘制资源是 Android 应用程序中的一种常用技术。 与其他资源一样,可绘制资源是声明性的,它们在 XML 文件中定义。 此方法允许将代码与资源完全分离。 这可以简化开发和维护,因为无需更改代码来更新或更改 Android 应用程序中的图形。 但是,虽然可绘制资源适用于许多简单和常见的图形要求,但它们缺乏画布 API 的强大功能和控制。
使用 Canvas 对象的另一种方法非常类似于其他传统 API 框架,例如 System.Drawing 或 iOS 的核心绘图。 使用 Canvas 对象可以最控制如何创建 2D 图形。 它适用于可绘制资源无法工作或难以使用的情况。 例如,可能需要绘制一个自定义滑块控件,该控件的外观将根据与滑块值相关的计算而更改。
让我们先检查一下可绘制的资源。 它们更简单,涵盖最常见的自定义绘图案例。
可绘制资源
可绘制的资源在目录 /Resources/drawable
的 XML 文件中定义。 与嵌入 PNG 或 JPEG 不同,不需要提供特定于密度的可绘制资源版本。
在运行时,Android 应用程序将加载这些资源,并使用这些 XML 文件中包含的说明创建 2D 图形。
Android 定义了多种不同类型的可绘制资源:
ShapeDrawable – 这是一个可绘制对象,用于绘制基元几何形状,并应用于该形状的一组有限的图形效果。 它们对于自定义按钮或设置 TextViews 背景等内容非常有用。 本文稍后有一个示例,介绍如何使用
ShapeDrawable
。StateListDrawable – 这是一个可绘制的资源,它将根据小组件/控件的状态更改外观。 例如,按钮可能会根据是否按下按钮来更改其外观。
LayerDrawable – 此可绘制资源将堆叠多个其他可绘制器,它们依次堆叠。 以下屏幕截图显示了 LayerDrawable 的示例:
TransitionDrawable – 这是 LayerDrawable,但有一个区别。 TransitionDrawable 能够呈现一个层显示在另一层之上的动画效果。
LevelListDrawable – 这与 StateListDrawable 非常相似,因为它将根据特定条件显示图像。 但是,与 StateListDrawable 不同,LevelListDrawable 基于整数值显示图像。 LevelListDrawable 的一个示例是显示 WiFi 信号的强度。 随着 WiFi 信号强度的变化,显示的可绘制量将相应地改变。
ScaleDrawable/ClipDrawable – 顾名思义,这些绘图器提供缩放和剪辑功能。 ScaleDrawable 将缩放另一个可绘制资源,而 ClipDrawable 将剪辑另一个可绘制资源。
InsetDrawable – 此绘图器将应用于另一个可绘制资源两侧的内嵌。 当视图需要小于视图的实际边界的背景时,将使用它。
XML BitmapDrawable – 此文件是在 XML 中执行的一组指令,用于对实际位图执行。 Android 可执行的一些操作是平铺、抖动和抗锯齿。 其中一种非常常见的用途是跨布局背景平铺位图。
可绘制示例
让我们看看如何使用 ShapeDrawable
创建二维图形的快速示例。 ShapeDrawable
可以定义四个基本形状之一:矩形、椭圆形、线条和环形。 还可以应用基本效果,如渐变、颜色和大小。 以下 XML 是可在 AnimationsDemo 配套项目中(位于文件 Resources/drawable/shape_rounded_blue_rect.xml
)中找到的 ShapeDrawable
。
它定义具有紫色渐变背景和圆角的矩形:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<!-- Specify a gradient for the background -->
<gradient android:angle="45"
android:startColor="#55000066"
android:centerColor="#00000000"
android:endColor="#00000000"
android:centerX="0.75" />
<padding android:left="5dp"
android:right="5dp"
android:top="5dp"
android:bottom="5dp" />
<corners android:topLeftRadius="10dp"
android:topRightRadius="10dp"
android:bottomLeftRadius="10dp"
android:bottomRightRadius="10dp" />
</shape>
我们可以在布局或其他绘图对象中以声明方式引用此可绘制资源,如以下 XML 所示:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#33000000">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/shape_rounded_blue_rect"
android:text="@string/message_shapedrawable" />
</RelativeLayout>
还可以以编程方式应用可绘制资源。 以下代码片段演示如何以编程方式设置 TextView 的背景:
TextView tv = FindViewById<TextView>(Resource.Id.shapeDrawableTextView);
tv.SetBackgroundResource(Resource.Drawable.shape_rounded_blue_rect);
若要查看其外观,请运行 AnimationsDemo 项目,并从主菜单中选择“形状可绘制”项。 应会看到类似于以下屏幕截图的内容:
有关可绘制资源的 XML 元素和语法的更多详细信息,请参阅 Google 的文档。
使用画布绘图 API
可绘制器功能强大,但有其限制。 某些操作要么不可能,要么非常复杂(例如:将筛选器应用于设备上相机拍摄的照片)。 使用可绘制资源来应用红眼减少是非常困难的。 相反,Canvas API 允许应用程序具有非常精细的控制,以选择性地更改图片的特定部分的颜色。
通常与 Canvas 一起使用的类是 Paint 类。 此类包含有关如何绘制的颜色和样式信息。 它用于提供如此颜色和透明度的东西。
画布 API 使用画家的模型绘制 2D 图形。 操作在彼此的连续层中应用。 每个操作将涵盖基础位图的某些区域。 当区域与以前绘制的区域重叠时,新画将部分或完全遮挡旧画。 这与许多其他绘图 API(如 System.Drawing 和 iOS 的核心图形)的工作方式相同。
可通过两种方式获取 Canvas
对象。 第一种方法涉及定义位图对象,然后使用该对象实例化 Canvas
对象。 例如,以下代码片段使用基础位图创建新的画布:
Bitmap bitmap = Bitmap.CreateBitmap(100, 100, Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(b);
获取 Canvas
对象的另一种方法是提供 View 基类的 OnDraw 回调方法。 Android 在确定视图需要自行绘制并传入 Canvas
对象供视图使用时,会调用此方法。
Canvas 类公开方法以编程方式提供绘图指令。 例如:
Canvas.DrawPaint – 用指定的颜料填充整个画布的位图。
Canvas.DrawPath – 使用指定的颜料绘制指定的几何形状。
Canvas.DrawText – 使用指定颜色绘制画布上的文本。 文本在位置
x,y
绘制。
使用画布 API 绘图
下面是画布 API 的操作示例。 以下代码片段演示如何绘制视图:
public class MyView : View
{
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
Paint green = new Paint {
AntiAlias = true,
Color = Color.Rgb(0x99, 0xcc, 0),
};
green.SetStyle(Paint.Style.FillAndStroke);
Paint red = new Paint {
AntiAlias = true,
Color = Color.Rgb(0xff, 0x44, 0x44)
};
red.SetStyle(Paint.Style.FillAndStroke);
float middle = canvas.Width * 0.25f;
canvas.DrawPaint(red);
canvas.DrawRect(0, 0, middle, canvas.Height, green);
}
}
上面的代码首先创建红色颜料和绿色颜料对象。 它用红色填充画布的内容,然后指示画布绘制一个占画布宽度 25% 的绿色矩形。 可通过本文的源代码随附的 AnimationsDemo
项目中查看此示例。 通过启动应用程序并从主菜单中选择“绘图”项,我们应该有如下所示的屏幕:
动画
用户喜欢在应用程序中移动的内容。 动画是改进应用程序用户体验并帮助其脱颖而出的好方法。最好的动画是用户不注意到的动画,因为它们感觉自然。 Android 提供以下三个 API 用于动画:
视图动画 – 这是原始 API。 这些动画绑定到特定视图,可以对视图的内容执行简单的转换。 由于简单起见,此 API 对于 alpha 动画、旋转等内容仍然很有用。
属性动画 - Android 3.0 中引入了属性动画。 它们使应用程序能够对几乎任何内容进行动画处理。 属性动画可用于更改任何对象的任意属性,即使该对象在屏幕上不可见也是如此。
可绘制动画 – 这是一种特殊的可绘制资源,用于对布局应用非常简单的动画效果。
通常,属性动画是首选系统,因为它更灵活,并提供更多功能。
查看动画
视图动画仅限于视图,只能对起点和终点、大小、旋转和透明度等值执行动画。 这些类型的动画通常称为补间动画。 可以通过两种方式(以编程方式在代码中或使用 XML 文件)定义视图动画。 XML 文件是声明视图动画的首选方法,因为它们更易于阅读且更易于维护。
动画 XML 文件将存储在 Xamarin.Android 项目的 /Resources/anim
目录中。 此文件必须具有以下元素之一作为根元素:
alpha
– 淡入或淡出动画。rotate
– 旋转动画。scale
– 调整动画大小。translate
– 水平和/或垂直运动。set
– 可以容纳一个或多个其他动画元素的容器。
默认情况下,XML 文件中的所有动画将同时应用。 若要按顺序呈现动画,请对上面定义的元素之一设置 android:startOffset
属性。
可以使用内插器来影响动画变化率。 使用内插器可以加速、重复或减速动画效果。 Android 框架现成提供多个内插器,例如(包括但不限于):
AccelerateInterpolator
/DecelerateInterpolator
– 这些内插器增加或降低动画中的更改速率。BounceInterpolator
– 更改在末尾反弹。LinearInterpolator
– 更改率不变。
以下 XML 显示了一个动画文件示例,该文件合并了其中一些元素:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android=http://schemas.android.com/apk/res/android
android:shareInterpolator="false">
<scale android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromXScale="1.0"
android:toXScale="1.4"
android:fromYScale="1.0"
android:toYScale="0.6"
android:pivotX="50%"
android:pivotY="50%"
android:fillEnabled="true"
android:fillAfter="false"
android:duration="700" />
<set android:interpolator="@android:anim/accelerate_interpolator">
<scale android:fromXScale="1.4"
android:toXScale="0.0"
android:fromYScale="0.6"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:fillEnabled="true"
android:fillBefore="false"
android:fillAfter="true"
android:startOffset="700"
android:duration="400" />
<rotate android:fromDegrees="0"
android:toDegrees="-45"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:fillEnabled="true"
android:fillBefore="false"
android:fillAfter="true"
android:startOffset="700"
android:duration="400" />
</set>
</set>
此动画将同时执行所有动画。 第一个缩放动画将水平拉伸图像,并垂直收缩图像,然后同时旋转 45 度反时针和收缩,从屏幕中消失。
动画可以通过膨胀动画并将其应用于视图,从而以编程方式应用于视图。 Android 提供帮助程序类 Android.Views.Animations.AnimationUtils
,用于膨胀动画资源并返回 Android.Views.Animations.Animation
实例。 通过调用 StartAnimation
并传递 Animation
对象,将此对象应用于视图。 以下代码片段演示了一个示例:
Animation myAnimation = AnimationUtils.LoadAnimation(Resource.Animation.MyAnimation);
ImageView myImage = FindViewById<ImageView>(Resource.Id.imageView1);
myImage.StartAnimation(myAnimation);
现在,我们已经对视图动画的工作原理有了基本的了解,让我们转到属性动画。
属性动画
属性动画器是 Android 3.0 中引入的新 API。 它们提供了一个更可扩展的 API,可用于对任何对象的任何属性进行动画处理。
所有属性动画都是由 Animator 子类的实例创建的。 应用程序不直接使用此类,而是使用其中一个子类:
ValueAnimator – 此类是整个属性动画 API 中最重要的类。 它计算需要更改的属性的值。
ViewAnimator
不会直接更新这些值;而是引发可用于更新动画对象的事件。ObjectAnimator – 此类是
ValueAnimator
的子类。 它旨在通过接受要更新的目标对象和属性来简化对象动画处理的过程。AnimationSet – 此类负责协调动画如何相互运行。 动画可以同时、按顺序或具有指定延迟运行动画。
Evaluators是动画器用于在动画期间计算新值的特殊类。 现时,Android 提供以下评估程序:
IntEvaluator – 计算整数属性的值。
FloatEvaluator – 计算浮点属性的值。
ArgbEvaluator – 计算颜色属性的值。
如果正在进行动画处理的属性不是 float
、int
或颜色,则应用程序可以通过实现 ITypeEvaluator
接口来创建自己的计算器。 (实现自定义计算器超出了本主题的范围。)
使用 ValueAnimator
任何动画都有两个部分:计算动画值,然后在某些对象的属性上设置这些值。 ValueAnimator 将仅计算值,但它不会直接对对象进行操作。 相反,将在动画生命周期内调用的事件处理程序中更新对象。 此设计允许从一个动画值更新多个属性。
可以通过调用以下工厂方法之一来获取 ValueAnimator
实例:
ValueAnimator.OfInt
ValueAnimator.OfFloat
ValueAnimator.OfObject
完成后,ValueAnimator
实例必须设置其持续时间,然后才能启动。 以下示例演示如何在 1000 毫秒的跨度内对值从 0 到 1 进行动画处理:
ValueAnimator animator = ValueAnimator.OfInt(0, 100);
animator.SetDuration(1000);
animator.Start();
但是,上面的代码片段并不十分有用,动画器将运行,但更新的值没有目标。 当 Animator
类决定有必要通知侦听器新值时,将引发 Update 事件。 应用程序可以提供事件处理程序来响应此事件,如以下代码片段所示:
MyCustomObject myObj = new MyCustomObject();
myObj.SomeIntegerValue = -1;
animator.Update += (object sender, ValueAnimator.AnimatorUpdateEventArgs e) =>
{
int newValue = (int) e.Animation.AnimatedValue;
// Apply this new value to the object being animated.
myObj.SomeIntegerValue = newValue;
};
现在我们已经了解了 ValueAnimator
,让我们详细了解 ObjectAnimator
。
使用 ObjectAnimator
ObjectAnimator 是 ViewAnimator
的子类,它将 ValueAnimator
的计时引擎和值计算与连接事件处理程序所需的逻辑相结合。 ValueAnimator
要求应用程序显式连接事件处理程序,ObjectAnimator
将为我们处理此步骤。
ObjectAnimator
API 与用于 ViewAnimator
的 API 非常相似,但需要提供要更新的对象和属性的名称。 以下示例演示了使用 ObjectAnimator
的示例:
MyCustomObject myObj = new MyCustomObject();
myObj.SomeIntegerValue = -1;
ObjectAnimator animator = ObjectAnimator.OfFloat(myObj, "SomeIntegerValue", 0, 100);
animator.SetDuration(1000);
animator.Start();
如上一代码片段所示,ObjectAnimator
可以减少和简化对对象进行动画处理所需的代码。
可绘制动画
最后一个动画 API 是可绘制动画 API。 可绘制动画加载一系列可绘制资源,并按顺序显示它们,类似于翻转卡通。
可绘制资源在 XML 文件中定义,该 XML 文件中具有一个 <animation-list>
元素作为根元素,以及一系列定义动画中每个帧的 <item>
元素。 此 XML 文件存储在应用程序的 /Resource/drawable
文件夹中。 以下 XML 是可绘制动画的示例:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/asteroid01" android:duration="100" />
<item android:drawable="@drawable/asteroid02" android:duration="100" />
<item android:drawable="@drawable/asteroid03" android:duration="100" />
<item android:drawable="@drawable/asteroid04" android:duration="100" />
<item android:drawable="@drawable/asteroid05" android:duration="100" />
<item android:drawable="@drawable/asteroid06" android:duration="100" />
</animation-list>
此动画将运行六帧。 android:duration
属性声明每个帧的显示时间。 下一个代码片段演示了一个示例:创建可绘制动画并在用户单击屏幕上的按钮时启动它:
AnimationDrawable _asteroidDrawable;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
_asteroidDrawable = (Android.Graphics.Drawables.AnimationDrawable)
Resources.GetDrawable(Resource.Drawable.spinning_asteroid);
ImageView asteroidImage = FindViewById<ImageView>(Resource.Id.imageView2);
asteroidImage.SetImageDrawable((Android.Graphics.Drawables.Drawable) _asteroidDrawable);
Button asteroidButton = FindViewById<Button>(Resource.Id.spinAsteroid);
asteroidButton.Click += (sender, e) =>
{
_asteroidDrawable.Start();
};
}
目前,我们介绍了 Android 应用程序中可用的动画 API 的基础。
总结
本文介绍了许多新概念和 API,以帮助向 Android 应用程序添加一些图形。 首先,它讨论了各种 2D 图形 API,并演示了 Android 如何允许应用程序使用 Canvas 对象直接绘制到屏幕。 我们还了解了一些替代技术,这些技术允许使用 XML 文件以声明方式创建图形。 然后,我们继续讨论在 Android 中创建动画的旧 API 和新 API。