Android中级——屏幕和绘图

屏幕和绘图

  • 屏幕
    • 系统屏幕密度
    • 独立像素密度dp
    • 单位转换
  • XML绘图(需放在Drawable)
    • Bitmap
    • Shape
    • Layer
    • Selector
  • 绘图技巧
    • Canvas
    • Layer
    • PorterDuffXfermode
    • Shader
    • PathEffect
    • SurfaceView

屏幕

  • 屏幕大小:指屏幕对角线长度,单位为寸
  • 分辨率:指屏幕宽高的像素点个数,如720x1280
  • PPI:每英寸像素(Pixels Per Inch),指对角线的像素点数除以屏幕大小,又称屏幕密度DPI(Dots Per Inch)

系统屏幕密度

不同手机的大小和像素密度都不同,为统一,定义了几个标准的DPI值

在这里插入图片描述

独立像素密度dp

相同的像素,在不同密度的屏幕上显示,长度会不同,因为高密度的屏幕包含更多像素点

  • 规定密度为mdpi(即密度值为160)时1px=1dp
  • 故上图各分辨率的换算比例为3:4:6:8:12

单位转换

如下工具类提供了px、dp、sp的互相转换

public class DisplayUtil {
    // dp = 像素/密度
    public static int px2dip(Context context, float pxValue) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    public static int dip2px(Context context, float dipValue) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    // sp = 像素/缩放密度
    public static int px2sp(Context context, float pxValue) {
        float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }

    public static int sp2px(Context context, float spValue) {
        float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }
}

或可以使用TypedValue.applyDimension()方法

public int dp2px(int dp) {
    return (int) TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            dp,
            getResources().getDisplayMetrics());
}

public int sp2px(int sp) {
    return (int) TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_SP,
            sp,
            getResources().getDisplayMetrics());
}

XML绘图(需放在Drawable)

Bitmap

如下引用图片并转为Bitmap



Shape

Shape可实现不同类型的形状,如下实现渐变阴影



    
    
    

效果如图

Android中级——屏幕和绘图_第1张图片

Layer

Layer可实现类似PS中图层的概念



    
    

上面两个item分别为图片进行叠加,效果如图

Android中级——屏幕和绘图_第2张图片

Selector

Selector实现不同事件设置反馈,如下实现修改为圆角矩形,点击后切换颜色



    
        
            
            
            
        
    

    
        
            
            
            
        
    

绘图技巧

Canvas

下面为常用的方法

  • save():将已绘制图像保存,在此之后的操作绘制在新图层
  • restore():合并图层,将save()之后绘制的图像和save()之前的合并
  • translate():坐标系平移
  • ratate():坐标系旋转

如下绘制一个时钟,通过平移、旋转坐标系简化实现

public class Clock extends View {


    private int mWidth;
    private int mHeight;

    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);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 绘制外圆,以(mWidth / 2, mHeight / 2)为圆形,以mWidth / 2为半径
        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 paintDegree = new Paint();
        for (int i = 0; i < 24; i++) {
            if (i == 0 || i == 6 || i == 12 || i == 18) {
                paintDegree.setStrokeWidth(5);
                paintDegree.setTextSize(30);
                canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,
                        mWidth / 2, mHeight / 2 - mWidth / 2 + 60,
                        paintDegree);
                String degree = String.valueOf(i);
                canvas.drawText(degree,
                        mWidth / 2 - paintDegree.measureText(degree) / 2,
                        mHeight / 2 - mWidth / 2 + 90,
                        paintDegree);
            } else {
                paintDegree.setStrokeWidth(3);
                paintDegree.setTextSize(15);
                canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,
                        mWidth / 2, mHeight / 2 - mWidth / 2 + 30,
                        paintDegree);
                String degree = String.valueOf(i);
                canvas.drawText(degree,
                        mWidth / 2 - paintDegree.measureText(degree) / 2,
                        mHeight / 2 - mWidth / 2 + 60,
                        paintDegree);
            }
            // 将画布以圆心旋转15°,简化坐标运算
            canvas.rotate(15, mWidth / 2, mHeight / 2);
        }

        // 绘制指针
        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();
    }
}

Layer

图层基于栈结构,入栈后操作都发生在该图层上,出栈后则把图像绘制到上层Canvas

  • 入栈:saveLayer()、saveLayerAlpha()
  • 出栈:restore()、restoreToCount()
public class MyView extends View {

    private static final String TAG = MyView.class.getSimpleName();
    private Paint mPaint;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mPaint = new Paint();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        mPaint.setColor(Color.BLUE);
        canvas.drawCircle(150, 150, 100, mPaint);

        canvas.saveLayerAlpha(0, 0, 400, 400, 127);
        mPaint.setColor(Color.RED);
        canvas.drawCircle(200, 200, 100, mPaint);
        canvas.restore();
    }
}

如下图,透明度为127的红色圆叠加在蓝色圆上方

Android中级——屏幕和绘图_第3张图片

PorterDuffXfermode

