Android群英传读书笔记第六章(Android绘图机制与处理技巧)

来源:互联网 发布:淘宝开店推广费用 编辑:程序博客网 时间:2024/05/16 00:37

笔记

尺寸单位转换

   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之间。

0 0
原创粉丝点击