Android画图系列(二)——自己定义View绘制基本图形

这个系列主要是介绍下Android自己定义View和Android画图机制。自己能力有限。假设在介绍过程中有什么错误。欢迎指正

前言

在上一篇Android画图系列(一)——自己定义View基础中我们了解自己定义View相关的基本知识。只是,这些东西依然还是理论,接下来我们就实际绘制一些东西

在本篇文章中,我们先了解下面Canvas,而且画一些主要的图形

Canvas简单介绍

Canvas我们能够称之为画布。能够在上面绘制各种东西。是安卓平台2D图形绘制的基础。非常强大。

一般来说,比較基础的东西有两大特点:

1.可操作性强:因为这些是构成上层的基础,所以可操作性必定十分强大。

2.比較难用:各种方法太过基础,想要完美的将这些操作组合起来有一定难度。**

Canvas的经常使用操作

操作类型 相关API 备注
绘制颜色 drawColor, drawRGB, drawARGB 使用单一颜色填充整个画布
绘制基本形状 drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc 依次为 点、线、矩形、圆角矩形、椭圆、圆、圆弧
绘制图片 drawBitmap, drawPicture 绘制位图和图片
绘制文本 drawText, drawPosText, drawTextOnPath 依次为 绘制文字、绘制文字时指定每一个文字位置、依据路径绘制文字
绘制路径 drawPath 绘制路径。绘制贝塞尔曲线时也须要用到该函数
顶点操作 drawVertices, drawBitmapMesh 通过对顶点操作能够使图像形变,drawVertices直接对画布作用、 drawBitmapMesh仅仅对绘制的Bitmap作用
画布剪裁 clipPath, clipRect 设置画布的显示区域
画布快照 save, restore, saveLayerXxx, restoreToCount, getSaveCount 依次为 保存当前状态、 回滚到上一次保存的状态、 保存图层状态、 回滚到指定状态、 获取保存次数
画布变换 translate, scale, rotate, skew 依次为 位移、缩放、 旋转、错切
Matrix(矩阵) getMatrix, setMatrix, concat 实际画布的位移。缩放等操作的都是图像矩阵Matrix,仅仅只是Matrix比較难以理解和使用。故封装了一些经常使用的方法。

PS: Canvas经常用法在上面表格中已经所有列出了。当然还存在一些其它的方法未列出,详细能够參考官方文档 Canvas


绘制基本图形

绘制颜色:

绘制颜色是填充整个画布。经常使用于绘制底色。

canvas.drawColor(Color.RED);

首先我们写一个类继承View,而且我们onDraw()方法中调用这种方法

package com.study.customview;

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

/**
 * Created by HFS on 2017/2/4.
 */

public class CustomView extends View {
    public CustomView(Context context) {
        super(context);
    }

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

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

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.RED);
    }
}

OK。看下效果

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvR3JlYXRoZnM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">


注意:接下来绘制的图形都须要另外一个參数:Paint 能够把Canvas理解为画布。Paint理解为画笔

  • 初始化画笔
  // 1.创建一个画笔
  private Paint mPaint = new Paint();

  // 2.初始化画笔
    private void initPaint() {
        mPaint.setColor(Color.BLACK);       //设置画笔颜色
        mPaint.setStyle(Paint.Style.FILL);  //设置画笔模式为填充
        mPaint.setStrokeWidth(10f);         //设置画笔宽度为10px
    }

  // 3.在构造函数中初始化
    public xxxView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

在创建完画笔之后。就能够在Canvas中绘制各种内容了。


绘制点

直接上代码了

package com.study.customview.widget;

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 HFS on 2017/2/4.
 */

public class PointView extends View {

    private Paint mPaint=new Paint();

    public PointView(Context context) {
        super(context);

        initPaint();
    }

    public PointView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    public PointView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(10f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画一个点
        canvas.drawPoint(200,300,mPaint);

        //画一组点
        canvas.drawPoints(new float[]{
                500,600,
                500,700,
                500,800,
                500,900

        },mPaint);
   }
}

效果图:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvR3JlYXRoZnM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

注意:坐标点都是相对于屏幕左上角而言的。坐标原点默认在左上角。水平向右为x轴增慷慨向,竖直向下为y轴增慷慨向。

绘制直线

