自定义一个圆圈View

这篇博客的目的是如何开发一个继承自View的自定义控件。最后的效果是能够得到一个任何页面都能使用的圆圈控件。代码很简单,也很粗陋。但是高楼平地起。
先贴代码,可以先不看,看完后面的解释再看代码:

package com.example.administrator.myfirstandroidstudioproject.customView;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by Administrator on 2016/4/13.
 */
public class CustomView extends View {
    //园的默认颜色
    private int mColor = Color.RED;
    //一个画笔,用于在onDraw函数中画圆
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private void init() {
        mPaint.setColor(mColor);
    }

    public CustomView(Context context) {
        this(context,null);
        init();
    }

    public CustomView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
        init();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //让warp生效,核心思想就是:
        // 当设置成warp时,就用控件自己本身的长度
        // (这个本身的长度不是说你在xml中配置的长度,而是比如一个字的长度是10px,那么有两个字的textView的长度就应该是20px)
        // 否则就是用控件测量的长度。之前一直以为测量的长度就是控件本身的长度,原来不是。
        // 测量的长度是通过父容器的MeasureSpec和自己的LayoutParams和本身的长度共同决定的结果。(这个就得看Android源码了)
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpaceMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpaceMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthSpaceMode == MeasureSpec.AT_MOST && heightSpaceMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(100, 100);
        } else if (widthSpaceMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(100, heightSpaceSize);
        } else if (heightSpaceSize == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpaceSize, 100);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //让padding生效,核心思想就是自己计算让padding之后的宽度和高度,最后画出来。
        final int paddingLeft = getPaddingLeft();
        final int paddingRight = getPaddingRight();
        final int paddingTop = getPaddingTop();
        final int paddingBottom = getPaddingBottom();
        int width = getWidth() - paddingLeft - paddingRight;
        int height = getHeight() - paddingTop - paddingBottom;
        int radius = Math.min(width, height) / 2;
        canvas.drawCircle(width / 2, height / 2, radius, mPaint);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

}

布局

    <com.example.administrator.myfirstandroidstudioproject.customView.CustomView
        android:id="@+id/circleView1"
        android:layout_width="wrap_content"
        android:layout_height="100px"
        android:background="#000000"
        />

形成一个View有三个过程。
1. onMeasure:测量控件的高和宽,并通过setMeasuredDimension(int measuredWidth, int measuredHeight)方法设置控件的高宽。
2. onLayout:计算控件在视图中的坐标。控件会按照这里设置的坐标决定自己在屏幕上的位置。
3. onDraw:画图,位置确定后,控件通过这个方法画出控件的图形。像上面的例子就是自己在控件的位置上话一个红色的圆。

知道了上面三个方法后就能解决继承View自定义控件所产生的两个问题:
问题1:wrap_content失效。给控件的布局中设置wrap_content将不会再有效果,解决方法参考上面的onMeasure代码。
问题2:padding失效。设置的padding将不会再有效。解决方法参考上面的onDraw方法。

如何给控件创建自定义属性
1. 在values文件夹下创建一个用于创建自定义属性的xml文件,这里就写成attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomView">
        <attr name="circle_color" format="color">
        </attr>
    </declare-styleable>
</resources>

declare-styleable元素指的是一个属性集合,里面可以有多个属性,在本例中只有一个颜色属性。还可以定义其他格式的属性,比如reference是id属性等等。
2. 在构造方法中解析这个xml文件,获取属性值,获得了属性的值,当然可以根据值来设置控件的样子了。

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        /*
        获取属性集合
         */
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
        /*
        从属性集合中获取单个属性
        第一个参数里的是获取的属性
        第二个参数是如果没有获取到属性的默认属性
         */
        mColor=a.getColor(R.styleable.CustomView_circle_color,Color.RED);
        /*
        释放资源
         */
        a.recycle();
        init();

    }
  1. 最后,在布局文件中使用自定义属性。很简单,只有一个地方需要注意,需要指定一个命名空间xmlns,就是下面的 xmlns:app=”http://schemas.android.com/apk/res-auto”,app这个名字可以随便取,报错的原因是,xml必须按照命名空间中的约定编写。这是为了规范xml的格式。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    tools:context="com.example.administrator.myfirstandroidstudioproject.MainActivity">

    <com.example.administrator.myfirstandroidstudioproject.customView.CustomView
        android:id="@+id/circleView1"
        android:layout_width="wrap_content"
        android:layout_height="100px"
        android:padding="20px"
        app:circle_color="@color/colorAccent"
        android:background="#000000"
        />
</LinearLayout>

