Android的自定义View和自定义ViewGroup

Android 自定义视图(View)和视图组(ViewGroup)详解

在 Android 开发中,有时候我们需要创建一些标准控件无法满足需求的自定义视图(View)和视图组(ViewGroup)。本文将详细介绍如何创建自定义视图和视图组,包括构造方法、自定义属性、绘制逻辑、测量逻辑、布局逻辑和设置布局参数等内容。

1. 自定义视图(View)

1.1 构造方法

自定义视图类通常需要实现三个构造方法,以便在不同的场景下使用:

  1. 无参数构造方法:用于从代码中直接创建视图。
  2. AttributeSet 参数的构造方法:用于从 XML 布局文件中加载视图。
  3. AttributeSetdefStyleAttr 参数的构造方法:用于从 XML 布局文件中加载视图并支持样式主题。
public class CustomView extends View {
public CustomView(Context context) {
super(context);
init(null);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs) {
if (attrs != null) {
// 处理自定义属性
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CustomView);
String text = typedArray.getString(R.styleable.CustomView_customText);
int color = typedArray.getColor(R.styleable.CustomView_customColor, Color.BLACK);
typedArray.recycle();
}
}
}

1.2 自定义属性

自定义属性允许我们在 XML 布局文件中设置视图的属性。首先,需要在 res/values/attrs.xml 文件中定义自定义属性:

<resources>
<declare-styleable name="CustomView">
<attr name="customText" format="string"/>
<attr name="customColor" format="color"/>
</declare-styleable>
</resources>

然后,在 init 方法中读取并处理这些属性:

private void init(AttributeSet attrs) {
if (attrs != null) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CustomView);
String text = typedArray.getString(R.styleable.CustomView_customText);
int color = typedArray.getColor(R.styleable.CustomView_customColor, Color.BLACK);
typedArray.recycle();
}
}

1.3 绘制逻辑

重写 onDraw 方法来实现自定义绘制逻辑。onDraw 方法会在视图需要重新绘制时被调用。

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, 50, paint);
}

1.4 测量逻辑

重写 onMeasure 方法来实现自定义测量逻辑。onMeasure 方法会在视图需要重新测量时被调用。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int suggestedMinimumWidth = getSuggestedMinimumWidth();
int suggestedMinimumHeight = getSuggestedMinimumHeight();
int width;
int height;
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
width = Math.min(suggestedMinimumWidth, widthSize);
break;
case MeasureSpec.UNSPECIFIED:
width = suggestedMinimumWidth;
break;
default:
width = suggestedMinimumWidth;
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
height = Math.min(suggestedMinimumHeight, heightSize);
break;
case MeasureSpec.UNSPECIFIED:
height = suggestedMinimumHeight;
break;
default:
height = suggestedMinimumHeight;
break;
}
setMeasuredDimension(width, height);
}

1.5 使用自定义视图

XML 布局文件

在 XML 布局文件中使用自定义视图:

<com.example.myapp.CustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:customText="Hello, World!"
app:customColor="#FF0000" />

代码中动态添加

在 Java 或 Kotlin 代码中动态添加自定义视图:

CustomView customView = new CustomView(this);
setContentView(customView);

2. 自定义视图组(ViewGroup)

2.1 构造方法

自定义视图组类通常也需要实现三个构造方法,以便在不同的场景下使用:

  1. 无参数构造方法:用于从代码中直接创建视图组。
  2. AttributeSet 参数的构造方法:用于从 XML 布局文件中加载视图组。
  3. AttributeSetdefStyleAttr 参数的构造方法:用于从 XML 布局文件中加载视图组并支持样式主题。
public class CustomViewGroup extends ViewGroup {
public CustomViewGroup(Context context) {
super(context);
init();
}
public CustomViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 初始化代码
}
}

2.2 定义自定义 LayoutParams

首先,定义一个自定义的 LayoutParams 类,继承自 ViewGroup.LayoutParams,并添加自定义属性。

public class CustomViewGroupLayoutParams extends ViewGroup.LayoutParams {
public int customAttribute;
public CustomViewGroupLayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.CustomViewGroupLayoutParams);
customAttribute = a.getInt(R.styleable.CustomViewGroupLayoutParams_customAttribute, 0);
a.recycle();
}
public CustomViewGroupLayoutParams(int width, int height) {
super(width, height);
}
public CustomViewGroupLayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}

2.3 定义自定义属性

res/values/attrs.xml 文件中定义自定义属性。

<resources>
<declare-styleable name="CustomViewGroupLayoutParams">
<attr name="customAttribute" format="integer" />
</declare-styleable>
</resources>

2.4 创建自定义 ViewGroup

创建一个自定义的 ViewGroup 类,并重写 generateLayoutParams 方法来处理自定义的 LayoutParams

public class CustomViewGroup extends ViewGroup {
public CustomViewGroup(Context context) {
super(context);
init();
}
public CustomViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 初始化代码
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
width = Math.max(width, child.getMeasuredWidth());
height += child.getMeasuredHeight();
}
if (widthMode == MeasureSpec.AT_MOST) {
width = Math.min(width, widthSize);
}
if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(height, heightSize);
}
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int currentTop = 0;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
child.layout(l, currentTop, l + childWidth, currentTop + childHeight);
currentTop += childHeight;
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new CustomViewGroupLayoutParams(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof CustomViewGroupLayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new CustomViewGroupLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}
}

2.5 使用自定义 ViewGroup

XML 布局文件

在 XML 布局文件中使用自定义 ViewGroup,并设置自定义属性。

<com.example.myapp.CustomViewGroup
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.example.myapp.CustomButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:customAttribute="10" />
<com.example.myapp.CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:customAttribute="20" />
</com.example.myapp.CustomViewGroup>

代码中动态添加

在 Java 或 Kotlin 代码中动态添加自定义 ViewGroup 和子视图,并设置布局参数。

CustomViewGroup customViewGroup = new CustomViewGroup(this);
CustomButton customButton = new CustomButton(this);
CustomViewGroupLayoutParams buttonLayoutParams = new CustomViewGroupLayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
buttonLayoutParams.customAttribute = 10;
customButton.setLayoutParams(buttonLayoutParams);
customViewGroup.addView(customButton);
CustomTextView customTextView = new CustomTextView(this);
CustomViewGroupLayoutParams textViewLayoutParams = new CustomViewGroupLayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
textViewLayoutParams.customAttribute = 20;
customTextView.setLayoutParams(textViewLayoutParams);
customViewGroup.addView(customTextView);
setContentView(customViewGroup);

3. 性能优化

3.1 避免不必要的重绘

onDraw 方法中避免不必要的绘制操作,例如缓存复杂的绘制结果。

3.2 使用硬件加速

AndroidManifest.xml 中启用硬件加速:

<application
android:hardwareAccelerated="true">
</application>

3.3 减少布局层次

尽量减少嵌套的布局层次,避免过度嵌套导致性能下降。

总结

通过本文,我们详细介绍了如何创建自定义视图和视图组,包括构造方法、自定义属性、绘制逻辑、测量逻辑、布局逻辑和设置布局参数等内容。自定义视图和视图组可以让我们更灵活地创建满足特定需求的 UI 组件,提高应用的用户体验。

希望这篇文章对你有所帮助!如果有任何问题或建议,请随时留言交流。



__EOF__

本文作者小书童
本文链接https://www.cnblogs.com/ruiruizhou/p/18524792.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   还要再努力一些吧  阅读(608)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2021-11-04 Java基础学习(三)
2021-11-04 Java基础学习(二)
点击右上角即可分享
微信分享提示