自定义view

LayoutInflater:加载布局

基本用法:

mScrollView = (ScrollView) findViewById(R.id.scrollView_id);

//获取实例
LayoutInflater layoutInflater = LayoutInflater.from(this);

//加载布局
View buttonLayout = layoutInflater.inflate(R.layout.word_item, null);
mScrollView.addView(buttonLayout);

inflate()方法一般接收两个参数,第一个参数就是要加载的布局id,第二个参数是指给该布局的外部再嵌套一层父布局,如果不需要就直接传null。


先说一下,自己关于自定义控件Canvas,Paint的看法

Paint–画笔,你需要画什么样式的画,就需要什么样式的画笔

例:需要画一个红色的,10cm粗的矩形,那么你就要选择一个红色的,粗10cm的画笔

Canvas–可以将它理解为画布

那么,接下来,开始正文:


自定义view - - - view的绘制过程

有三个重要阶段onMeasure()、onLayout()和onDraw()

在layout的过程中需要用到measure过程中计算得到的每个View的测量大小,而draw过程需要layout确定每个view的位置才能进行绘制。

基本步骤:

1、自定义View的属性

2、在View的构造方法中获得我们自定义的属性

3、重写onMesure

4、重写onLayout(通常用于自定义viewGroup,自定义view很少用到)

5、重写onDraw


第一步:

自定义View的属性,首先在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式。

demo:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ScratchView">
        <!--蒙层的颜色-->
        <attr name="maskColor" format="color|reference" />
        <!--水印图片值-->
        <attr name="watermark" format="reference" />
        <!--擦除尺寸大小-->
        <attr name="eraseSize" format="float" />
        <!--最大比例-->
        <attr name="maxPercent" format="integer" />
    </declare-styleable>

</resources>

format是值该属性的取值类型

然后在布局中声明我们的自定义View,并设置属性值(不是必须)


第二步:在View的构造方法中获得我们自定义的属性

demo:

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

    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ScratchView);

    int maskColor = typedArray.getColor(R.styleable.ScratchView_maskColor, DEFAULT_MASKER_COLOR);      

    int watermarkResId = typedArray.getResourceId(R.styleable.ScratchView_watermark, -1);

    float eraseSize = typedArray.getFloat(R.styleable.ScratchView_eraseSize, DEFAULT_ERASER_SIZE);

    typedArray.recycle();
}

注意格式:typedArray.getFloat(R.styleable.ScratchView_***,defaultValue);


第三步:onMeasure()

onMeasure()方法,顾名思义就是用于测量视图的大小的。measure()方法接收两个参数,widthMeasureSpec和heightMeasureSpec,这两个值分别用于确定视图的宽度和高度的规格和大小。

MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格。specMode一共有三种类型,如下所示:

  1. MeasureSpec.EXACTLY:父视图希望子视图的大小是specSize中指定的大小,对应的也就是match_parent及固定尺寸

  2. MeasureSpec.AT_MOST:子视图的大小最多是specSize中的大小,对应的也就是wrap_content

  3. MeasureSpec.UNSPECIFIED:父视图不对子视图施加任何限制,这种情况比较少见,不太会用到。

获取测量模式、尺寸

int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);

如果你不想使用系统默认的测量方式,可以按照自己的意愿进行定制,重写onMeasure()方法。

如当我们需要绘制正方形,且android:layout_widthmatch_parent / wrap_content时,我们需要重写onMeasure( );

方式一:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(200, 200);
}

这样的话就把View默认的测量流程覆盖掉了,不管在布局文件中定义MyView这个视图的大小是多少,最终在界面上显示的大小都将会是200*200。

需要注意的是,在setMeasuredDimension()方法调用之后,我们才能使用getMeasuredWidth()和getMeasuredHeight()来获取视图测量出的宽高,以此之前调用这两个方法得到的值都会是0。