直接上代码:

package com.study.customview.widget;

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 HFS on 2017/2/4.
 * 直线View
 */

public class LineView extends View {
    private Paint mPaint=new Paint();

    public LineView(Context context) {
        super(context);

        initPaint();
    }

    public LineView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    public LineView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(10f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //从(200,300)这个点開始 到(500,600)这个点结束
        canvas.drawLine(200,400,500,600,mPaint);
        // 绘制一组线 每四数字(两个点的坐标)确定一条线
        canvas.drawLines(new float[]{
                100,200,200,200,
                100,300,200,300
        },mPaint);
    }
}

效果图:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvR3JlYXRoZnM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

绘制矩形

关于绘制矩形,Canvas提供了三种重载方法

解释下:

第一种就是提供四个数值(矩形左上角和右下角两个点的坐标)来确定一个矩形进行绘制。

其余两种是先将矩形封装为Rect或RectF(实际上仍然是用两个坐标点来确定的矩形),然后传递给Canvas绘制

package com.study.customview.widget;

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

/**
 * Created by HFS on 2017/2/4.
 */

public class RectView extends View {
    private Paint mPaint=new Paint();

    public RectView(Context context) {
        super(context);

        initPaint();
    }

    public RectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    public RectView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(10f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //第一种
        canvas.drawRect(200,300,500,600,mPaint);
        //另外一种
        Rect rect=new Rect(200,300,500,600);
        canvas.drawRect(rect,mPaint);
        //第三种
        RectF rectF=new RectF(200,300,500,600);
        canvas.drawRect(rectF,mPaint);
    }
}

效果图

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvR3JlYXRoZnM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

为什么会有Rect和RectF两种?两者有什么差别吗?

答案当然是存在差别的,两者最大的差别就是精度不同。Rect是int(整形)的,而RectF是float(单精度浮点型)的

除了精度不同,两种提供的方法也略微存在差别。在这里我们临时无需关注,想了解很多其它參见官方文档 RectRectF


绘制圆角矩形:

相同有两个重载方法

一般我们使用第一个

package com.study.customview.widget;

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

/**
 * Created by HFS on 2017/2/4.
 * 圆角矩形View
 */

public class RoundRectView extends View {
    private Paint mPaint=new Paint();

    public RoundRectView(Context context) {
        super(context);

        initPaint();
    }

    public RoundRectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    public RoundRectView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(10f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        RectF rect=new RectF(100,100,800,400);
        canvas.drawRoundRect(rect,30,30,mPaint);
    }
}

效果图

注意:下面截取自http://www.gcssloop.com/customview/Canvas_BasicGraphics

下面简单解析一下圆角矩形的几个必要的參数的意思。

非常明显能够看出,另外一种方法前四个參数和第一种方法的RectF作用是一样的。都是为了确定一个矩形,最后一个參数Paint是画笔,无需多说,与矩形相比。圆角矩形多出来了两个參数rx 和 ry,这两个參数是干什么的呢?

略微分析一下。既然是圆角矩形,他的角肯定是圆弧(圆形的一部分),我们一般用什么确定一个圆形呢?

答案是圆心 和 半径,当中圆心用于确定位置,而半径用于确定大小

因为矩形位置已经确定。所以其边角位置也是确定的。那么确定位置的參数就能够省略,仅仅须要用半径就能描写叙述一个圆弧了。

可是,半径仅仅须要一个參数,但这里怎么会有两个呢?

好吧。让你发现了,这里圆角矩形的角实际上不是一个正圆的圆弧,而是椭圆的圆弧,这里的两个參数实际上是椭圆的两个半径。他们看起来个例如以下图:

红线标注的 rx 与 ry 就是两个半径,也就是相比绘制矩形多出来的那两个參数。

我们了解到原理后,就能够为所欲为了。通过计算可知我们上次绘制的矩形宽度为700,高度为300,当你让 rx大于350(宽度的一半)。 ry大于150(高度的一半) 时奇迹就出现了, 你会发现圆角矩形变成了一个椭圆。 他们画出来是这种 ( 为了方便确认我更改了画笔颜色, 同一时候绘制出了矩形和圆角矩形 ):

        // 矩形
        RectF rectF = new RectF(100,100,800,400);  

        // 绘制背景矩形
        mPaint.setColor(Color.GRAY);
        canvas.drawRect(rectF,mPaint);

        // 绘制圆角矩形
        mPaint.setColor(Color.BLUE);
        canvas.drawRoundRect(rectF,700,400,mPaint);

