Android重写View实现全新的控件

那么让我们来详细讲解一下“Android重写View实现全新的控件”的完整攻略。

什么是重写View

在Android中,View是用户界面的基本构建单元,绝大部分控件都是基于View的,因此我们可以通过重写View来实现我们自定义的控件。

在进行View的重写时,通常需要继承View或者它的子类,然后重写对应的方法。View的子类较多,它们之间的主要区别在于有一些默认的绘制行为和触摸反馈。

实现步骤

  1. 继承View或者它的子类,并重写对应方法。

扩展自View,通常需要重写onDraw方法。这是一个绘制你自定义View的方法,你需要在该方法中实现自己想要的绘制,例如绘制图像、绘制文字等。

public class MyView extends View {
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 在这里进行绘制操作
    }
}

扩展自ViewGroup,通常需要重写onLayout方法和onMeasure方法。onMeasure方法用于决定目标View的大小,onLayout用于确定它的位置。

public class MyViewGroup extends ViewGroup {
    public MyViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // 在这里进行子View的布局操作
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 在这里计算目标View的大小
    }
}
  1. 实现一些自定义属性。

在大多数情况下,自定义ViewGroup和自定义View通常需要一些自定义属性。这些属性允许你自定义View的各种方面,例如:颜色、大小、样式等。

可以通过在xml中定义属性,然后在自定义View中进行解析,来实现添加自定义属性的功能。下面是一个简单的例子。

在res/values/attrs.xml中添加以下内容:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyView">
        <attr name="my_attribute" format="string"/>
    </declare-styleable>
</resources>

在xml中使用MyView时,可以定义一个my_attribute,例如:

<com.example.MyView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:my_attribute="value"/>

在自定义View类中引入属性并解析:

public class MyView extends View {
    private String mAttributeValue;

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

        TypedArray a = context.getTheme().obtainStyledAttributes(
            attrs,
            R.styleable.MyView,
            0, 0);

        try {
            mAttributeValue = a.getString(R.styleable.MyView_my_attribute);
        } finally {
            a.recycle();
        }
    }
}
  1. 处理触摸输入事件。

如果你想让你的自定义View能够响应触摸事件,那么就需要处理它们。你可以通过覆盖onTouchEvent方法来处理输入事件。

public class MyView extends View {
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 处理点击事件
                break;
            case MotionEvent.ACTION_MOVE:
                // 处理移动事件
                break;
            case MotionEvent.ACTION_UP:
                // 处理放开事件
                break;
        }
        return true;
    }
}

示例

现在我们来看两个实际的例子,来更好的理解如何重写View创建自定义控件。

自定义圆形进度条

我们来尝试实现一个自定义的圆形进度条控件。实现的主要步骤如下:

  1. 自定义View来承载该控件。
public class CircleProgressBar extends View {
    // ...
}
  1. 实现属性。

定义自定义控件的一些属性,例如底部圆形颜色、进度圆环颜色、圆环宽度、最大进度值等。

<declare-styleable name="CircleProgressBar">
    <attr name="bottomColor" format="color"/>
    <attr name="progressColor" format="color"/>
    <attr name="strokeWidth" format="dimension"/>
    <attr name="maxValue" format="integer"/>
</declare-styleable>
  1. 重写onDraw方法,代码中包含如何画一个圆环及进度条的描述。
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //绘制背景
    canvas.drawArc(oval, startAngle, sweepAngle, false, mPaintBottom);
    //绘制进度条进度值
    canvas.drawArc(oval, startAngle, progressValue / maxValue * sweepAngle, false, mPaintProgress);
}
  1. 添加setXXX方法,并在其中进行相应的处理。
public void setProgressValue(int progress) {
    if (progress < 0) {
        throw new IllegalArgumentException("progress < 0");
    }
    this.progressValue = progress;
    invalidate();
}

自定义流式布局

流式布局是Android中较为受欢迎的布局方式之一,它可以自适应子View的宽高,从而在屏幕上按照一行或多行排列。

实现的主要步骤如下:

  1. 继承ViewGroup类。
public class FlowLayout extends ViewGroup {
    public FlowLayout(Context context) {
        super(context);
        inflater = LayoutInflater.from(context);
    }

    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        inflater = LayoutInflater.from(context);
    }

    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        inflater = LayoutInflater.from(context);
    }
    // ...
}
  1. 重写onMeasure方法,进行布局的测量和排列。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);

    int lineWidth = 0;
    int lineHeight = 0;

    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
        View childView = getChildAt(i);
        measureChild(childView, widthMeasureSpec, heightMeasureSpec);

        MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
        int childWidth = childView.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
        int childHeight = childView.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;

        if (lineWidth + childWidth > width) {
            width = Math.max(width, lineWidth);
            lineWidth = childWidth;
            height += lineHeight;
            lineHeight = childHeight;
        } else {
            lineWidth += childWidth;
            lineHeight = Math.max(lineHeight, childHeight);
        }

        if (i == childCount - 1) {
            width = Math.max(width, lineWidth);
            height += lineHeight;
        }
    }
    setMeasuredDimension(width, height);
}
  1. 重写onLayout方法,对子View进行排列。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    mAllViews.clear();
    mLineHeights.clear();

    int width = getWidth();

    int lineWidth = 0;
    int lineHeight = 0;

    List<View> lineViews = new ArrayList<>();
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
        View childView = getChildAt(i);

        MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
        int childWidth = childView.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
        int childHeight = childView.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;

        if (lineWidth + childWidth > width) {
            mLineHeights.add(lineHeight);
            mAllViews.add(lineViews);

            lineWidth = 0;
            lineHeight = childHeight;

            lineViews = new ArrayList<>();
        }

        lineWidth += childWidth;
        lineHeight = Math.max(lineHeight, childHeight);
        lineViews.add(childView);
    }

    mLineHeights.add(lineHeight);
    mAllViews.add(lineViews);

    int left = 0;
    int top = 0;
    int lineCount = mAllViews.size();
    for (int i = 0; i < lineCount; i++) {
        lineViews = mAllViews.get(i);
        lineHeight = mLineHeights.get(i);

        for (int j = 0; j < lineViews.size(); j++) {
            View childView = lineViews.get(j);
            if (childView.getVisibility() == View.GONE) {
                continue;
            }

            MarginLayoutParams layoutParams = (MarginLayoutParams) childView.getLayoutParams();
            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            int lc = left + layoutParams.leftMargin;
            int tc = top + layoutParams.topMargin;
            int rc = lc + childWidth;
            int bc = tc + childHeight;

            childView.layout(lc, tc, rc, bc);
            left += layoutParams.leftMargin + childWidth + layoutParams.rightMargin;
        }
        left = 0;
        top += lineHeight;
    }
}
  1. 添加removeAllViews和addView方法。
