笔记
尺寸单位转换
public static int dp2px(int dp, Context context) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
context.getResources().getDisplayMetrics());
}
public static int dip2px(int dip,Context context){
float scale = context.getResources().getDisplayMetrics().density;
return (int) (dip*scale+0.5f);
}
public static int sp2px(int sp,Context context){
float scale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (sp*scale+0.5f);
}
Android绘图
先看下Canvas的操作
- save():将所有之前绘制的图像保存起来,让后续的操作好像在一个新的图层一样
- restore():将save之后绘制的图像和save之前的图像合并。
translate()和rotate():画布平移旋转,也可以理解过坐标的平移旋转,这样更加好理解。
接下来我们绘制一个表盘。
public class ClockView extends View {
Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint degreePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint hourPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint minutePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int height;
private int width;
public ClockView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public void init(){
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeWidth(5);
circlePaint.setColor(Color.RED);
hourPaint.setStrokeWidth(10);
minutePaint.setStrokeWidth(8);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
height = getMeasuredHeight();
width = getMeasuredWidth();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(width/2,height/2,width/2,circlePaint);
drawDegree(canvas);
drawHourAndMinute(canvas);
}
private void drawHourAndMinute(Canvas canvas) {
canvas.save();
canvas.translate(width/2,height/2);
canvas.drawLine(0,0,100,100,hourPaint);
canvas.drawLine(0,0,200,100,minutePaint);
canvas.drawRect(0,0,20,20,hourPaint);
canvas.restore();
}
public void drawDegree(Canvas canvas){
for (int i = 0; i < 24; i++) {
if(i==0||i==6||i==12||i==18){
degreePaint.setStrokeWidth(5);
degreePaint.setTextSize(30);
canvas.drawLine(width/2,height/2-width/2,width/2,height/2 - width/2+60,degreePaint);
String degree = String.valueOf(i);
canvas.drawText(degree,width/2-degreePaint.measureText(degree)/2,
height/2-width/2+90,degreePaint);
}else{
degreePaint.setStrokeWidth(3);
degreePaint.setTextSize(20);
canvas.drawLine(width/2,height/2-width/2,width/2,height/2 - width/2+30,degreePaint);
String degree = String.valueOf(i);
canvas.drawText(degree,width/2-degreePaint.measureText(degree)/2,
height/2-width/2+60,degreePaint);
}
canvas.rotate(15,width/2,height/2);
}
}
}
Layer图层
通过saveLayer()来创建一个图层,基于栈的结构进行管理。
相关方法:
saveLayer();
saveLayerAlpha()
restore:()
restoreToCount()
色彩特效处理
变换颜色值的两种方法
- 改变偏移量
改变颜色系数
颜色效果处理
色调:
matrix.setRotate();
- 饱和度:
matrix.setSaturation();
- 亮度:
matrix.setScale();
Android系统不允许修改原图,要先创建一个原图的副本,然后利用这个副本创建一个画布,最后把原图绘制到画布中。
Bitmap bmp = Bitmap.createBitmap(bm.getWidth(),bm.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();
canvas.drawBitmap(bm,0,0,paint);
通过ColorMatrix的set方法,可以将一个一维数组转化为ColorMatrix
Android图形处理
Android的图形变换是一个3X3的矩阵,图形变化的初始矩阵也是对角线全部为1的矩阵
图形变化包含下面四个基本操作
- 平移
- 旋转
- 缩放
- 错切
Shader
- BitmapShader
- LinearGradient
- RadiaGradient
- SweepGradient
- ComposeShader
填充模式
- CLAMP:拉伸,拉伸的是图片的最后一个像素,不断重复
- REPEAT:重复:横向,纵向不断重复
MIRROR:镜像:横向不断重复旋转,纵向不断重复旋转
SurfaceView
Android系统通过发出VSYNC信号来进行屏幕的重绘,刷新的时间间隔为16ms.
SurfaceView和View的区别
- View主要适用于主动更新情况,而SurfaceView主要适用于被动更新,例如频繁的刷新
- View在主线程中对画面刷新,而SurfaceView会通过一个子线程来刷新
- SurfaceView在底层实现了双缓冲机制
- 当程序需要更新View上的图像时,程序必须重绘View上显示的整张图片
如果你的自定义View需要频繁刷新,或者刷新时数据量比较大,就可以使用SurfaceView了。下面来看两个SurfaceView的例子
绘制正弦曲线
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable {
private SurfaceHolder surfaceHolder;
private Canvas canvas;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
//控制子线程
private boolean mIsDrawing;
private int x = 0;
private int y;
private Path mPath = new Path();
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
setKeepScreenOn(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
}
/**
* surfaceview创建
* @param holder
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
new Thread(this).start();
}
/**
* surfaceview改变
* @param holder
* @param format
* @param width
* @param height
*/
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
/**
* surfaceview销毁
* @param holder
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
@Override
public void run() {
while(mIsDrawing){
draw();
x+=1;
y = (int) (100*Math.sin(x*2*Math.PI/180)+400);
mPath.lineTo(x,y);
}
}
public void draw(){
try {
canvas = surfaceHolder.lockCanvas();
//设置画布背景颜色
canvas.drawColor(Color.WHITE);
canvas.drawPath(mPath,paint);
}catch (Exception e){
}finally {
//对画布内容进行提交,放在finally里面可以保证执行
if(canvas!=null){
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
画布
public class SurfaceViewCanvas extends SurfaceView implements SurfaceHolder.Callback,Runnable {
private SurfaceHolder mHolder;
private Canvas canvas;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private boolean mIsDrawing;
private Path mPath = new Path();
public SurfaceViewCanvas(Context context, AttributeSet attrs) {
super(context, attrs);
mHolder = getHolder();
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
setKeepScreenOn(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
new Thread(this).start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing =false;
}
@Override
public void run() {
long start = System.currentTimeMillis();
while(mIsDrawing){
draw();
}
long end = System.currentTimeMillis();
if(end - start <100){
try {
Thread.sleep(100-(end - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void draw(){
try {
canvas = mHolder.lockCanvas();
canvas.drawColor(Color.WHITE);
canvas.drawPath(mPath,paint);
}catch (Exception e){
}finally {
if(canvas!=null){
mHolder.unlockCanvasAndPost(canvas);
}
}
}
@Override
public 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;
}
}
与上一个例子不同的地方在于,我们对绘制的频率进行了限制,因为我们有时候不需要很频繁的绘制,这里的100mx是一个大致的经验证,这个值的范围一般在50ms到100mx之间。