《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;}}
- 《Android群英传》---读书笔记4
- Android 群英传读书笔记
- 《Android群英传》读书笔记
- 《Android群英传》读书笔记
- 《Android群英传》---读书笔记1
- 《Android群英传》---读书笔记2
- 《Android群英传》---读书笔记3
- 《Android群英传》---读书笔记5
- 《Android群英传》---读书笔记6
- 《Android群英传》---读书笔记7
- 《Android群英传》---读书笔记8
- 《Android群英传》---读书笔记9
- Android群英传读书笔记1
- Android群英传读书笔记-----控件架构
- Android群英传读书笔记---自定义控件(-)
- android群英传读书笔记---自定义ViewGroup
- 《Android群英传》读书笔记---10.2(终篇)
- 《Android群英传》——读书笔记
- 关于C++虚函数,纯虚函数以及模板等重要概念的深入讨论(一)
- jquery json
- Ubuntu让新增用户获取su
- java EE WEB 零基础到架构师教学培训资料视频
- 重新安装eclipse需要做的几件事
- 《Android群英传》---读书笔记4
- 编译原理初探(续)
- Android view(二)
- POJ 1061 青蛙的约会
- jmethodID的NewGlobalRef(NewGlobalRef for jmethodID)和DeleteLocalRef
- XX银行数据中心建设方案
- 《团队执行力》分享记录
- git协作
- finished with non-zero exit value 42的解决办法