注意的点:自定义view有三个构造函数
在代码中直接new一个Custom View实例的时候,会调用第一个构造函数。这个没有任何争议。
在xml布局文件中调用Custom View的时候,会调用第二个构造函数。这个也没有争议。
在xml布局文件中调用Custom View,并且Custom View标签中还有自定义属性时,这里调用的还是第二个构造函数。
所以,在第三个构造函数如果不在第二个函数里用this()调用的化,自定义的属性将不会有效

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一个使用 Qt 画图工具绘制自定义大小圆圈的例子: ``` #include <QApplication> #include <QWidget> #include <QPainter> int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget w; w.resize(400, 300); QPainter painter(&w); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::red); painter.setBrush(Qt::red); int x = 100; // 圆圈的 x 坐标 int y = 100; // 圆圈的 y 坐标 int radius = 50; // 圆圈的半径 painter.drawEllipse(x - radius, y - radius, radius * 2, radius * 2); w.show(); return a.exec(); } ``` 请注意,在这个例子圆圈的坐标和半径都是固定的。如果你希望使用鼠标动态改变圆圈的位置和大小,可以使用鼠标事件处理器(mouse event handler)来实现。你可以在鼠标按下时记录当前鼠标的位置,在鼠标移动时动态更新圆圈的位置和大小,在鼠标释放时结束绘制。 ### 回答2: 以下是一个使用Qt编写的用鼠标绘制自定义大小圆圈的代码示例: ```cpp #include <QtWidgets> class DrawingWidget : public QWidget { public: DrawingWidget(QWidget *parent = nullptr) : QWidget(parent) { setFixedSize(400, 400); setMouseTracking(true); } protected: void paintEvent(QPaintEvent *event) override { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); foreach (QPointF point, m_points) { painter.drawEllipse(point, m_radius, m_radius); } } void mousePressEvent(QMouseEvent *event) override { if (event->button() == Qt::LeftButton) { m_points.append(event->pos()); update(); } } void mouseMoveEvent(QMouseEvent *event) override { if (event->buttons() & Qt::LeftButton) { m_points.append(event->pos()); update(); } } private: QList<QPointF> m_points; int m_radius = 10; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); DrawingWidget widget; widget.show(); return app.exec(); } ``` 代码解析: 1. 创建一个名为`DrawingWidget`的自定义窗口部件,继承自`QWidget`。 2. 在构造函数设置窗口大小为400x400,并开启鼠标追踪的功能。 3. 重写`paintEvent`函数,在其使用`QPainter`绘制所有的圆圈。 4. 重写`mousePressEvent`函数,在鼠标左键按下时在当前鼠标位置绘制一个圆圈,并更新窗口。 5. 重写`mouseMoveEvent`函数,在鼠标左键按下并移动时,在当前鼠标位置添加一个圆圈,并更新窗口。 6. 在`main`函数创建一个`QApplication`实例和一个`DrawingWidget`实例,并展示窗口。 当运行该代码后,会弹出一个窗口。在该窗口内,当你按住鼠标左键并移动时,会在鼠标位置绘制一个自定义大小的圆圈。 ### 回答3: 以下是一个使用Qt框架编写的代码段,该代码允许使用鼠标绘制自定义大小的圆圈: ```cpp #include <QtWidgets> #include <QWidget> class MyWidget : public QWidget { public: MyWidget(QWidget *parent = nullptr) : QWidget(parent) {} protected: void paintEvent(QPaintEvent *) override { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(QPen(Qt::black, 2)); for (const QRectF &rect : m_circles) { painter.drawEllipse(rect); } } void mousePressEvent(QMouseEvent *event) override { m_lastPos = event->pos(); } void mouseMoveEvent(QMouseEvent *event) override { int width = abs(event->x() - m_lastPos.x()); int height = abs(event->y() - m_lastPos.y()); int x = qMin(event->x(), m_lastPos.x()); int y = qMin(event->y(), m_lastPos.y()); QRectF rect(x, y, width, height); m_circles.append(rect); update(); m_lastPos = event->pos(); } private: QList<QRectF> m_circles; QPoint m_lastPos; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); MyWidget widget; widget.show(); return app.exec(); } ``` 这段代码创建了一个自定义的QWidget子类`MyWidget`,当鼠标在窗口上拖动时,会根据鼠标的位置绘制一个矩形,然后在绘制的矩形上绘制一个圆形。 在`paintEvent`函数,我们使用`QPainter`类来绘制圆圈。`QPainter`类可以设置渲染提示和画笔样式,然后使用`drawEllipse`函数来绘制圆圈。`m_circles`列表保存绘制的所有圆圈的矩形区域。 在`mousePressEvent`和`mouseMoveEvent`函数,我们记录鼠标的位置,并计算出绘制圆圈所需的矩形的位置和大小。然后将该矩形添加到`m_circles`列表,并调用`update`函数来刷新窗口,触发`paintEvent`函数的调用来重新绘制圆圈。 最后,在`main`函数,我们创建了一个`MyWidget`对象并显示出来,然后启动应用程序的事件循环。 要使用该代码,请将其保存为.cpp文件,然后使用Qt编译器编译并运行。运行后,您可以在窗口上使用鼠标绘制自定义大小的圆圈

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值