程序猿学习Android到处扒

Android自定义控件(一)_2015-03-22

2015-03-23  本文已影响773人  CoderGC

自定义控件教程:

1,http://blog.csdn.net/aigestudio/article/details/41212583
2,http://blog.csdn.net/aigestudio/article/details/41316141
3,http://blog.csdn.net/aigestudio/article/details/41447349
4,http://blog.csdn.net/aigestudio/article/details/41960507
5,http://blog.csdn.net/aigestudio/article/details/42677973
6,http://blog.csdn.net/aigestudio/article/details/42989325

1,Paint类

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);//反锯齿
        paint.setColor(Color.RED);
        /*
         * 设置画笔样式为描边,圆环嘛……当然不能填充不然就么意思了
         *
         * 画笔样式分三种:
         * 1.Paint.Style.STROKE:描边
         * 2.Paint.Style.FILL_AND_STROKE:描边并填充
         * 3.Paint.Style.FILL:填充
         */
        paint.setStyle(Paint.Style.FILL);
        /*
         * 设置描边的粗细,单位:像素px
         * 注意:当setStrokeWidth(0)的时候描边宽度并不为0而是只占一个像素
         */
        paint.setStrokeWidth(paintStrokeWidth);
    }

Paint的其他方法:


TextPaint专门用户绘制文字的画笔

/* 
 * 初始化文字画笔 
 */
textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(30);
textPaint.setTextAlign(Paint.Align.CENTER);

与画笔颜色相关的类

详情见博客:2,http://blog.csdn.net/aigestudio/article/details/41316141

PorterDuffXfermode类,图形混合模式的意思

2,View类的invalidate()方法,和postInvalidate()方法

在Android中提供了一个叫invalidate()的方法来让我们重绘我们的View。poistInvalidate()方法和invaliadate方法相同,只是它可以在子线程中运行.
nvalidate()方法只能在主线程中调用,而postInvalidate()方法可以在子线程中调用。

3,Shader类(着色器)

Paste_Image.png
Shader类呢也是个灰常灰常简单的类,它有五个子类,像PathEffect一样每个子类都实现了一种Shader,Shader在三维软件中我们称之为着色器,其作用嘛就像它的名字一样是来给图像着色的或者更通俗的说法是上色!这么说该懂了吧!再不懂去厕所哭去!这五个Shader里最异类的是BitmapShader,因为只有它是允许我们载入一张图片来给图像着色,那我们还是先来看看这个怪胎吧BitmapShader只有一个含参的构造方法BitmapShader (Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)

BitmapShader (Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)的第一个参数是位图这个很显然,而后两个参数则分别表示XY方向上的着色模式。
Shader.TileMode里有三种模式:CLAMP、MIRROR和REPETA。MIRROR镜像效果,REPETA重复,CLAMP的意思就是边缘拉伸的意思

4,Canvas 画布

Canvas我们一直称其为画布,其实更准确地说Canvas是一个容器,如果把Canvas理解成画板,那么我们的“层”就像张张夹在画板上的透明的纸,而这些纸对应到Android则是一个个封装在Canvas中的Bitmap。
除了save()方法Canvas还给我们提供了一系列的saveLayerXXX方法给我们保存画布,与save()方法不同的是,saveLayerXXX方法会将所有的操作存到一个新的Bitmap中而不影响当前Canvas的Bitmap,而save()方法则是在当前的Bitmap中进行操作,并且只能针对Bitmap的形变和裁剪进行操作,saveLayerXXX方法则无所不能,当然两者还有很多的不同.
save和saveLayerXXX方法有着本质的区别,saveLayerXXX方法会将所有操作在一个新的Bitmap中进行,而save则是依靠stack栈来进行
详情:http://blog.csdn.net/aigestudio/article/details/42677973

Paste_Image.png Paste_Image.png

注意:画布的平移旋转同样也会影响画布的自身坐标

5,Canvas类(画布)


