盒子
盒子
文章目录
  1. 步骤一:继承View或者其子类
  2. 步骤二:实现构造方法
  3. 步骤三:实现onDraw方法
  4. 步骤四:处理触摸事件
  5. 步骤五:处理测量和布局
  6. 结论
  7. 推荐

Android 自定义 View 独家技巧

在Android开发中,自定义View是非常常见的需求。自定义View可以帮助我们实现一些特殊的效果,或者让我们的应用更加美观。本文将介绍Android自定义View的步骤,并提供示例代码。

步骤一:继承View或者其子类

要自定义View,我们首先需要创建一个新的类,并让它继承自View或者其子类。View是所有控件的基类,因此我们可以通过继承View来创建自定义控件。

比如,我们可以创建一个名为MyView的类,并让它继承自View:

1
2
3
public class MyView extends View {
// ...
}

步骤二:实现构造方法

接下来,我们需要为自定义View实现构造方法。在构造方法中,我们可以完成一些初始化的工作,比如设置画笔颜色、初始化属性等。

1
2
3
4
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// ... 初始化工作
}

步骤三:实现onDraw方法

onDraw方法是自定义View中最重要的方法之一。在这个方法中,我们可以使用画笔绘制自己想要的图形。

1
2
3
4
5
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// ... 绘制图形
}

在实现onDraw方法时,我们可以使用以下技巧:

  • 使用局部变量:在onDraw方法中创建对象和变量会增加内存分配和垃圾回收的负担。因此,在onDraw方法中使用局部变量可以提高性能。
  • 使用缓存:如果我们需要频繁重绘的自定义View,可以使用缓存来提高性能。可以使用Bitmap或者Canvas来进行缓存。
  • 使用线程:如果我们需要进行一些耗时的操作,比如网络请求、图片加载等,可以使用线程来避免阻塞UI线程。可以使用AsyncTask或者Handler来开启线程。

以下是一个使用局部变量和缓存的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MyView extends View {

private Paint mPaint; // 画笔
private Bitmap mBitmap; // 缓存的Bitmap
private Canvas mCanvas; // 缓存的Canvas

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

private void init() {
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(5);
mPaint.setStyle(Paint.Style.STROKE);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBitmap == null) {
// 创建缓存的Bitmap和Canvas
mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
// 绘制图形到缓存的Canvas上
mCanvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - 5, mPaint);
// 将缓存的Bitmap绘制到View的Canvas上
canvas.drawBitmap(mBitmap, 0, 0, null);
}
}

步骤四:处理触摸事件

如果我们的自定义View需要支持触摸事件,那么我们还需要实现触摸事件处理方法。

1
2
3
4
5
@Override
public boolean onTouchEvent(MotionEvent event) {
// ... 处理触摸事件
return super.onTouchEvent(event);
}

在处理触摸事件时,我们可以使用以下技巧:

  • 使用GestureDetector:GestureDetector可以帮助我们检测手势,比如单击、双击、长按、滑动等。
  • 使用VelocityTracker:VelocityTracker可以帮助我们计算触摸事件的速度和方向,比如滑动的速度和方向。
  • 使用Scroller:Scroller可以帮助我们实现平滑的滚动效果,比如ListView和ScrollView中的滚动效果。

以下是一个使用GestureDetector的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MyView extends View {

private GestureDetector mGestureDetector;

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

private void init() {
mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
// 处理单击事件
return super.onSingleTapUp(e);
}

@Override
public void onLongPress(MotionEvent e) {
// 处理长按事件
super.onLongPress(e);
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// 处理滑动事件
return super.onScroll(e1, e2, distanceX, distanceY);
}
});
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// 将事件交给GestureDetector处理
return mGestureDetector.onTouchEvent(event);
}
}

步骤五:处理测量和布局

如果我们的自定义View需要支持自适应大小,那么我们还需要处理测量和布局。在测量阶段,我们需要测量自定义View的大小;在布局阶段,我们需要根据测量结果来确定自定义View的位置。

1
2
3
4
5
6
7
8
9
10
11
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// ... 测量自定义View的大小
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// ... 确定自定义View的位置
super.onLayout(changed, left, top, right, bottom);
}

在处理测量和布局时,我们可以使用以下技巧:

  • 使用MeasureSpec:MeasureSpec可以帮助我们测量自定义View的大小,它包含了两个参数:spec和size。spec用于表示测量模式,size用于表示测量大小。
  • 使用LayoutParams:LayoutParams可以帮助我们设置自定义View的布局参数,比如宽度、高度、位置等。
  • 使用MeasureSpec和LayoutParams结合使用:我们可以使用MeasureSpec和LayoutParams结合使用来实现自定义View的自适应大小和位置。

以下是一个使用MeasureSpec和LayoutParams结合使用的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class MyView extends View {

private Paint mPaint; // 画笔

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

private void init() {
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(5);
mPaint.setStyle(Paint.Style.STROKE);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 指定自定义View的大小为200dp x 200dp
int width = MeasureSpec.makeMeasureSpec(dp2px(200), MeasureSpec.EXACTLY);
int height = MeasureSpec.makeMeasureSpec(dp2px(200), MeasureSpec.EXACTLY);
setMeasuredDimension(width, height);
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// 在父容器的中央放置自定义View
int parentWidth = ((ViewGroup) getParent()).getWidth();
int parentHeight = ((ViewGroup) getParent()).getHeight();
int viewWidth = getMeasuredWidth();
int viewHeight = getMeasuredHeight();
int x = (parentWidth - viewWidth) / 2;
int y = (parentHeight - viewHeight) / 2;
setLeft(x);
setTop(y);
setRight(x + viewWidth);
setBottom(y + viewHeight);
}

private int dp2px(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
}
}

结论

本文介绍了Android自定义View的步骤,并加入了一些开发技巧,包括使用局部变量、缓存、线程、GestureDetector、VelocityTracker、Scroller、MeasureSpec和LayoutParams等。希望这篇文章能够帮助到你,让你更好地掌握自定义View的技巧。 同时,需要注意性能和内存的问题,以及与其他控件的交互和兼容性。

推荐

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: 每日一算法,由浅入深,欢迎加入一起共勉。

支持一下
赞赏是一门艺术