方式二:(我们这里给了一个默认值)

 private int getMySize(int defaultSize, int measureSpec) {
        int mySize = defaultSize;

        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);

        switch (mode) {
            case MeasureSpec.UNSPECIFIED: {//如果没有指定大小,就设置为默认大小
                mySize = defaultSize;
                break;
            }
            case MeasureSpec.AT_MOST: {//如果测量模式是最大取值为size
                //我们将大小取最大值,你也可以取其他值
                mySize = size;
                break;
            }
            case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改变它
                mySize = size;
                break;
            }
        }
        return mySize;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = getMySize(100, widthMeasureSpec);
        int height = getMySize(100, heightMeasureSpec);

        if (width < height) {
            height = width;
        } else {
            width = height;
        }

        setMeasuredDimension(width, height);
}

第四步:onLayout()

measure过程结束后,视图的大小就已经测量好了,接下来就是layout的过程了。正如其名字所描述的一样,这个方法是用于给视图进行布局的,也就是确定视图的位置。

host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight); 

layout()方法接收四个参数,分别代表着左、上、右、下的坐标,当然这个坐标是相对于当前视图的父视图而言的。

在onLayout()过程结束后,我们就可以调用getWidth()方法和getHeight()方法来获取视图的宽高了。

getMeasureWidth()方法在measure()过程结束后就可以获取到了,而getWidth()方法要在layout()过程结束后才能获取到。

另外,getMeasureWidth()方法中的值是通过setMeasuredDimension()方法来进行设置的,而getWidth()方法中的值则是通过视图右边的坐标减去左边的坐标计算出来的。

两者在一般情况下都是相等的,除非用户在onlayout的时候childView.layout(0, 0, 200, 200);,再次设置了具体值,而不是使用的getMeasureWidth()去计算,但此方式不推荐。


第五步:onDraw()

Paint,Canvas :http://www.cnblogs.com/yishujun/p/5559917.html


一、认识Paint:

Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色, 样式等绘制信息,指定了如何绘制文本和图形,画笔对象有很多设置方法,大体上可以分为两类,一类与图形绘制相关,一类与文本绘制相关。

以下是常用到的

图形绘制

  • 设置绘制的颜色,a代表透明度,r,g,b代表颜色值。
setARGB(int a,int r,int g,int b); 

类似:

//设置绘制图形的透明度
setAlpha(int a);  

//设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色
setColor(int color);  
  • 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE
    Style.FILL: 实心 STROKE:空心 FILL_OR_STROKE:同时实心与空心
setStyle(Paint.Style style);
  • 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式 Cap.ROUND,或方形样式Cap.SQUARE
setStrokeCap(Paint.Cap cap);  
  • 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
setStrokeWidth(float width); 
  • 设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢
setAntiAlias(boolean aa)
  • 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
setDither(boolean dither);
  • 图像在动画进行中是否会滤掉对Bitmap图像的优化操作,本设置项依赖于dither和xfermode的设置
setFilterBitmap(boolean filter); 
  • 设置图像效果,使用Shader可以绘制出各种渐变效果

setShader(Shader shader);

  • 设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果
setXfermode(Xfermode xfermode);  

文本绘制

  • 设置绘制文字的对齐方向
setTextAlign(Paint.Align align);  
  • 设置绘制文字x轴的缩放比例,可以实现文字的拉伸的效果
setTextScaleX(float scaleX);  
  • 设置绘制文字的字号大小
setTextSize(float textSize);  
  • 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等
 setTypeface(Typeface typeface);  
  • 设置带有下划线的文字效果
setUnderlineText(boolean underlineText); 
  • 设置带有删除线的效果
setStrikeThruText(boolean strikeThruText);  

二、认识Canvas

Canvas类简单理解就是表示一块画布,可以在上面画我们想画的东西

Canvas中的方法很多,Canvas可以绘制的对象有:

  • 弧线(arcs)
//基本语法
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) 

//参数说明
oval:RectF对象,定义的圆弧的形状和大小的范围。
startAngle:设置圆弧是从哪个角度来顺时针绘画的。
sweepAngle:设置圆弧扫过的角度。
useCenter:是否经过圆心

值得注意的是,这个参数在我们的 mPaint.setStyle(Paint.Style.STROKE); 设置为描边属性的时候,是看不出效果的。

paint:绘制时所使用的画笔。
  • 填充颜色(argb和color)

  • Bitmap

// 第一种:矩阵变换
drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint)