PorterDuffXfermod设置的是两个图层交集区域的显示方式,dst是先画的图形,而src是后画的图形,共有16种模式,如图

Android中级——屏幕和绘图_第4张图片

如下实现圆角图形,mOut为先画的图像,mBitmap为后画的图像,用SrcIn取交集

public class MyView extends View {

    private static final String TAG = MyView.class.getSimpleName();
    private Bitmap mBitmap;
    private Bitmap mOut;
    private Paint mPaint;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
        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);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(mOut, 0, 0, null);
    }
}

效果如图

在这里插入图片描述

如下代码设置透明的画笔,实时获取坐标调用drawPath()涂抹上面的图层,实现刮刮乐

public class XfermodeView extends View {

    private Bitmap mBgBitmap, mFgBitmap;
    private Paint mPaint;
    private Canvas mCanvas;
    private Path mPath;

    public XfermodeView(Context context) {
        this(context, null);
    }

    public XfermodeView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint = new Paint();
        mPaint.setAlpha(0);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeWidth(50);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPath = new Path();
        mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.b);
        mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mFgBitmap);
        mCanvas.drawColor(Color.GRAY);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPath.reset();
                mPath.moveTo(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_MOVE:
                mPath.lineTo(event.getX(), event.getY());
                break;
        }
        mCanvas.drawPath(mPath, mPaint);
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mBgBitmap, 0, 0, null);
        canvas.drawBitmap(mFgBitmap, 0, 0, null);
    }
}

Android中级——屏幕和绘图_第5张图片

Shader

用于实现渐变、渲染效果,包括

  • BitmapShader——位图Shader
  • LinnerGradient——线性Shader
  • RadialGradient——光束Shader
  • SweepGradient——梯度Shader
  • ComposeShader——混合Shader

上述渐变都有三种模式选择:

  • CLAMP:拉伸,拉伸的是图片最后一个像素,不断重复
  • REPEAT:重复,横向、纵向不断重复
  • MIRROR:镜像,横向、纵向不断翻转重复

如下,将图片填充到BitmapShader,创建一支具有图像填充功能的Paint

public class MyView extends View {

    private static final String TAG = MyView.class.getSimpleName();
    private Bitmap mBitmap;
    private Paint mPaint;
    private BitmapShader mBitmapShader;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        mPaint = new Paint();
        mPaint.setShader(mBitmapShader);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(mBitmap.getWidth() / 2.0f, mBitmap.getHeight() / 2.0f, 50, mPaint);
    }
}

利用Paint在图片中心绘制圆形,效果如下

在这里插入图片描述
如下改为REPEAT,并扩大半径,可看到图片不断重复绘制

public class MyView extends View {

    private static final String TAG = MyView.class.getSimpleName();
    private Bitmap mBitmap;
    private Paint mPaint;
    private BitmapShader mBitmapShader;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        mPaint = new Paint();
        mPaint.setShader(mBitmapShader);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(mBitmap.getWidth() / 2.0f, mBitmap.getHeight() / 2.0f, 500, mPaint);
    }
}

Android中级——屏幕和绘图_第6张图片

如下利用PorterDuffXfermode和LinnerGradient实现倒影效果的图片

public class ReflectView extends View {
    private Bitmap mSrcBitmap, mRefBitmap;
    private Paint mPaint;
    private PorterDuffXfermode mXfermode;

    public ReflectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.b);

        Matrix matrix = new Matrix();
        matrix.setScale(1F, -1F);//垂直翻转

        mRefBitmap = Bitmap.createBitmap(mSrcBitmap, 0, 0,
                mSrcBitmap.getWidth(), mSrcBitmap.getHeight(), matrix, true);

        mPaint = new Paint();
        mPaint.setShader(new LinearGradient(0, mSrcBitmap.getHeight(),
                0, mSrcBitmap.getHeight() * 2,
                0xDD000000, 0x10000000, Shader.TileMode.REPEAT));

        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.BLACK);
        //原图
        canvas.drawBitmap(mSrcBitmap, 0, 0, null);
        //倒影图
        canvas.drawBitmap(mRefBitmap, 0, mSrcBitmap.getHeight(), null);

        mPaint.setXfermode(mXfermode);
        //绘制渐变效果层
        canvas.drawRect(0, mRefBitmap.getHeight(), mRefBitmap.getWidth(), mSrcBitmap.getHeight() * 2, mPaint);
        mPaint.setXfermode(null);
    }
}

先将图片翻转,再添加黑色透明度递减的渐变,DST_IN是为了避免上面的图像被渐变效果遮挡

Android中级——屏幕和绘图_第7张图片

PathEffect

PathEffect指用各种笔触效果来绘制一个路径
Android中级——屏幕和绘图_第8张图片

如上,分别为

  • 无效果
  • CornerPathEffect:线段拐角圆滑
  • DiscretePathEffect:线段出现杂点
  • DashPathEffect:绘制虚线,用一个数组来设置各个点之间的间隔,还可设置偏移量实现动态路径效果
  • PathPathEffect:类似DashPathEffect,但还可设置点的图形,如上面的圆点虚线
  • ConposePathEffect:组合效果
