首页 > 其他分享 >Android的自定义View和自定义ViewGroup

Android的自定义View和自定义ViewGroup

时间:2024-11-04 11:09:22浏览次数:1  
标签:ViewGroup 自定义 int 视图 attrs Android public

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 组件,提高应用的用户体验。

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


标签:ViewGroup,自定义,int,视图,attrs,Android,public
From: https://www.cnblogs.com/ruiruizhou/p/18524792

相关文章

  • 「Java开发指南」如何自定义Spring代码生成?(一)
    搭建用户经常发现自己对生成的代码进行相同的修改,这些修改与个人风格/偏好、项目特定需求或公司标准有关,本教程演示自定义代码生成模板,您将学习如何:创建自定义项目修改现有模板来包含自定义注释使用JET和Skyway标记库中的标记配置项目来使用自定义MyEclipsev2024.1离线版......
  • PbootCMS如何在网站管理后台增加点击数自定义修改功能
    修改方法:模板增加点击数输入框:修改文件:APPs/admin/view/default/content/content.html搜索 {if([$mod])},在其下方添加:<divclass="layui-form-item"><labelclass="layui-form-label">浏览量</label><divclass="layui-input-block&......
  • FontDialogTest自定义字体对话框的使用
    packagecom.shrimpking.t1;importjavax.swing.*;importjava.awt.*;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;/***CreatedbyIntelliJIDEA.**@Author:Shrimpking*@create2024/11/310:44*/publicclassFontDial......
  • echarts 画一个自定义饼图
    varchartDom=document.getElementById("chart_1");varmyChart=echarts.init(chartDom);varoption;option={tooltip:{trigger:"item",},legend:{show:false,top:"5%",lef......
  • Laravel 11.x 未认证用户如何自定义重定向
    很久没玩Laravel,这次将原来的一个内容网站升级到最新版Laravel,发现版本已从laravel7.x到laravel11.x了,网站比较简单大部分更新都很顺利。但是也遇到了问题网站前端和后台分别使用了2套用户登录验证,前端使用了laravel/ui的用户认证,打开需要用户认证的界面时,能正常跳转到登录......
  • Android Studio启动安卓模拟器失败,出现The emulator process for AVD Medium_Phone_AP
    前言软件版本已安装的SDKTools包。AndroidStudio安装设置Proxy代理问题。可在此处设置代理,可在本窗口的左下角的CheckConnection处进行检测链接的有效性。也可以查看以下地址,设置代理的地址:阿里云Android仓库清华大学开源软件镜像站模拟器问题如果你在这里运行......
  • 开发 Android 应用时,可以使用以下几种编程语言:
    开发Android应用时,可以使用以下几种编程语言:###1.**Java**-**描述**:Java是Android开发的传统语言,广泛使用,并且有丰富的文档和社区支持。-**特点**:面向对象,适合大型应用开发。AndroidSDK和大多数库均支持Java。###2.**Kotlin**-**描述**:Kotlin是官方推荐的Andro......
  • react的自定义hook的使用场景
    场景一:共享状态逻辑类似于vuex,reduximport{useState}from'react';//自定义钩子useCounterfunctionuseCounter(initialCount){//useState可以类比vuex中的state定义const[count,setCount]=useState(initialCount); //以下的setCount方法可以类比vuex中的mutatio......
  • 【NOI】C++函数入门二(自定义函数)
    文章目录前言一、概念1.导入1.1首先什么是函数呢?2.函数分类3.为什么要定义函数呢?4.函数结构5.函数使用注意事项二、例题讲解问题:1137-纯粹素数问题:1258-求一个三位数问题:1140-亲密数对问题:1149-回文数个数三、总结四、感谢前言在这一章节中,我们将深入探......
  • FPGA实例——按键消抖和自定义IP封装
    按键消抖:简介:目前,在大部分的FPGA开发板上都带有机械按键,由于机械按键的物理特性,按键在按下和释放的过程中,存在一段时间的抖动,这就导致在识别按键的时候可以检测到多次的按键按下,而通常检测到一次按键输入信号的状态为低电平,就可以确认按键被按下了,所以我们在使用按键时往往需......