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

笔记

尺寸单位转换

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值