Canvas所提供的各种方法根据功能来看大致可以分为几类,第一是以drawXXX为主的绘制方法,第二是以clipXXX为主的裁剪方法,第三是以scale、skew、translate和rotate组成的Canvas变换方法,最后一类则是以saveXXX和restoreXXX构成的画布锁定和还原。

  • drawBitmapMesh (Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)
    很吊毛的方法,但是计算复杂度很高,所有被沉默了。(一般不推荐使用,因为计算难度很大)
    详细原理http://blog.csdn.net/aigestudio/article/details/41960507

drawBitmapMesh是个很屌毛的方法,为什么这样说呢?因为它可以对Bitmap做几乎任何改变,是的,你没听错,是任何,几乎无所不能,这个屌毛方法我曾一度怀疑谷歌那些逗比为何将它屈尊在Canvas下,因为它对Bitmap的处理实在在强大了。上一节我们在讲到Matrix的时候说过Matrix可以对我们的图像做多种变换,实际上drawBitmapMesh也可以,只不过需要一点计算,比如我们可以使用drawBitmapMesh来模拟错切skew的效果。

ivMain = (ImageView) findViewById(R.id.main_iv);
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.RED);
ivMain.setImageBitmap(bitmap); 
clipPath(Path path, Region.Op op)
clipRect(Rect rect, Region.Op op)
clipRect(RectF rect, Region.Op op)
clipRect(float left, float top, float right, float bottom, Region.Op op)
clipRegion(Region region, Region.Op op) 

Canvas中有关裁剪的方法,你会发现有一大堆带有Region.Op参数的重载方法.要明白这些方法的Region.Op参数那么首先要了解Region为何物。Region的意思是“区域”,在Android里呢它同样表示的是一块封闭的区域。
详情请看 一下8中有关Region的简介
-canvas.drawPath(Path path, Paint paint) 绘制路径。
-canvas.drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint) ; 绘制文字在Path路径上。

6 Rect和RectF类

Rect和RectF是类似的,只不过RectF中涉及计算的时候数值类型均为float型,两者均表示一块规则矩形。重要的方法:

  • intersect(RectF rectF) / intersect (float left, float top, float right, float bottom) :此方法表示两个矩形相交的部分。取两个区域的相交区域作为最终区域。intersect方法的计算方式是相当有趣的,它不是单纯地计算相交而是去计算相交区域最近的左上端点和最近的右下端点
public class CanvasView extends View {  
    private Rect mRect;  
  
    public CanvasView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        mRect = new Rect(0, 0, 500, 500);  
  
        mRect.intersect(250, 250, 750, 750);  
    }  
  
    @Override  
    protected void onDraw(Canvas canvas) {  
        canvas.drawColor(Color.BLUE);  
  
        canvas.clipRect(mRect);  
  
        canvas.drawColor(Color.RED);  
    }  
图示.png
注意:黄色线框为后期加上的辅助线非程序生成
  • union(RectF rect) / union(float left, float top, float right, float bottom) 方法:union方法与intersect相反,取的是相交区域最远的左上端点作为新区域的左上端点,而取最远的右下端点作为新区域的右下端点
  • mRect.union(250, 250, 750, 750);

运行后我们会看到如下结果:

Paste_Image.png

是不是觉得不是我们想象中的那样单纯地两个区域相加。此方法是指的左上角的点和右下角的点的最远距离。

7 Path 类

此类表示一个路径

// 实例化路径  
mPath = new Path();    
// 移动点至[300,300]  
mPath.moveTo(100, 100);    
// 连接路径到点  
mPath.lineTo(300, 100);  
mPath.lineTo(400, 200);  
mPath.lineTo(200, 200);    
// 闭合曲线  
mPath.close(); 
Paste_Image.png
// 实例化路径  
mPath = new Path();    
// 移动点至[100,100]  
mPath.moveTo(100, 100);    
// 连接路径到点  
mPath.cubicTo(200, 200, 300, 0, 400, 100);  
三阶贝塞尔曲线.png
// 实例化路径  
mPath = new Path();    
// 移动点至[100,100]  
mPath.moveTo(100, 100);    
// 连接路径到点  
RectF oval = new RectF(100, 100, 200, 200);  
mPath.arcTo(oval, 0, 90); 
运行后.png
运行后效果跟我们想想的有点不一样,这是因为使用Path生成的路径必定都是连贯的,虽然我们使用arcTo绘制的时一段狐,但是其最终都会与我们Path的起点链接起来。如果你不想要链接,那么path也提供了一种方法,而已设置是否强制链接起点。
rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)  
rLineTo(float dx, float dy)  
rMoveTo(float dx, float dy)  
rQuadTo(float dx1, float dy1, float dx2, float dy2)  

