3

掌握Android自定义View与独家优化技巧

 6 months ago
source link: http://www.rousetime.com/2023/11/02/%E6%8E%8C%E6%8F%A1Android%E8%87%AA%E5%AE%9A%E4%B9%89View%E4%B8%8E%E7%8B%AC%E5%AE%B6%E4%BC%98%E5%8C%96%E6%8A%80%E5%B7%A7/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在Android应用开发中,自定义View是一种强大的工具,可以帮助你创建独特的用户界面元素。本文将详细介绍如何创建自定义View,并提供优化技巧,以确保你的自定义View在性能和用户体验方面表现出色。

什么是自定义View

自定义View是Android开发中的重要概念,允许你创建不同于标准UI组件的用户界面元素。这些自定义View可以是各种形状、颜色和交互方式,完全满足你的设计需求。

自定义View优点

自定义View具有多方面的优点,包括:

  1. 创造性和定制性:自定义View允许你创建完全独特的用户界面元素,无限扩展Android原生UI组件的功能和外观。

  2. 灵活性:自定义View可以满足各种复杂的设计需求,从简单的动画效果到高度定制的绘图应用。

  3. 可重用性:一旦创建自定义View,它可以在应用中多次重复使用,提高代码的可维护性和可重用性。

  4. 分离关注点:自定义View可以帮助你将应用的不同部分分开,使代码更易于管理和测试。

  5. 提高性能:通过正确优化自定义View,可以提高性能,减少不必要的绘制操作,以及利用硬件加速。

  6. 掌握用户界面:自定义View让你有更多控制权,可以实现独特的用户体验和创新的界面设计。

创建自定义View

步骤1: 继承View类或其子类

要创建自定义View,首先需要继承自Android的View类或其子类,如ViewGroup。根据需要,你还可以继承更具体的子类,如TextViewImageView等。以下是一个简单的示例:

public class MyCustomView extends View {
public MyCustomView(Context context) {
super(context);
}
}

步骤2: 重写onMeasure方法

你需要重写onMeasure方法来定义自定义View的尺寸。这个方法决定了View的宽度和高度,通常基于View的内容和布局需求计算测量值。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desiredWidth = // 计算所需宽度;
int desiredHeight = // 计算所需高度;

int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);

int width, height;

if (widthMode == MeasureSpec.EXACTLY) {
// 确切的宽度
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
// 宽度不超过所需尺寸
width = Math.min(desiredWidth, widthSize);
} else {
// 未指定宽度
width = desiredWidth;
}

if (heightMode == MeasureSpec.EXACTLY) {
// 确切的高度
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
// 高度不超过所需尺寸
height = Math.min(desiredHeight, heightSize);
} else {
// 未指定高度
height = desiredHeight;
}

setMeasuredDimension(width, height);
}

步骤3: 重写onDraw方法

onDraw方法用于绘制自定义View的内容。在这里,你可以使用Canvas对象进行绘制操作,包括绘制形状、文本、位图等。

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

// 绘制自定义UI
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
}

步骤4: 在XML布局中使用自定义View

你可以在XML布局文件中使用你的自定义View,就像使用标准的UI组件一样。

<com.example.myapp.MyCustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

步骤5: 在Java代码中操作自定义View

你可以在Java代码中获取对自定义View的引用,并进一步自定义和操作它。

MyCustomView customView = findViewById(R.id.my_custom_view);
// 进行自定义操作,例如设置属性或监听器

自定义View注意事项

在创建自定义View时,需要考虑以下注意事项:

  1. 性能问题:自定义View的绘制操作可能影响应用的性能,因此需要谨慎优化,避免不必要的重绘。

  2. 测量和布局:正确实现onMeasureonLayout方法,以确保自定义View在布局中正确地排列和测量。

  3. 绘制顺序:了解绘制顺序,确保子View在父View之上正确绘制,避免遮挡或重叠。

  4. 触摸事件处理:处理触摸事件以实现交互,需要正确处理触摸事件的分发和处理。

  5. 内存管理:确保及时释放不再需要的资源,如位图,以防止内存泄漏。

  6. 适配屏幕尺寸:考虑在不同屏幕尺寸和密度下的表现,以确保用户界面适应不同的设备。

  7. 自定义属性:如果需要,可以定义和处理自定义属性,以便在XML布局中配置自定义View。

优化自定义View

使用硬件加速

启用硬件加速可以提高自定义View的绘制性能。在XML布局文件中,可以使用以下属性启用硬件加速:

android:layerType="hardware"

避免不必要的绘制

只在数据发生变化时进行绘制,可以减少CPU和GPU的负载。在onDraw方法中添加必要的条件检查,以确定是否需要重新绘制。

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

