《Android群英传》---读书笔记4

来源:互联网 发布:云计算paas平台 编辑:程序博客网 时间:2024/05/16 08:56

《Android群英传》—读书笔记4

标签: android中级 读书笔记


Android绘图机制与处理技巧

Android屏幕相关知识

2D绘图基础

系统通过提供的Canvans对象来提供绘图,如下所示:
drawPoint;
drawRect;
drawRoundRect;//绘制圆角矩形
drawLine;//绘制直线
drawLines;//绘制多条直线
drawVertices;绘制多边形
drawArc;绘制弧
drawOval;//绘制椭圆
drawCircle;
drawText;//绘制文本
drawPosText;//在指定位置绘制文本
drawPath

Canvans通常配合Paint来使用
Paint相关的属性和方法如下:
setAntiAlias();//设置画笔锯齿效果
setColor();
setARGB();
setAlpha();
setTextSize();
setStyle();//设置画笔是空心(FILL)还是实心(STROKE);
setStrokeWidth();//设置空心边框的宽度

Android XML绘图

在XML使用BitMap
< ?xml version=”1.0” encoding=”utf-8”?>
< bitmap xmlns:android=”http://schemas.android.com/apk/res/android”
android:src=”@drawable/ic_lanubcher”/>

Shape
通过Shape可以在XML中绘制各种形状,Shape功能十分强大,可以实现扁平化,拟物化,渐变等

Layer
类似于PS中的图层概念,非常容易实现叠加效果

Selector
可以帮助实现静态绘图中的事件反馈

Android绘图技巧

6.4.1 Canvas

(所有的绘制操作的默认原点是屏幕的左上角)
Canvas.save();//将之前所有的绘制保存起来,让后续的操作就像在一个新的图层上操作
Canvas.restore();//类似于PS中图层合并操作
Canvas.translate();//画布平移,或者叫做坐标系的平移,例如translate(x,y)之后,就将原点(0,0)移动到了(x,y)位置
Canvas.rorate();//画布旋转,或者叫做坐标系的旋转
例如下面的代码就绘制一个带指针和刻度的Clock

import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.util.AttributeSet;import android.view.View;public class Clock extends View {private int mHeight, mWidth;public Clock(Context context) {    super(context);}public Clock(Context context, AttributeSet attrs) {    super(context, attrs);}public Clock(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);}@Overrideprotected void onDraw(Canvas canvas) {    // 获取宽高参数    mWidth = getMeasuredWidth();    mHeight = getMeasuredHeight();    // 画外圆    Paint paintCircle = new Paint();    paintCircle.setStyle(Paint.Style.STROKE);    paintCircle.setAntiAlias(true);    paintCircle.setStrokeWidth(5);    canvas.drawCircle(mWidth / 2,            mHeight / 2, mWidth / 2, paintCircle);    // 画刻度    Paint painDegree = new Paint();    paintCircle.setStrokeWidth(3);    for (int i = 0; i < 24; i++) {        // 区分整点与非整点        if (i == 0 || i == 6 || i == 12 || i == 18) {            painDegree.setStrokeWidth(5);            painDegree.setTextSize(30);            canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,                    mWidth / 2, mHeight / 2 - mWidth / 2 + 60,                    painDegree);            String degree = String.valueOf(i);            canvas.drawText(degree,                    mWidth / 2 - painDegree.measureText(degree) / 2,                    mHeight / 2 - mWidth / 2 + 90,                    painDegree);        } else {            painDegree.setStrokeWidth(3);            painDegree.setTextSize(15);            canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,                    mWidth / 2, mHeight / 2 - mWidth / 2 + 30,                    painDegree);            String degree = String.valueOf(i);            canvas.drawText(degree,                    mWidth / 2 - painDegree.measureText(degree) / 2,                    mHeight / 2 - mWidth / 2 + 60,                    painDegree);        }        // 通过旋转画布简化坐标运算        canvas.rotate(15, mWidth / 2, mHeight / 2);    }    // 画圆心    Paint paintPointer = new Paint();    paintPointer.setStrokeWidth(30);    canvas.drawPoint(mWidth / 2, mHeight / 2, paintPointer);    // 画指针    Paint paintHour = new Paint();    paintHour.setStrokeWidth(20);    Paint paintMinute = new Paint();    paintMinute.setStrokeWidth(10);    canvas.save();    canvas.translate(mWidth / 2, mHeight / 2);    canvas.drawLine(0, 0, 100, 100, paintHour);    canvas.drawLine(0, 0, 100, 200, paintMinute);    canvas.restore();}}

6.4.2 Layer图层
在Android中使用saveLayer()方法创建一个图层,图层的管理是基于栈的
Android中通过Canvas的saveLayer(),saveLayerAlpha()将一个图层入栈,使用restore(),restoreToCount()将一个图层出栈。

Android图像处理之色彩特效处理

在Android中,系统使用一个颜色矩阵—ColorMatrix,来处理图像的色调,饱和度,亮度,Android颜色矩阵是一个4X5的数字矩阵:第一行决定R–红色,第二行决定G–绿色,第三行决定B–蓝色,第四行决定A–透明度

通过ColorMatrix改变色光属性
1获取实例
ColorMatrix colorMatrix=new ColorMatrix();
2通过颜色矩阵改变属性
colorMatrix.setRotate(int axis,float degree);//改变色调
colorMatrix.setSaturation(float sat);//改变饱和度
colorMatrix.setScale();//改变亮度

也可以通过postConcat()方法来将矩阵的作用效果混合:
ColorMatrix imageMatrix=new ColorMatrix();
imageMatrix.postConcat(hueMatrix);
imageMatrix.postConcat(saturationMatrix);
imageMatrix.postConcat(lumMatrix);
3使用Paint类的setColorFilter()方法,将ColorMatrix构造的的ColorMatrixColorFilter对象传递进去。然后使用这个画笔绘制原来的图像

Paint paint = new Paint();    paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));    canvas.drawBitmap(bitmap, 0, 0, paint);    (注意Android系统不允许直接修改原图,必须通过原图创建一个同样大小的Bitmap,并将原图绘制到该Bitmap中,以一个副本的形式来修改图像,代码如下:    Bitmap bmp = Bitmap.createBitmap(            bitmap.getWidth(),            bitmap.getHeight(),            Bitmap.Config.ARGB_8888);    Canvas canvas = new Canvas(bmp);    Paint paint = new Paint();    canvas.drawBitmap(bitmap, 0, 0, paint);   (关于构造Canvas传入的Bitmap对象,可以理解为bitmap为Paint作用的画布,以后在该canvas上调用的绘制方法都是绘制在这个画布上的)    )

当然也可以直接通过修改矩阵的值来实现颜色效果的处理:实现一个包含20个float值的一位数组,然后通过ColorMatrix的set方法,将一个一维数组的效果设置给该ColorMatrix的实例

    android.graphics.ColorMatrix colorMatrix =            new android.graphics.ColorMatrix();    colorMatrix.set(mColorMatrix);

6.5.3 常用的图像颜色矩阵的效果处理

灰度效果
图像反转
怀旧效果
去色效果
高饱和度
(每个效果都对应一个特定的颜色矩阵,这里各自详细的内容略)

6.5.4 像素点分析

该方法可以实现更加精确的图像处理方式,步骤如下
(注意原图不能直接修改,需要更据原始图片生成新的图片来修改)

1 Android中可以通过Bitmap.getPixels()来提取整个Bitmap中的像素点,并保存到一个数组中

bitmap.getPixels(pixels,offset,stride,x,y,width,height);通常可以使用如下代码bitmap.getPixels(oldPx,0,bm.getWidth(),0,0,width,height);

2 提取每个像素具体的ARGB值
color=oldPx[i];
r=Color.red(color);
g=Color.green(color);
h=Color.blue(color);
a=Colot.alpha(color);

3 更据相应算法修改它的ARGB,从而来重构一张新的图像

 r1 = (int) (0.393 * r + 0.769 * g + 0.189 * b); g1 = (int) (0.349 * r + 0.686 * g + 0.168 * b); b1 = (int) (0.272 * r + 0.534 * g + 0.131 * b);

4 在新的ARGB()合成新的像素点

newPx[i]=Color.argb(a,r1,g1,b1);

5 最后将处理后的像素点重新set给我们的Bitmap,
bmp.setPixels(newPx,0,width,0,0,width,height);

Android图像处理之图形特效处理

6.6.1 Android变形矩阵—Matrix
是一个3X3的矩阵:
a b c
d e f
g h f
其中a和e控制Scale—缩放变换
b和d控制Skew—错切变换
c和f控制Trans—平移变换
a ,b, d, e 共同控制Rotate—旋转变换

即规律如下
Scale_x Skew_x Trans_x
Skew_x Scale_y Trans_y
0 0 1

在图形变形矩阵中,同样可以通过一个一维数组,并通过setValues()方法将一个一维数组转换为图形变换矩阵,代码示例如下

private float[] mImageMatrix=new float[9];Matrix matrix=new Matrix();matrix.setValues(mImageMatrix);然后绘制canvas.drawBitmap(mBitmap,matrix,null);

Android系统也提供了API来简化矩阵的运算,Android提供Matrix类来简化矩阵的运算

setRotate()---旋转变换setTranslate()---平移变换setScale()---缩放变换setSkew()---错切变换pre()和post()---提供矩阵的前乘和后乘运算(set方法会重置矩阵中的所有矩阵中的所有值,而post(即后乘)和pre(即前乘)方法不会,这两个方法常用来实现矩阵的混合作用,例如 先平移到(300,100) 再旋转45度 最后平移到(200,200)则若采用后乘: matrix.setRotate(45);matrix.postTranslate(200,200);若采用前乘:matrix.setTranslate(200,200);matrix.setRotate(45);)

像素块分析
在进行图效的特效处理时也有两种方式来处理:即矩阵运算和像素块分析(即drawBitmapMesh(),为Canvas类的方法)
方法参数如下:

drawBitmapMesh(Bitmap bitmap,int meshWidth,int meshHeight,float[] verts,int vertOffset,int[] colors,int colorOffset,Paint paint);
其中
bitmap:将要扭曲的图像
meshWidth:需要的横向网格数目
meshHeight:需要的纵向网格数目
verts:网格交叉坐标数组
vertOffset:verts数组中开始跳过的(x,y)坐标对的数目

drawBitmapMesh()就是讲图像分成了一个个的小块,然后通过改变每一个图像来修改整个图像。此方法可以实现许多复杂的效果,不过对算法要求较高

以下代码实现了一个旗帜飘扬的效果import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.util.AttributeSet;import android.view.View;public class FlagBitmapMeshView extends View {private final int WIDTH = 200;private final int HEIGHT = 200;private int COUNT = (WIDTH + 1) * (HEIGHT + 1);private float[] verts = new float[COUNT * 2];private float[] orig = new float[COUNT * 2];private Bitmap bitmap;private float A;private float k = 1;public FlagBitmapMeshView(Context context) {    super(context);    initView(context);}public FlagBitmapMeshView(Context context, AttributeSet attrs) {    super(context, attrs);    initView(context);}public FlagBitmapMeshView(Context context, AttributeSet attrs,                          int defStyleAttr) {    super(context, attrs, defStyleAttr);    initView(context);}private void initView(Context context) {    setFocusable(true);    bitmap = BitmapFactory.decodeResource(context.getResources(),            R.drawable.test);    float bitmapWidth = bitmap.getWidth();    float bitmapHeight = bitmap.getHeight();    int index = 0;    for (int y = 0; y <= HEIGHT; y++) {        float fy = bitmapHeight * y / HEIGHT;        for (int x = 0; x <= WIDTH; x++) {            float fx = bitmapWidth * x / WIDTH;            orig[index * 2 + 0] = verts[index * 2 + 0] = fx;            orig[index * 2 + 1] = verts[index * 2 + 1] = fy + 100;            index += 1;        }    }    A = 50;}@Overrideprotected void onDraw(Canvas canvas) {    flagWave();    k += 0.1F;    canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT,            verts, 0, null, 0, null);    invalidate();}private void flagWave() {    for (int j = 0; j <= HEIGHT; j++) {        for (int i = 0; i <= WIDTH; i++) {            verts[(j * (WIDTH + 1) + i) * 2 + 0] += 0;            float offsetY =                    (float) Math.sin((float) i / WIDTH * 2 * Math.PI +                            Math.PI * k);//利用三角函数的周期来使图像动起来            verts[(j * (WIDTH + 1) + i) * 2 + 1] =                    orig[(j * WIDTH + i) * 2 + 1] + offsetY * A;        }    }}}

6.7 画笔特效处理

画笔的一些高级属性:

6.7.1 PorterDuffXfermode
即设置两个图层交集区域的显示方式,dst是先画的图形,src是后画的图形(常用的模式有DST_IN,SRC_IN)
使用PoterDuffXfermode非常简单,只需要让画面拥有这个属性就可以了

 mBitmap = BitmapFactory.decodeResource(getResources(),            R.drawable.test1);    mOut = Bitmap.createBitmap(mBitmap.getWidth(),            mBitmap.getHeight(), Bitmap.Config.ARGB_8888);    Canvas canvas = new Canvas(mOut);    mPaint = new Paint();    mPaint.setAntiAlias(true);    canvas.drawRoundRect(0, 0,            mBitmap.getWidth(),            mBitmap.getHeight(), 80, 80, mPaint);    mPaint.setXfermode(new PorterDuffXfermode(            PorterDuff.Mode.SRC_IN));    canvas.drawBitmap(mBitmap, 0, 0, mPaint);    以上代码绘制了一个圆角图片

6.7.2 Shader
Shader又被称之为着色器,渲染器,它用来一系列的渐变,渲染效果。Android中的Shader包括以下几种
BitmapShader—位图Shader
LinearGradient—线性Shader
RadialGradient—光束Shader
SweepGradient—梯度Shader
ComposeShader—混合Shader

注意BitmapShader产生的是一个图像,它的作用就是通过Paint对画布进行指定的Bitmap的填充,填充时有以下几种模式
CLAMP—拉伸,拉伸的是图片最后的那一个像素,不断重复
REPEAT—重复,横向,纵向不断重复
MIRROR—镜像,横向和纵向不断翻转重复

 mBitmap = BitmapFactory.decodeResource(getResources(),            R.drawable.ic_launcher);    mBitmapShader = new BitmapShader(mBitmap,            Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);    mPaint = new Paint();    mPaint.setShader(mBitmapShader);    canvas.drawCircle(500, 250, 200, mPaint);

6.7.3 PathEffect

PathEffect就是指用各种笔触效果来绘制一个路径

CornerPathEffect:使拐弯处变得圆滑

DiscretePathEffect:使用这个效果之后,线段上就会产生许多杂点
DashPathEffect:绘制虚线

PathDashPathEffect:与前面的DashPathEffect类似,不过功能更
加强大

ComposePathEffect:可以组合PathEffect效果

6.8 View之孪生兄弟—SurfaceView

SurfaceView与View的区别:SurfaceView更加适合频繁刷新界面的情况,更加适合处理刷新时更大数据量的处理,此时用SurfaceView取代View就比较合适

使用Surface的步骤

1创建SurfaceView
继承SurfaceView,并实现SurfaceHolder.callback和Runnable接口

2初始化SurfaceView
在自定义SurfaceView的构造方法中,对SurfaceView进行初始化,同时需要定义三个成员变量

private SurfaceHolder mHolder;private Canvas mCanvas;private boolean mIsDrawing//子线程标志位,SurfaceView是通过子线程来进行绘制的

初始化的的方式:
mHodler=getHolder();
mHolder.addCallback(this);

3使用SurfaceView
使用lockCanvas()可以获得当前的Canvas对象,接下来就可以像在View中绘制的方式一样绘制了。(获得的Canvas对象是上次绘制的对象,之前绘制的所有绘图操作都会保留,若需要擦除,可以调用drawColor()进行清屏操作).
在绘制的时候,充分利用SurfaceView的三个回调方法,在surfaceCreated()中开启子线程进行绘制,在绘制的具体逻辑中,通过lockCanvas()获得的Canvas进行绘制,并通过unlockCanvasAndPost(mCanvas)对画布内容进行提交。
整个的模版示例代码如下:

import android.content.Context;import android.graphics.Canvas;import android.util.AttributeSet;import android.view.SurfaceHolder;import android.view.SurfaceView;public class SurfaceViewTemplate extends SurfaceView    implements SurfaceHolder.Callback, Runnable {// SurfaceHolderprivate SurfaceHolder mHolder;// 用于绘图的Canvasprivate Canvas mCanvas;// 子线程标志位private boolean mIsDrawing;public SurfaceViewTemplate(Context context) {    super(context);    initView();}public SurfaceViewTemplate(Context context, AttributeSet attrs) {    super(context, attrs);    initView();}public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyle) {    super(context, attrs, defStyle);    initView();}private void initView() {    mHolder = getHolder();    mHolder.addCallback(this);    setFocusable(true);    setFocusableInTouchMode(true);    this.setKeepScreenOn(true);    //mHolder.setFormat(PixelFormat.OPAQUE);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {    mIsDrawing = true;    new Thread(this).start();}@Overridepublic void surfaceChanged(SurfaceHolder holder,                           int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {    mIsDrawing = false;}@Overridepublic void run() {    while (mIsDrawing) {        draw();    }}private void draw() {    try {        mCanvas = mHolder.lockCanvas();        // draw sth    } catch (Exception e) {    } finally {        if (mCanvas != null)            mHolder.unlockCanvasAndPost(mCanvas);    }}}

下面的代码可以绘制一个正弦曲线

import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.view.SurfaceHolder;import android.view.SurfaceView;public class SinView extends SurfaceView    implements SurfaceHolder.Callback, Runnable {private SurfaceHolder mHolder;private Canvas mCanvas;private boolean mIsDrawing;private int x = 0;private int y = 0;private Path mPath;private Paint mPaint;public SinView(Context context) {    super(context);    initView();}public SinView(Context context, AttributeSet attrs) {    super(context, attrs);    initView();}public SinView(Context context, AttributeSet attrs, int defStyle) {    super(context, attrs, defStyle);    initView();}private void initView() {    mHolder = getHolder();    mHolder.addCallback(this);    setFocusable(true);    setFocusableInTouchMode(true);    this.setKeepScreenOn(true);    mPath = new Path();    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);    mPaint.setColor(Color.RED);    mPaint.setStyle(Paint.Style.STROKE);    mPaint.setStrokeWidth(10);    mPaint.setStrokeCap(Paint.Cap.ROUND);    mPaint.setStrokeJoin(Paint.Join.ROUND);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {    mIsDrawing = true;    mPath.moveTo(0, 400);    new Thread(this).start();}@Overridepublic void surfaceChanged(SurfaceHolder holder,                           int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {    mIsDrawing = false;}@Overridepublic void run() {    while (mIsDrawing) {        draw();        x += 1;        y = (int) (100*Math.sin(x * 2 * Math.PI / 180) + 400);        mPath.lineTo(x, y);    }}private void draw() {    try {        mCanvas = mHolder.lockCanvas();        // SurfaceView背景        mCanvas.drawColor(Color.WHITE);        mCanvas.drawPath(mPath, mPaint);    } catch (Exception e) {    } finally {        if (mCanvas != null)            mHolder.unlockCanvasAndPost(mCanvas);    }}}

以下代码可以实现绘图板

import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.SurfaceHolder;import android.view.SurfaceView;public class SimpleDraw extends SurfaceView    implements SurfaceHolder.Callback, Runnable {private SurfaceHolder mHolder;private Canvas mCanvas;private boolean mIsDrawing;private Path mPath;private Paint mPaint;public SimpleDraw(Context context) {    super(context);    initView();}public SimpleDraw(Context context, AttributeSet attrs) {    super(context, attrs);    initView();}public SimpleDraw(Context context, AttributeSet attrs,                  int defStyle) {    super(context, attrs, defStyle);    initView();}private void initView() {    mHolder = getHolder();    mHolder.addCallback(this);    setFocusable(true);    setFocusableInTouchMode(true);    this.setKeepScreenOn(true);    mPath = new Path();    mPaint = new Paint();    mPaint.setColor(Color.RED);    mPaint.setStyle(Paint.Style.STROKE);    mPaint.setStrokeWidth(40);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {    mIsDrawing = true;    new Thread(this).start();}@Overridepublic void surfaceChanged(SurfaceHolder holder,                           int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {    mIsDrawing = false;}@Overridepublic void run() {    long start = System.currentTimeMillis();    while (mIsDrawing) {        draw();    }    long end = System.currentTimeMillis();    // 50 - 100,通过判断draw()方法所使用的逻辑时长来确定sleep的时长,这是一个非常通用的解决方案,代码中100ms是一个大致的经验图    if (end - start < 100) {        try {            Thread.sleep(100 - (end - start));        } catch (InterruptedException e) {            e.printStackTrace();        }    }}private void draw() {    try {        mCanvas = mHolder.lockCanvas();        mCanvas.drawColor(Color.WHITE);        mCanvas.drawPath(mPath, mPaint);    } catch (Exception e) {    } finally {        if (mCanvas != null)            mHolder.unlockCanvasAndPost(mCanvas);    }}@Overridepublic boolean onTouchEvent(MotionEvent event) {    int x = (int) event.getX();    int y = (int) event.getY();    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            mPath.moveTo(x, y);            break;        case MotionEvent.ACTION_MOVE:            mPath.lineTo(x, y);            break;        case MotionEvent.ACTION_UP:            break;    }    return true;}}
0 0
原创粉丝点击