// 第二种:指定了图片左上角的坐标(距离坐标原点的距离)
drawBitmap (Bitmap bitmap, float left, float top, Paint paint)

// 第三种
drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint)

第三种 demo:

        // 将画布坐标系移动到画布中央
        canvas.translate(width/2,height/2);
        // 指定图片绘制区域(左上角的四分之一)
        Rect src = new Rect(0,0,bitmap.getWidth()/2,bitmap.getHeight()/2);
        // 指定图片在屏幕上显示的区域
        Rect dst = new Rect(0,0,200,200);
        // 绘制图片
        canvas.drawBitmap(bitmap,src,dst,null);

拓展:

//资源文件 (drawable/mipmap/ic_launcher):
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),R.mipmap.ic_launcher);

//资源文件(assets):
Bitmap bitmap=null;
try {
    InputStream is = mContext.getAssets().open("bitmap.png");
    bitmap = BitmapFactory.decodeStream(is);
    is.close();
} catch (IOException e) {
    e.printStackTrace();
}

//内存卡文件:
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/bitmap.png");

//网络文件:
Bitmap bitmap = BitmapFactory.decodeStream(is);
is.close();
  • 圆(circle和oval)
//基本语法
drawCircle(float cx, float cy, float radius,Paint paint)

//参数说明
cx:圆心X轴坐标
cy:圆心Y轴坐标
radius:圆半径
paint:绘制时所使用的画笔。
  • 点(point)
//基本语法
drawPoint(float x, float y, Paint paint)
  • 线(line)
//基本语法
drawLine(float startX, float startY, float stopX, float stopY, Paintpaint) 
  • 矩形(Rect)
//基本语法
drawRect(RectF rect, Paint paint)
  • 图片(Picture)
drawPicture (Picture picture)

drawPicture (Picture picture, Rect dst)

drawPicture (Picture picture, RectF dst)
  • 圆角矩形 (RoundRect)
//基本语法
drawRoundRect (RectF rect, float rx, float ry, Paint paint)

//参数说明
rect:RectF对象。
rx:x方向上的圆角半径。
ry:y方向上的圆角半径。
paint:绘制时所使用的画笔。
  • 文本(text)
//基本语法
drawText (String text, float x, float y, Paint paint)

//参数说明
text:文本。
x:x默认是这个字符串的左边在屏幕的位置。
y:y是指定这个字符baseline在屏幕上的位置。
paint:绘制时所使用的画笔。
  • 顶点(Vertices)
  • 路径(path)
//基本语法
drawPath(Path path, Paint paint)

方法:

  • 把当前的绘制的图像保存起来,让后续的操作相当于是在一个新的图层上的操作。
canvas.save();
  • 把当前画布返回(调整)到上一个save()状态之前
canvas.restore();  
  • 把当前画布的原点移到(dx,dy),后面的操作都以(dx,dy)作为参照点,默认原点为(0,0)
canvas.translate(dx, dy); 
  • 扩大,x为水平方向的放大倍数,y为竖直方向的放大倍数
canvas.scale(x,y);
  • 旋转,angle指旋转的角度,顺时针旋转。
canvas.rotate(angel);

RectF和Rect区别:精度的区别,一个float,一个int

RectF()构造一个无参的矩形
RectF(float left,float top,float right,float bottom)构造一个指定了4个参数的矩形

Rect和RecF的用法基本类似,只是参数为int类型,Rect(int left,int top,int right,int bottom)


常用Paint笔记:

    private void initPaint() {
        //画白色的圆画笔
        mPaint = new Paint();
        //抗锯齿
        mPaint.setAntiAlias(true);
        // 防抖动
        mPaint.setDither(true);
        // 开启图像过滤,对位图进行滤波处理。
        mPaint.setFilterBitmap(true);
        //画笔颜色
        mPaint.setColor(Color.parseColor("#38B072"));
        //空心圆
        mPaint.setStyle(Paint.Style.STROKE);
        //画笔宽度
        mPaint.setStrokeWidth(mCircleR);
        //设置笔刷样式为圆形
        mPaint.setStrokeCap(Paint.Cap.ROUND);
    }

demo:

小圆围绕大圆360旋转

链接下载地址:点这里,Github

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值