Material Design 是 Google 推出的一种视觉设计语言,旨在为 Android 和 Web 设计提供一致的界面样式和交互效果。在 Android 应用开发中,使用 Material Design 可以使 UI 更加美观、易用和规范化。而自定义 View 则是实现 Material Design 的关键,因为 Material Design 强调动画、阴影、色彩和形状等效果,需要通过自定义 View 来实现。
这篇文章将介绍基于 Material Design 的 Android UI 自定义 View 开发。我们将从以下几个方面进行讲解:
- Material Design 的基础知识
- 自定义 View 的基本原理
- 实现 Material Design 效果的关键技术
- 示例代码演示
Material Design 的基础知识
Material Design 是一种所见即所得的设计语言,能够同时表达出物理和数字世界内的交互设计。其特点包括:
- 平面化设计:弱化传统浮雕效果,让界面更加扁平化。
- 卡片化布局:将内容放在矩形的卡片中,使得内容结构更加清晰。
- 纸张的阴影效果:使用阴影来表达卡片之间的层级关系。
- 鲜艳的颜色:强调使用鲜艳明亮的颜色,通过颜色和图标来区分不同的功能和内容。
- 交互细节:提供易于理解和友好的交互细节,如触摸反馈、视差效果、动画效果等。
在 Android 中,我们可以使用 Material Design Support Library 来实现各种 Material Design 效果,包括 Toolbar、TabLayout、RecyclerView、CardView 等控件。但是,对于一些特定的视觉效果,我们需要通过自定义 View 来实现。
自定义 View 的基本原理
在 Android 中,自定义 View 的基本原理是继承 View 或者 ViewGroup 类,并在 onDraw 方法中实现自己的绘制代码。即通过 Canvas、Paint、Path 等绘图相关类来绘制出我们需要的图形或者效果。
例如,我们使用 Canvas 来绘制一个圆形:
// www.javascriptcn.com code example
public class CircleView extends View {
private Paint mPaint;
public CircleView(Context context) {
super(context);
init();
}
public CircleView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
int radius = Math.min(getWidth(), getHeight()) / 2;
canvas.drawCircle(centerX, centerY, radius, mPaint);
}
}上面的代码定义了一个 CircleView 类,继承自 View。在构造函数中初始化了 Paint 对象,并在 onDraw 方法中绘制了一个圆形。当我们在布局文件中使用 CircleView 控件时,就能够显示出一个红色的圆形。
这只是自定义 View 的基本方法,更加复杂的视觉效果需要使用更多的绘制方法和技巧。下面,我们将介绍几个关键的技术,用于实现 Material Design 效果。
实现 Material Design 效果的关键技术
圆形头像
在 Material Design 中,头像一般采用圆形样式,带有阴影效果。为了实现这个样式,我们需要使用 Canvas 的 clipPath 方法来裁剪头像,同时使用 Paint 的 setShadowLayer 方法来添加阴影。
// www.javascriptcn.com code example
public class CircleImageView extends ImageView {
private Paint mPaint;
private Path mPath;
private int mBorderColor;
private int mBorderWidth;
private int mShadowColor;
private float mShadowRadius;
private float mShadowDx;
private float mShadowDy;
public CircleImageView(Context context) {
super(context);
init(null);
}
public CircleImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
private void init(AttributeSet attrs) {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.CircleImageView, 0, 0);
try {
mBorderColor = a.getColor(R.styleable.CircleImageView_borderColor, Color.TRANSPARENT);
mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_borderWidth, 0);
mShadowColor = a.getColor(R.styleable.CircleImageView_shadowColor, Color.BLACK);
mShadowRadius = a.getFloat(R.styleable.CircleImageView_shadowRadius, 0);
mShadowDx = a.getFloat(R.styleable.CircleImageView_shadowDx, 0);
mShadowDy = a.getFloat(R.styleable.CircleImageView_shadowDy, 0);
} finally {
a.recycle();
}
}
@Override
protected void onDraw(Canvas canvas) {
// 获取 ImageView 的 Bitmap 对象
Bitmap bitmap = ((BitmapDrawable) getDrawable()).getBitmap();
// 绘制圆形
mPaint.setStyle(Paint.Style.FILL);
mPaint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, mPaint);
// 绘制边框
if (mBorderWidth > 0) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mBorderColor);
mPaint.setStrokeWidth(mBorderWidth);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - mBorderWidth / 2, mPaint);
}
// 添加阴影
if (mShadowRadius > 0) {
mPaint.setShader(null);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.WHITE);
mPaint.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor);
mPath = new Path();
mPath.addCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, Path.Direction.CW);
canvas.clipPath(mPath);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - mBorderWidth, mPaint);
}
}
}上面的代码定义了一个 CircleImageView 类,继承自 ImageView。在 onDraw 方法中,我们使用 BitmapShader 来设置 ImageView 的 Bitmap 对象,并使用 drawCircle 方法绘制了一个圆形,同时使用 drawCircle 方法绘制了一个边框。最后,使用 setShadowLayer 方法来添加阴影效果。
涟漪效果
涟漪效果是 Material Design 中常见的交互效果,指的是在用户点击一个控件时,控件周围会出现像水波纹一样的涟漪效果。
为了实现这个效果,我们可以使用 RippleDrawable 来实现。RippleDrawable 是一个用于绘制涟漪效果的 Drawable 类,它包含一个中心圆和扩散效果。
// www.javascriptcn.com code example
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorPrimaryDark">
<item android:id="@android:id/mask">
<shape android:shape="oval">
<solid android:color="@android:color/white" />
</shape>
</item>
<item android:id="@android:id/background">
<shape android:shape="oval">
<solid android:color="@android:color/white" />
</shape>
</item>
</ripple>上面的代码演示了一个 RippleDrawable 的实现,包括一个 mask 层和一个 background 层。mask 层用于定义 RippleDrawable 的形状,background 层用于定义 RippleDrawable 的颜色。在使用 RippleDrawable 时,我们可以将其作为 View 的背景来实现涟漪效果。
按钮阴影效果
Material Design 中的按钮通常具有阴影效果,阴影效果可以增加按钮的深度感,使得距离更加明显。为了实现这个效果,我们可以使用 StateListDrawable 来实现。
// www.javascriptcn.com code example
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="@color/colorPrimaryDark" />
<corners android:radius="8dp" />
</shape>
</item>
<item>
<layer-list>
<item>
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="8dp" />
</shape>
</item>
<item android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="@color/md_grey_200" />
<corners android:radius="8dp" />
</shape>
</item>
</layer-list>
</item>
</selector>上面的代码演示了一个 StateListDrawable 的实现,包括一个按下状态和一个普通状态。在按下状态时,背景使用一个圆角矩形,并填充指定的颜色。在普通状态中,背景由两层组成,一个为白色圆角矩形,一个为灰色圆角矩形,并使用 layer-list 来实现。
示例代码演示
最后,我们通过一个示例代码来展示如何组合上面的技术来实现一个 Material Design 风格的控件效果。
// www.javascriptcn.com code example
public class MaterialButton extends AppCompatButton {
public MaterialButton(Context context) {
super(context);
init(null);
}
public MaterialButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
private void init(AttributeSet attrs) {
GradientDrawable backgroundDrawable = new GradientDrawable();
backgroundDrawable.setCornerRadius(getResources().getDimensionPixelSize(R.dimen.material_button_corner_radius));
int[] buttonColors = new int[2];
buttonColors[0] = getResources().getColor(R.color.md_green_200);
buttonColors[1] = getResources().getColor(R.color.md_green_500);
GradientDrawable uncheckedDrawable = new GradientDrawable();
uncheckedDrawable.setShape(GradientDrawable.OVAL);
uncheckedDrawable.setColor(getResources().getColor(R.color.md_grey_500));
GradientDrawable checkedDrawable = new GradientDrawable();
checkedDrawable.setShape(GradientDrawable.OVAL);
checkedDrawable.setColor(getResources().getColor(R.color.md_white_1000));
int radius = getResources().getDimensionPixelSize(R.dimen.material_button_corner_radius);
int strokeWidth = getResources().getDimensionPixelSize(R.dimen.material_button_stroke_width);
int strokeColor = getResources().getColor(R.color.md_green_600);
Drawable rippleDrawable = getResources().getDrawable(R.drawable.ripple_material_button);
CompoundButtonCompat.setButtonTintList(this, ColorStateList.valueOf(getResources().getColor(R.color.md_green_500)));
setBackground(rippleDrawable);
DrawableCompat.setTintList(DrawableCompat.wrap(getCompoundDrawables()[1]), ColorStateList.valueOf(getResources().getColor(R.color.md_white_1000)));
setPadding(getResources().getDimensionPixelSize(R.dimen.material_button_horizontal_padding), getPaddingTop(), getPaddingRight(), getPaddingBottom());
setTextColor(getResources().getColor(R.color.md_white_1000));
}
}上面的代码实现了一个 Material Design 风格的按钮控件效果,包括圆角矩形背景、圆形复选框、涟漪效果、按钮阴影、颜色渐变等效果。通过组合使用上面的技术,我们可以实现出更加丰富多彩的控件效果,让应用更加符合 Material Design 的风格。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d7ed54a941bf7134e2b28b