这一系列rXXXTo方法其实跟上面的那些XXXTo差不多的,唯一的不同是rXXXTo方法的参考坐标是相对的而XXXTo方法的参考坐标始终是参照画布原点坐标。相对的指的是,终点的坐标是相对于起点的坐标的,例如rMoveTo(100,100) ; rLineTo(200,200) ,表示这个线段的长度是200而不是100.

addCircle(float x, float y, float radius, Path.Direction dir)  
addOval(float left, float top, float right, float bottom, Path.Direction dir)  
addRect(float left, float top, float right, float bottom, Path.Direction dir)  
addRoundRect(float left, float top, float right, float bottom, float rx, float ry, Path.Direction dir)  

这些方法和addArc有很明显的区别,就是多了一个Path.Direction参数Path.Direction只有两个常量值CCW和CW分别表示逆时针方向闭合和顺时针方向闭合
mPath.addOval(oval, Path.Direction.CW);顺时针

Paste_Image.png
mPath.addOval(oval, Path.Direction.CCW); 逆时针
Paste_Image.png
public class PathView extends View {  
    private Path mPath;// 路径对象  
    private Paint mPaint;// 路径画笔对象  
  
    public PathView(Context context, AttributeSet attrs) {  
        super(context, attrs);    
        /* 
         * 实例化画笔并设置属性 
         */  
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);  
        mPaint.setStyle(Paint.Style.STROKE);  
        mPaint.setColor(Color.CYAN);  
        mPaint.setStrokeWidth(5);    
        // 实例化路径  
        mPath = new Path();    
        // 移动点至[100,100]  
        mPath.moveTo(100, 100);    
        // 连接路径到点  
        mPath.lineTo(200, 200);    
        // 添加一条弧线到Path中  
        RectF oval = new RectF(100, 100, 300, 400);  
        mPath.addArc(oval, 0, 90);  
    }    
    @Override  
    protected void onDraw(Canvas canvas) {  
        // 绘制路径  
        canvas.drawPath(mPath, mPaint);  
    }  
} 
Paste_Image.png

8 Region

回顾Canvas中有关裁剪的方法,你会发现有一大堆带有Region.Op参数的重载方法:
clipPath(Path path, Region.Op op) clipRect(Rect rect, Region.Op op) clipRect(RectF rect, Region.Op op) clipRect(float left, float top, float right, float bottom, Region.Op op) clipRegion(Region region, Region.Op op)
要明白这些方法的Region.Op参数那么首先要了解Region为何物。Region的意思是“区域”,在Android里呢它同样表示的是一块封闭的区域,Region中的方法都非常的简单,我们重点来瞧瞧Region.Op,Op是Region的一个枚举类,里面呢有六个枚举常量:

Paste_Image.png

那么Region.Op究竟有什么用呢?其实它就是个组合模式,我们曾学过一个叫图形混合模式的,而在本节开头我们也曾讲过Rect也有类似的组合方法,Region.Op灰常简单,如果你看过图形混合模式的话。这里我就给出一段测试代码,大家可以尝试去改变不同的组合模式看看效果

public class CanvasView extends View {  
    private Region mRegionA, mRegionB;// 区域A和区域B对象  
    private Paint mPaint;// 绘制边框的Paint  
  
    public CanvasView(Context context, AttributeSet attrs) {  
        super(context, attrs);    
        // 实例化画笔并设置属性  
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);  
        mPaint.setStyle(Paint.Style.STROKE);  
        mPaint.setColor(Color.WHITE);  
        mPaint.setStrokeWidth(2);    
        // 实例化区域A和区域B  
        mRegionA = new Region(100, 100, 300, 300);  
        mRegionB = new Region(200, 200, 400, 400);  
    }    
    @Override  
    protected void onDraw(Canvas canvas) {  
        // 填充颜色  
        canvas.drawColor(Color.BLUE);    
        canvas.save();    
        // 裁剪区域A  
        canvas.clipRegion(mRegionA);    
        // 再通过组合方式裁剪区域B  
        canvas.clipRegion(mRegionB, Region.Op.DIFFERENCE);    
        // 填充颜色  
        canvas.drawColor(Color.RED);    
        canvas.restore();    
        // 绘制框框帮助我们观察  
        canvas.drawRect(100, 100, 300, 300, mPaint);  
        canvas.drawRect(200, 200, 400, 400, mPaint);  
    }  
}  

以下是各种组合模式的效果
DIFFERENCE


最终区域为第一个区域与第二个区域不同的区域。
INTERSECT

最终区域为第一个区域与第二个区域相交的区域。
REPLACE

最终区域为第二个区域。
REVERSE_DIFFERENCE

最终区域为第二个区域与第一个区域不同的区域。
UNION

最终区域为第一个区域加第二个区域。
XOR

最终区域为第一个区域加第二个区域并减去两者相交的区域。
Region.Op就是这样,它和我们之前讲到的图形混合模式几乎一模一样换汤不换药……我在做示例的时候仅仅是使用了一个Region,实际上Rect、Cricle、Ovel等封闭的曲线都可以使用Region.Op,介于篇幅,而且也不难以理解就不多说了。
有些童鞋会问那么Region和Rect有什么区别呢?
首先最重要的一点,Region表示的是一个区域,而Rect表示的是一个矩形,这是最根本的区别之一,其次,Region有个很特别的地方是它不受Canvas的变换影响,Canvas的local不会直接影响到Region自身,
什么意思呢?我们来看一个simple你就会明白:
public class CanvasView extends View {  
    private Region mRegion;// 区域对象  
    private Rect mRect;// 矩形对象  
    private Paint mPaint;// 绘制边框的Paint  
  
    public CanvasView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
  
        // 实例化画笔并设置属性  
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);  
        mPaint.setStyle(Paint.Style.STROKE);  
        mPaint.setColor(Color.DKGRAY);  
        mPaint.setStrokeWidth(2);  
  
        // 实例化矩形对象  
        mRect = new Rect(0, 0, 200, 200);  
  
        // 实例化区域对象  
        mRegion = new Region(200, 200, 400, 400);  
    }  
  
    @Override  
    protected void onDraw(Canvas canvas) {  
        canvas.save();  
  
        // 裁剪矩形  
        canvas.clipRect(mRect);  
        canvas.drawColor(Color.RED);  
  
        canvas.restore();  
  
        canvas.save();  
  
        // 裁剪区域  
        canvas.clipRegion(mRegion);  
        canvas.drawColor(Color.RED);  
  
        canvas.restore();  
  
        // 为画布绘制一个边框便于观察  
        canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mPaint);  
    }  
}  

大家看到,我在[0, 0, 200, 200]和[200, 200, 400, 400]的位置分别绘制了Rect和Region,它们两个所占大小是一样的:



画布因为和屏幕一样大,so~~我们看不出描边的效果,这时,我们将Canvas缩放至75%大小,看看会发生什么:

@Override  
protected void onDraw(Canvas canvas) {  
    // 缩放画布  
    canvas.scale(0.75F, 0.75F);  
  
    canvas.save();  
  
    // 裁剪矩形  
    canvas.clipRect(mRect);  
    canvas.drawColor(Color.RED);  
  
    canvas.restore();  
  
    canvas.save();  
  
    // 裁剪区域  
    canvas.clipRegion(mRegion);  
    canvas.drawColor(Color.RED);  
  
    canvas.restore();  
  
    // 为画布绘制一个边框便于观察  
    canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mPaint);  
}  

这时我们会看到,Rect随着Canvas的缩放一起缩放了,但是Region依旧泰山不动地淡定:


上一篇下一篇

猜你喜欢

热点阅读