public class MyView extends View {

    private static final String TAG = MyView.class.getSimpleName();
    private Path mPath;
    private PathEffect[] mPathEffect;
    private Paint mPaint;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPath = new Path();
        mPath.moveTo(0, 0);
        for (int i = 0; i <= 30; i++) {
            mPath.lineTo(i * 35, (float) (Math.random() * 100));
        }
        mPathEffect = new PathEffect[6];
        mPathEffect[0] = null;
        mPathEffect[1] = new CornerPathEffect(30);
        mPathEffect[2] = new DiscretePathEffect(3.0F, 5.0F);
        mPathEffect[3] = new DashPathEffect(new float[]{20, 10, 5, 10}, 0);
        Path path = new Path();
        path.addRect(0, 0, 8, 8, Path.Direction.CCW);
        mPathEffect[4] = new PathDashPathEffect(path, 12, 0, PathDashPathEffect.Style.ROTATE);
        mPathEffect[5] = new ComposePathEffect(mPathEffect[3], mPathEffect[1]);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (PathEffect pathEffect : mPathEffect) {
            mPaint.setPathEffect(pathEffect);
            canvas.drawPath(mPath, mPaint);
            canvas.translate(0, 200);
        }
    }
}

SurfaceView

Android通过VSYNC信号刷新View进行屏幕的重绘,时间间隔为16ms,若在需要频繁刷新的界面上执行太多的操作逻辑,则容易阻塞主线程,造成画面卡顿

当需要频繁刷新或刷新时数据处理量比较大时,可使用SurfaceView

  • View用于主动刷新,SurfaceView用于被动刷新
  • View在主线程刷新,SurfaceView在子线程刷新
  • View绘图未使用双缓冲机制,SurfaceView则使用双缓冲机制

SurfaceView的模板代码如下,主要原理是初始化时自行创建子线程并调用draw()方法绘制

public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    //内部Holder,用于获取Canvas和提交绘制
    private SurfaceHolder mHolder;
    //用于控制线程的标志位
    private boolean mIsDrawing;
    //用于绘图的Canvas
    private Canvas mCanvas;

    public SurfaceViewTemplate(Context context) {
        this(context, null);
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mHolder = getHolder();
        mHolder.addCallback(this);  //将自身作为回调

        setFocusableInTouchMode(true);
        setFocusable(true);

        this.setKeepScreenOn(true);
    }

    //创建时开启线程,调用draw()
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsDrawing = true;

        new Thread(this).start();
    }

    //改变时回调
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    //结束时回调
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false;
    }

    @Override
    public void run() {
        while (mIsDrawing) {
            draw();
        }
    }

    private void draw() {
        try {
            mCanvas = mHolder.lockCanvas();
            //获取Canvas开始绘制,获取的并非新的Canvas,而是上次的
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mHolder != null) {
                //绘制完后需提交
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }
}

如下代码利用SurfaceView实现正弦函数的动态绘制

public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    private SurfaceHolder mHolder;
    private boolean mIsDrawing;
    private Canvas mCanvas;
    private int x;
    private int y;
    private Path mPath;
    private Paint mPaint;

    public SurfaceViewTemplate(Context context) {
        this(context, null);
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);

        mHolder = getHolder();
        mHolder.addCallback(this);

        setFocusableInTouchMode(true);
        setFocusable(true);

        this.setKeepScreenOn(true);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsDrawing = true;

        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @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);
        }
    }

    private void draw() {
        try {
            mCanvas = mHolder.lockCanvas();
            mCanvas.drawColor(Color.WHITE); //设置背景
            mCanvas.drawPath(mPath, mPaint);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mHolder != null) {
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }
}

效果如图

在这里插入图片描述

如下代码利用SurfaceView实现绘图板,根据手指移动绘制路径,在draw()中调用sleep()避免过度频繁绘制

public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    private SurfaceHolder mHolder;
    private boolean mIsDrawing;
    private Canvas mCanvas;
    private Path mPath;
    private Paint mPaint;

    public SurfaceViewTemplate(Context context) {
        this(context, null);
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);

        mHolder = getHolder();
        mHolder.addCallback(this);

        setFocusableInTouchMode(true);
        setFocusable(true);

        this.setKeepScreenOn(true);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsDrawing = true;

        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false;
    }

    @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;
    }

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        while (mIsDrawing) {
            draw();
        }
        long end = System.currentTimeMillis();
        if (end - start < 100) {
            SystemClock.sleep(100 - (end - start));
        }
    }

    private void draw() {
        try {
            mCanvas = mHolder.lockCanvas();
            mCanvas.drawColor(Color.WHITE);
            mCanvas.drawPath(mPath, mPaint);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (mHolder != null) {
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }
}

效果如图

Android中级——屏幕和绘图_第9张图片

你可能感兴趣的:(#,Android中级,android,java)