当中灰色部分是我们所选定的矩形,而里面的圆角矩形则变成了一个椭圆,实际上在rx为宽度的一半,ry为高度的一半时,刚好是一个椭圆,通过上面我们分析的原理推算一下就能得到,而当rx大于宽度的一半。ry大于高度的一半时。实际上是无法计算出圆弧的,所以drawRoundRect对大于该数值的參数进行了限制(修正)。凡是大于一半的參数均依照一半来处理。


绘制椭圆:


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //第一种
        RectF rectF=new RectF(100,100,800,400);
        canvas.drawOval(rectF,mPaint);

        //另外一种
        canvas.drawOval(100,100,800,400,mPaint);
    }
}

相同。以上两种方法效果全然一样,但一般使用第一种。

绘制椭圆实际上就是绘制一个矩形的内切图形,原理例如以下,就不多说了:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvR3JlYXRoZnM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描写叙述" title="">

PS: 假设你传递进来的是一个长宽相等的矩形(即正方形),那么绘制出来的实际上就是一个圆。


绘制圆:

绘制圆形也比較简单, 例如以下:


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //在(600,600)这个点绘制一个半径为300的圆
        canvas.drawCircle(600,600,300,mPaint);
    }

绘制圆形有四个參数。前两个是圆心坐标,第三个是半径,最后一个是画笔。


绘制圆弧:

绘制圆弧就比較奇妙一点了。为了理解这个比較奇妙的东西。我们先看一下它须要的几个參数:

// 第一种
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint){}

// 另外一种
public void drawArc(float left, float top, float right, float bottom, float startAngle,
            float sweepAngle, boolean useCenter, @NonNull Paint paint) {}

从上面能够看出,相比于绘制椭圆,绘制圆弧还多了三个參数:

startAngle  // 開始角度
sweepAngle  // 扫过角度
useCenter   // 是否使用中心

通过字面意思我们基本能推測出来前两个參数(startAngle, sweepAngel)的作用。就是确定角度的起始位置和扫过角度, 只是第三个參数是干嘛的?试一下就知道了,上代码:

        RectF rectF = new RectF(100,100,800,400);
        // 绘制背景矩形
        mPaint.setColor(Color.GRAY);
        canvas.drawRect(rectF,mPaint);

        // 绘制圆弧
        mPaint.setColor(Color.BLUE);
        canvas.drawArc(rectF,0,90,false,mPaint);

        //-------------------------------------

        RectF rectF2 = new RectF(100,600,800,900);
        // 绘制背景矩形
        mPaint.setColor(Color.GRAY);
        canvas.drawRect(rectF2,mPaint);

        // 绘制圆弧
        mPaint.setColor(Color.BLUE);
        canvas.drawArc(rectF2,0,90,true,mPaint);

上述代码实际上是绘制了一个起始角度为0度。扫过90度的圆弧,两者的差别就是是否使用了中心点。结果例如以下:

能够发现使用了中心点之后绘制出来相似于一个扇形,而不使用中心点则是圆弧起始点和结束点之间的连线加上圆弧围成的图形。这样中心点这个參数的作用就非常明显了,不必多说想必大家试一下就明确了



Ok,这篇就这些吧

Github地址

原文地址:https://www.cnblogs.com/zhchoutai/p/8718191.html

时间: 2024-10-12 14:09:04

Android画图系列(二)——自己定义View绘制基本图形的相关文章

Android自定义控件系列二:如何自定义属性

上一篇Android自定义控件系列一:如何测量控件尺寸 我们讲了如何确定控件的属性,这篇接着也是讲个必要的知识-如何自定义属性.对于一个完整的或者说真正有实用价值的控件,自定义属性是必不可少的. 如何为控件定义属性 在res/values/attrs.xml(attrs.xml如果不存在,可以创建个)中使用<declare-styleable>标签定义属性,比如我想定义个显示头像的圆形的图片控件(AvatarImageView): 01.<?xml version="1.0&q

Android自定义控件系列二:自定义开关按钮(一)