if (dataChanged) {
// 重新绘制
// ...
}
}

使用合适的绘制方法

根据需求选择适当的绘制方法,以提高性能。例如,如果你只需绘制一个位图,可以使用Canvas.drawBitmap()方法。

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

// 绘制位图
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image);
canvas.drawBitmap(bitmap, 0, 0, null);
}

处理触摸事件

如果需要自定义View响应触摸事件,可以重写onTouchEvent方法,处理触摸事件逻辑。

@Override
public boolean onTouchEvent(MotionEvent event) {
// 处理触摸事件逻辑
return true; // 或者返回super.onTouchEvent(event)根据需要
}

使用自定义绘制缓存

使用自定义绘制缓存可以减少不必要的重绘操作。在自定义View的类中,你可以创建一个Canvas和一个Bitmap,然后在Canvas上绘制内容。这样,在onDraw方法中,你只需要将Bitmap绘制到屏幕上,而不必每次都重新绘制内容。

public class MyCustomView extends View {
private Bitmap cacheBitmap;
private Canvas cacheCanvas;
private Paint paint;

public MyCustomView(Context context) {
super(context);
init();
}

public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {
paint = new Paint();
paint.setColor(Color.RED);
cacheBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
cacheCanvas = new Canvas(cacheBitmap);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

// 绘制缓存的Bitmap到屏幕
canvas.drawBitmap(cacheBitmap, 0, 0, null);
}

public void updateView() {
// 在cacheCanvas上绘制内容
cacheCanvas.drawRect(0, 0, getWidth(), getHeight(), paint);

// 通知系统重绘
invalidate();
}
}

使用图层合成

使用图层合成可以创建多个图层,然后将它们合成成一个单一的图像。这对于创建复杂的自定义View和特效非常有用。以下是一个示例,使用Canvas.saveLayer方法创建一个图层:

public class MyCustomView extends View {
private Paint paint;

public MyCustomView(Context context) {
super(context);
init();
}

public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {
paint = new Paint();
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

int width = getWidth();
int height = getHeight();

int saveFlags = Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG |
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG;
int layerId = canvas.saveLayer(0, 0, width, height, null, saveFlags);

// 在图层上绘制内容
paint.setColor(Color.RED);
canvas.drawRect(0, 0, width / 2, height, paint);

paint.setColor(Color.BLUE);
canvas.drawRect(width / 2, 0, width, height, paint);

// 合并图层
canvas.restoreToCount(layerId);
}
}

考虑多线程绘制

将自定义View的绘制操作移到后台线程,以提高性能和响应性。下面是一个使用AsyncTask的示例,将绘制操作放在后台线程中:

public class MyCustomView extends View {
// ...

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

// 绘制UI
}

public void drawInBackground() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// 在后台线程上绘制UI
return null;
}

@Override
protected void onPostExecute(Void aVoid) {
// 更新UI
invalidate();
}
}.execute();
}
}

使用自定义View组合

将多个自定义View组合到一个更大的自定义View中,以提高可维护性和可重用性。以下是一个示例,其中MyCustomViewGroup包含了多个子自定义View:

public class MyCustomViewGroup extends ViewGroup {
public MyCustomViewGroup(Context context) {
super(context);
init();
}

private void init() {
// 添加子自定义View
MyCustomView view1 = new MyCustomView(getContext());
MyCustomView view2 = new MyCustomView(getContext());
// ...
addView(view1);
addView(view2);
// ...
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 布局子自定义View
}
}

自定义View是Android应用开发中的关键工具,允许开发人员创建独特的用户界面元素。通过继承View类、重写onMeasure和onDraw等方法,以及应用性能优化技巧,你可以打造出高性能和令人印象深刻的自定义用户界面。

android_startup: 提供一种在应用启动时能够更加简单、高效的方式来初始化组件,优化启动速度。不仅支持Jetpack App Startup的全部功能,还提供额外的同步与异步等待、线程控制与多进程支持等功能。

AwesomeGithub: 基于Github的客户端,纯练习项目,支持组件化开发,支持账户密码与认证登陆。使用Kotlin语言进行开发,项目架构是基于JetPack\&DataBinding的MVVM;项目中使用了Arouter、Retrofit、Coroutine、Glide、Dagger与Hilt等流行开源技术。

flutter_github: 基于Flutter的跨平台版本Github客户端,与AwesomeGithub相对应。

android-api-analysis: 结合详细的Demo来全面解析Android相关的知识点, 帮助读者能够更快的掌握与理解所阐述的要点。

daily_algorithm: 每日一算法,由浅入深,欢迎加入一起共勉。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK