Android自定义组件(一)

1.Activity 的组成结构

Activity代表一个窗口,其本质上是一个PhoneWindow对象,其负责窗口的管理,其负责的DecorView负责界面的效果。DecorView类是FrameLayout的子类,是整个View的根。各组件之间的关系如下图所示:

Android自定义组件(一)

(1)Activity负责整个容器的生命周期以及活动,窗口通过window来管理

(2)窗口的绘制以及渲染交给DecorView来完成

(3)从图中可以得知,开发人员定义的layout都是ContentParent的子视图 

Android自定义组件(一)

WindowManager将创建一个ViewRootImpl对象和WindowManagerService进行沟通。WindowManagerService能够获取触摸时间、键盘事件以及轨迹球事件,通过ViewRootImpl将事件分发给各个Activity,另外ViewRootImpl还负责整个Activity的GUI绘制。

 2.View树的绘制流程

(1)测量组件的大小:首先测量子组件的大小,最后才会确定该组件的大小

(2)确定组件的位置:首先确定顶层View的位置,然后从最顶层开始,从上向下确定各个组件的位置。

(3)组件的绘制:组件的绘制也是一个递归的过程,绘制过程是从根容器开始绘制,当根容器绘制结束后才会绘制子组件,直到所都的组件均绘制完成为止。(绘制背景background.draw(canvas)->绘制自己onDraw(canvas)->绘制子视图dispatchDraw(canvas)->绘制滚动条onDrawScrollBars(canvas))

继承View需要重写的方法:

(1)构造方法

(2)onDraw()方法,用来绘制View的图像

(3)如果需要改变View的大小,需要重写onMeasure()方法

(4)如果要改变其在父控件中的位置,需要重写onLayout()方法

继承ViewGroup需要重写的方法:

(1)重写OnMeasure()方法,测试子控件的大小。

(2)重写onLayout()方法,计算子控件的布局。

(3)在onDraw()方法中,绘制子控件,可有可无。

(4)监听onTouch事件,响应屏幕触摸事件。

 

3.Graphics2D

 1.Point和PointF类

1)Point类是最简单的结构,代表了一个点,实现了Parcelable接口,支持序列化和反序列化。

2)PointF类和Point类是完全相同的,唯一的区别是x和y的取值是float类型

2.Rect和RectF类

Rect类中需要正确理解left、top、right和bottom四个变量的作用:

Android自定义组件(一)

 矩形是一种常见的图形,能够衍生出更多的图形,如:椭圆、扇形、弧线等等。

3.Bitmap类和BitmapDrawable类

bitmap是用于储存png、jpg、gif等格式的图片数据,一般情况是先将图片读入bitmap对象,在对图像进行相关处理,图片的读取操作是由BitmapFactory类来完成的。(bitmap是相当占用系统资源的,所以要及时对其进行回收操作)

 在绘图中,通常采用“双缓存”技术,即先将图绘制到bitmap上,然后再统一展示出来。BitmapDrawable是一种通用的位图格式,但是相较于bitmap占用资源更少、性能更高。

4.Cavas类和Paint类

Canvas类中定义了绘图的方法,Paint类用于指定绘图参数。

Paint类用于定义绘图时的参数,主要包含颜色、文本、图形样式、位图模式、滤镜等方面。

绘制文本时,可以指定文本的大小、对齐方式、文本样式等属性,文本样式主要是文本指定粗体、下划线、删除线等修饰属性。

Canvas类封装了大量的绘图方法,绘制的图形受到Paint对象的影响。一般情况下,在绘制图形之前我们都先创建一个Paint对象,定义绘制的颜色、样式等。Paint是一个轻量级的对象,在程序中我们可以创建多个、也可以仅仅创建一个即可。

(1)绘制位图:

        //绘制一个原图和比原图片大三倍的图 均在一个画布上显示
        Bitmap bitmap = Bitmap.createBitmap(500, 800, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
        canvas.drawBitmap(bmp, 0, 0, null);
        //获取图片的高度和宽度
        int bmpWidth = bmp.getWidth();
        int bmpHeight = bmp.getHeight();
        Rect src = new Rect(0, 0, bmpWidth, bmpHeight);
        Rect dst = new Rect(0, bmpHeight, bmpWidth * 3, bmpHeight * 3);
        canvas.drawBitmap(bmp, src, dst, null);
        tmpIv.setImageBitmap(bitmap);

(2)绘制点:

        Bitmap bitmap = Bitmap.createBitmap(500, 800, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
//        画一个点
        Paint paint = new Paint();
        paint.setColor(Color.RED);
        paint.setStrokeWidth(8);//参数越大 点就越大
        canvas.drawPoint(120, 20, paint);//画一个点
        paint.setColor(Color.BLACK);
        float[] points = new float[]{10, 10, 50, 50, 50, 100, 250, 150};
        canvas.drawPoints(points, paint);
        paint.setColor(Color.GRAY);
        float[] points1 = new float[]{101, 101, 150, 150, 250, 100, 250, 160};
        canvas.drawPoints(points1, 1, 4, paint);//从offset:1开始取count:4个数字 每两个数字为一组
        iv.setImageBitmap(bitmap);

 (3)绘制线:

两个点确定一条直线,同画点一样,画直线同样有三个重载方法:

        
        Bitmap bitmap = Bitmap.createBitmap(500, 800, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        paint.setColor(Color.RED);
        paint.setStrokeWidth(8);//参数越大 点就越大
        canvas.drawLine(10, 20, 200, 300, paint);
        canvas.drawLines( float[],offset, counts, paint);
        canvas.drawLines( float[],paint);
        iv.setImageBitmap(bitmap);

 (4)绘制矩形:

绘制矩形有直角矩形和圆角矩形,正方形也是矩形的一种,所以Canvas没有提供正方形的画法。

绘制矩形时,参数分为两种,一种是直接指定left、top、bottom和right等四个参数,一种是直接指定一个Rect或者RectF对象。

绘制圆角矩形共提供了两个重载方法:

绘制圆角矩形不能分别为四个角指定圆角,而是统一设置成统一的值。

public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,Paint paint)和

public void drawRoundRect(RectF rect, float rx, float ry, Paint paint)
其中rx和ry不一定相同,其分别代表圆角处的水平半径和垂直半径,如果两个值不同则是椭圆上的一段弧线。

        Bitmap bitmap = Bitmap.createBitmap(500, 800, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        paint.setColor(Color.RED);
        canvas.drawRoundRect(10, 10, 400, 300, 50, 30, paint);
        paint.setStyle(Paint.Style.FILL);//FILL:填充内部 STROKE:仅边框 FILL_AND_STROKE:所有
        canvas.drawRoundRect(new RectF(10, 320, 400, 620), 30, 50, paint);
        iv.setImageBitmap(bitmap);

(5)绘制圆形:

绘制椭圆的大小是由它的外形相切矩形来决定的,所以绘制椭圆的方法有:

public void drawOval(float left, float top, float right, float bottom, Paint paint)和public void drawOval(RectF oval, Paint paint)
上面的两个方法均是定义了一个矩形结构,绘制的结果就是内切椭圆。

绘制椭圆时,如果长和宽相等,那么绘制出来的图形就是一个圆形,但是Canvas提供了更加简单的方法绘制圆形:

public void drawCircle(float cx, float cy, float radius, Paint paint)
其中:cx和cy是圆心坐标,radius是圆的半径

绘制弧线和扇形本质上是相同的,他们均是椭圆上的一段,绘制方法如下所示:

public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

public void drawArc(float left, float top, float right, float bottom, float startAngle, floatsweepAngle, boolean useCenter, Paint paint)
其中,startAngle表示起始的角度,正数为顺时针,负数为逆时针,sweepAngle表示扇形或者弧线所占的角度,userCenter为true表示扇形,为false表示弧线。

(6)绘制路径

Path是一个类,用于绘制复杂图形,所有的信息都存储在Path对象中,Canvas根据Path对象来绘制相应的图形。

Path的功能可以分为添加线条;矩形、椭圆、弧线;曲线和贝赛尔曲线;对图形进行运算。

Path对象中如果要添加矩形,椭圆、圆或者圆弧,则需要传递一个Path.Direction的参数,其中CW表示顺时针,CCW表示逆时针

        Bitmap bitmap = Bitmap.createBitmap(500, 800, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);

        Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        Path path = new Path();
        path.addRect(new RectF(10, 10, 300, 100), Path.Direction.CCW);
//        四个角的弧度均不相同,;两个数确定一个弧度
        path.addRoundRect(new RectF(10, 120, 300, 220),
                new float[]{10, 20, 20, 10, 30, 40, 40, 30}, Path.Direction.CCW);
        path.addOval(new RectF(10, 240, 300, 340), Path.Direction.CCW);
        path.addCircle(60, 390, 50, Path.Direction.CCW);
        path.addArc(new RectF(10, 500, 300, 600), -30, -60);
        canvas.drawPath(path,paint);
        iv.setImageBitmap(bitmap);

贝塞尔曲线又分为一节贝塞尔曲线、二阶贝赛尔曲线、三阶贝赛尔曲线以及高阶贝塞尔曲线,不过Path类支持二阶和三阶贝塞尔曲线,在画一条二阶贝塞尔曲线时,我们必须调用moveTo()方法定义一个起点,然后调用

public void quadTo(float x1, float y1, float x2, float y2)方法,其中x1,y1表示控制点,x2,y2表示终点。


        Bitmap bitmap = Bitmap.createBitmap(500, 800, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);

        Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        Path path = new Path();
        path.moveTo(100, 100);
        path.quadTo(200, 10, 300, 300);
        canvas.drawPath(path, paint);
        iv.setImageBitmap(bitmap);

三阶贝塞尔曲线有一个起点,两个控制点以及一个终点,Path类通过

public void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)进行控制,其中x3,y3表示终点。
 

我们还可以将多个Path进行图形运算,得到更加复杂或者不规则的图形:

1)Path.Op. DIFFERENCE
差集, 图形 A 减去与图形 B 重叠的区域后 A 余下的区域。
2)Path.Op. INTERSECT
交集, 图形 A 和图形 B 的重叠区域。
3)Path.Op. REVERSE_DIFFERENCE
反差集, 图形 B 减去与图形 A 重叠的区域后 B 余下的区域。
4)Path.Op. UNION
并集, 包含了图形 A 和图形 B 的所有区域。
5)Path.Op.XOR
补集, 即图形 A 和图形 B 的所有区域减去他们的重叠区域后余下的区域。

        Bitmap bitmap = Bitmap.createBitmap(500, 800, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);

        Paint paint = new Paint();
        paint.setAntiAlias(true);//抗锯齿
        paint.setStyle(Paint.Style.FILL);
        Path path1 = new Path();
        path1.addRect(new RectF(10, 10, 110, 110), Path.Direction.CCW);
        Path path2 = new Path();
        path2.addCircle(100, 100, 50, Path.Direction.CCW);
        path1.op(path2, Path.Op.XOR);
        canvas.drawPath(path1, paint);
//        paint.setColor(Color.RED);
//        canvas.drawPath(path2, paint);
        iv.setImageBitmap(bitmap);

(7)绘制文字:

Canvas为我们提供了两种绘制文字的方式,一种是从指定的位置开始绘制文字,另一种则是沿着Path绘制文字。

1) public void drawText(char[] text, int index, int count, float x, float y, Paint paint)
 public void drawText(String text, float x, float y, Paint paint)
 public void drawText(String text, int start, int end, float x, float y, Paint paint)
 public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint)
上面的方法均是从指定的位置开始绘制文字,其中x,y表示文字绘制的坐标位置。

2)public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)
 public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float
vOffset, Paint paint)

其中hOffset和vOffset分别定义了文字和Path之间的水平偏移量和垂直偏移量,正数和负数则影响着文字与路径的相对位置。

        Bitmap bitmap = Bitmap.createBitmap(500, 800, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        paint.setTextSize(14);
        String text = "哎哟~ 这个可是绘制文字哦~~~";
        canvas.drawText(text, 10, 50, paint);
        canvas.drawText(text, 0, 4, 10, 100, paint);
        canvas.drawText(text.toCharArray(), 4, 10, 10, 150, paint);
        Path path = new Path();
        path.moveTo(10, 200);
        path.quadTo(100, 100, 300, 300);
        canvas.drawTextOnPath(text, path, 15, 15, paint);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawPath(path, paint);
        iv.setImageBitmap(bitmap);