这一次我们将会实现一个完整纯粹的自定义控件,而不是像之前的组合控件一样,拿系统的控件来实现:计划分为三部分:自定义控件的基本部分,自定义控件的触摸事件的处理和自定义控件的自定义属性: 下面就开始第一部分的编写,本次以一个定义的开关按钮为例,下面就开始吧: 先看看效果,一个点击开关按钮,实现点击切换开关状态: 为了能够讲解清晰,还是来一些基本的介绍. 首先需要明确的就是自定义控件还是继承自View这个类,Google在View这个类里面提供了相当多的方法供我们使用,使用这些方法我们可以实现相当多的

Android学习系列(二)布局管理器之线性布局及其自定义实现

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39643669 LinearLayout是Android控件中的线性布局控件,它包含的子控件将以横向(HORIZONTAL)或竖向(VERTICAL)的方式排列,按照相对位置来排列所有的子控件及引用的布局容器.超过边界时,某些控件将缺失或消失.因此一个垂直列表的每一行只会有一个控件或者是引用的布局容器. 一.LinearLayout线性布局的相关属性说明: android:orientat

Android自定义控件系列(二)—icon+文字的多种效果实现

转载请注明出处:http://www.cnblogs.com/landptf/p/6290810.html 今天给大家带来一个很简单但是很常用的控件ButtonExtendM,在开发中我们经常会用到图片加文字的组合控件,像这样: 以上图片都是从微信上截取的.(暂时没有找到icon在下,文字在上的例子) 下面我们通过一个控件来实现上下左右全部的样式,只需改动一个属性值即可改变icon的位置,是不是很方便,先看下demo效果图: 没错上图的三种不同的样式都是通过同一个控件实现的,下面我们看下代码 第

ANDROID Porting系列二、配置一个新产品

详细说明 下面的步骤描述了如何配置新的移动设备和产品的makefile运行android. 1.         目录//vendor/创建一个公司目录   mkdir vendor/<company_name> 2.         创建一个目录下的公司与您在步骤1中创建产品目录.   mkdir vendor/<company_name>/products/ 3.         创建一个特定于产品的 makefile, 调用 vendor/<company_name&g

【Garry逆袭校招】之 计算机网络 知识点学习--Android地基系列(二)

转载请注明: http://write.blog.csdn.net/postedit/44019791 嗯,没什么好抱怨,再接再厉自己打气~,没学不会,可以理解,但是放任盲点!绝对不能容忍,被自己气到(打气太多了),哈哈哈,自己气自己,我真可爱~~,不说废话.(推荐搜索问题直接Ctrl +F) 以下内容为各路大牛提供,我只是个大牛的搬运工~~,做一个汇总,方便大家查找.(当然Garry我还需要看书学习,推荐书籍:计算机网络(谢希仁)),习得的过程不是一下子就完成的,Garry也没有完全吃透这些知

Android视图View绘制流程与源码分析(全)

来源:[工匠若水 http://blog.csdn.net/yanbober] 1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原理,记不记得最终分析结果就是下面的关系: 看见没有,如上图中id为content的内容就是整个View树的结构,所以对每个具体View对象的操作,其实就是个递归的实现. 前面<Android触摸屏事件派发机制详解与源码分析一(

Path类的最全面具体解释 - 自己定义View应用系列

前言 自己定义View是Android开发人员必须了解的基础:而Path类的使用在自己定义View绘制中发挥着很关键的数据 网上有大量关于自己定义View中Path类的文章.但存在一些问题:内容不全.思路不清晰.简单问题复杂化等等 今天.我将全面总结自己定义View中Path类的使用,我能保证这是市面上的最全面.最清晰.最易懂的 文章较长,建议收藏等充足时间再进行阅读 阅读本文前请先阅读自己定义View基础 - 最易懂的自己定义View原理系列 文件夹 1. 简单介绍 定义:路径.即无数个点连起

Android应用层View绘制流程与源码分析

Android应用层View绘制流程与源码分析 1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原理,记不记得最终分析结果就是下面的关系: 看见没有,如上图中id为content的内容就是整个View树的结构,所以对每个具体View对象的操作,其实就是个递归的实现. 前面<Android触摸屏事件派发机制详解与源码分析一(View篇)>文章的3-1