public void removeAllViews(boolean isRemoveHead) {
    if (isRemoveHead) {
        try {
            for (int i = getChildCount() - 1; i >= 0; i--) {
                removeViewAt(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    } else {
        removeAllViews();
    }
}

public void addChildView(View child, boolean isNewline) {
    addView(child, childLayoutParams(isNewline));
}

结语

以上是重写View实现全新的控件的攻略,可以通过这些步骤和例子来实现更多的自定义控件,如果你想深入学习Android自定义控件,可以学习更多的基本知识,例如如何绘制Path和Canvas、如何处理手势操作等等。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android重写View实现全新的控件 - Python技术站

(0)
上一篇 2023年6月11日
下一篇 2023年6月11日

相关文章

  • js实现日历

    当我们开发一个网站或应用时,通常需要为用户提供日历功能。在 Web 界面中,为用户提供日历的最常用方式是使用 JavaScript 实现。在这里,我将通过一些示例和说明,为大家介绍JavaScript实现日历的攻略。 1. 获取当前日期 要实现一个日历,我们首先需要获取当前日期,在 JavaScript 中可以使用 Date() 对象获取当前日期。 cons…

    JavaScript 2023年5月27日
    00
  • JavaScript中使用stopPropagation函数停止事件传播例子

    下面是详细讲解“JavaScript中使用stopPropagation函数停止事件传播”的攻略。 一、什么是事件传播 在 JavaScript 中,事件传播是指一个正在执行的事件被传递到多个目标元素时的行为。当事件发生时,它将从最深嵌套的 DOM 元素(称为目标)开始,然后传递到 DOM 树的根,逐步往上传递,直到文档的顶部。事件可以在传播的过程中被捕获和…

    JavaScript 2023年5月28日
    00
  • js获取判断上传文件后缀名的示例代码

    当需要上传文件时,有时我们需要判断文件的后缀名是否符合规范,例如只支持上传jpg、png、gif等图片格式。这时我们可以通过 JavaScript 来获取并判断上传文件的后缀名是否符合要求。下面是获取判断上传文件后缀名的示例代码的完整攻略: 1. 获取上传的文件信息 在 HTML 中,我们需要使用 <input> 标签的 type 属性为 “fi…

    JavaScript 2023年5月27日
    00
  • javascript异步编程

    下面我会来详细讲解“JavaScript 异步编程”的完整攻略,包括基本概念、异步编程方式、回调函数、Promise、async/await 等。 基础概念 在学习异步编程之前,我们需要了解以下几个基础概念: 同步代码 同步代码指的是按照代码的书写顺序,依次执行的代码,一行代码的执行需要等待上一行代码的执行完成。 console.log(‘start’); …

    JavaScript 2023年5月18日
    00
  • 6种javascript显示当前系统时间代码

    以下是关于“6种JavaScript显示当前系统时间代码”的详细攻略。 概述 在网页中显示当前系统时间是一项常见的需求,JavaScript提供了多种方法来实现这个目标。本文将介绍6种不同的实现方法,并提供示例代码。 方法1: JavaScript Date对象 JavaScript Date对象是处理日期和时间的常用对象,可以获取当前日期和时间。下面是获取…

    JavaScript 2023年5月27日
    00
  • 一文了解JavaScript闭包函数

    一文了解JavaScript闭包函数 JavaScript中的闭包函数是一种常见的概念,尤其常用于前端开发中。本文将对闭包函数进行详细讲解,帮助更好地理解它的概念和使用方法。 什么是JavaScript闭包函数? 在了解什么是闭包函数之前,我们先要了解嵌套函数的概念。在JavaScript中,我们可以在一个函数内部定义另一个函数,这个内部函数就是嵌套函数。 …

    JavaScript 2023年5月27日
    00
  • JavaScript中return false的用法

    JavaScript中return false的用法是一个非常基础的知识点,它主要用于阻止默认行为或事件冒泡,下面就详细讲解一下return false的使用方法。 一、阻止默认行为 我们首先要了解的是,当我们在网页中点击一个超链接或提交表单时,浏览器会自动执行默认行为,即跳转页面或提交表单。这时我们可以通过JavaScript来阻止默认行为的发生,具体方法…

    JavaScript 2023年5月28日
    00
  • 从零开始用electron手撸一个截屏工具的示例代码

    下面是从零开始用Electron手撸一个截屏工具的示例代码的攻略: 创建一个Electron项目 首先,我们需要使用npm来创建一个空的Electron项目,可以使用以下命令: npm init -y npm install electron –save-dev 安装完成后,我们需要在package.json文件中添加一个start script: &qu…

    JavaScript 2023年6月11日
    00
合作推广
合作推广
分享本页
返回顶部