Android绘图机制与处理技巧

参考资料


目录

  • 1)屏幕尺寸信息
  • 2)2D绘图基础
  • 3)XML绘图
  • 4)绘图技巧
  • 5)图像处理-色彩特效处理
  • 6)图像处理-图形特效处理
  • 7)图像处理-画笔特效处理
  • 8)SurfaceView

1)屏幕尺寸信息

ldpi mdpi hdpi xhdpi xxhdpi
120 160 240 320 480
240x320 320x480 480x800 720x1280 1080x1920
1dp=1px 1dp=1.5px 1dp=2px 1dp=3px
//单位转换工具
public class DisplayUtil {
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    public static int dip2px(Context context, float dipValue) {
//        final float scale = context.getResources().getDisplayMetrics().density;
//        return (int) (dipValue * scale + 0.5f);
        //或者使用系统的TypedValue类来转换
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, context.getResources().getDisplayMetrics());
    }

    public static int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }

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

TypedValue applyDimension源码

  public static float applyDimension(int unit, float value,  
                                     DisplayMetrics metrics)  
  {  
      switch (unit) {  
      case COMPLEX_UNIT_PX:  
          return value;  
      case COMPLEX_UNIT_DIP:  
          return value * metrics.density;  
      case COMPLEX_UNIT_SP:  
          return value * metrics.scaledDensity;  
      case COMPLEX_UNIT_PT:  
          return value * metrics.xdpi * (1.0f/72);  
      case COMPLEX_UNIT_IN:  
          return value * metrics.xdpi;  
      case COMPLEX_UNIT_MM:  
          return value * metrics.xdpi * (1.0f/25.4f);  
      }  
      return 0;  
  }  

2)2D绘图基础(参见自定义View)


3)XML绘图

  • Bitmap,这样引用可以将图片直接转成Bitmap使用
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
 android:src="@drawable/xxx"/>
  • Shape
    新建文件res/drawable/shape_test.xml
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    //默认为rectangle矩形,oval椭圆,line横线,ring圆环
    android:shape=["rectangle"|"oval"|"line"|"ring"]>
   <corners   //当shape="rectangle"时使用
      //半径,会被后面的单个半径属性覆盖,默认为1dp
      android:radius="integer"
      android:topLeftRadius="integer"
      android:topRightRadius="integer"
      android:bottomLeftRadius="integer"
      android:bottomRightRadius="integer"/>
   <gradient  
      android:type=["linear" | "radial" | "sweep"]    //共有3中渐变类型,线性渐变(默认)/放射渐变/扫描式渐变   
      android:angle="integer"     //渐变角度,必须为45的倍数,0为从左到右,90为从上到下   
      android:centerX="float"     //渐变中心X的相当位置,范围为0~1   
      android:centerY="float"     //渐变中心Y的相当位置,范围为0~1   
      android:startColor="color"   //渐变开始点的颜色   
      android:centerColor="color"  //渐变中间点的颜色,在开始与结束点之间   
      android:endColor="color"    //渐变结束点的颜色   
      android:gradientRadius="float"  //渐变的半径,只有当渐变类型为radial时才能使用   
      android:useLevel=["true" | "false"] />  //使用LevelListDrawable时就要设置为true。设为false时才有渐变效果  
      />
  <solid    //填充颜色,与gradient互相排斥
      android:color="color"/>
  <stroke        //描边属性
      android:width="dimension"   //描边的宽度   
      android:color="color"   //描边的颜色   
      // 以下两个属性设置虚线   
      android:dashWidth="dimension"   //虚线的宽度,值为0时是实线   
      android:dashGap="dimension" />      //虚线的间隔  

//下面2个一般不怎么用,因为他们的功能控件本身也有
   <padding    
      android:left="integer"   
      android:top="integer"   
      android:right="integer"   
      android:bottom="integer" /> 
   <size   //指定大小 一般在imageview配合scaleType
      android:width="integer"   
      android:height="integer" /> 
</shape>
  • Layer图层
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@drawable/xxx1">
  <item android:drawable="@drawable/xxx2">
</layer-list>
  • Selector可实现静态绘图的事件反馈,可针对事件设置不同图像。Selector中也可以使用Shape作为它的Item。
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- 默认时的背景图片 -->  
  <item android:drawable="@drawable/pic1" />    
  <!-- 没有焦点时的背景图片 -->  
  <item android:state_window_focused="false"   
        android:drawable="@drawable/pic1" />   
  <!-- 非触摸模式下获得焦点并单击时的背景图片 -->  
  <item android:state_focused="true" android:state_pressed="true"   
        android:drawable= "@drawable/pic2" />  
  <!-- 触摸模式下单击时的背景图片 -->  
  <item android:state_focused="false" android:state_pressed="true"   
        android:drawable="@drawable/pic3" />   
  <!--选中时的图片背景  -->  
  <item android:state_selected="true"   
        android:drawable="@drawable/pic4" />   
  <!--获得焦点时的图片背景  -->  
  <item android:state_focused="true"   
        android:drawable="@drawable/pic5" />  
</selector>

4)绘图技巧(参见自定义View)


5)图像处理-色彩特效处理

  • ColorMatrix

Android对于图片的处理,常使用数据结构是位图,即Bitmap,它包含了图片所有的数据。整个图片是由点阵和颜色值组成。每个点阵就是一个包含像素的矩阵。

ColorMatrix
public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener{
    private static final String TAG = "MainActivity";
    private ImageView img;
    private Bitmap bm;
    private int mHue=1,mSaturation=1,mLum=1;
    private SeekBar seek_hue,seek_saturation,seek_lum;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        seek_hue = (SeekBar) findViewById(R.id.seek_hue);
        seek_saturation = (SeekBar) findViewById(R.id.seek_saturation);
        seek_lum = (SeekBar) findViewById(R.id.seek_lum);
        seek_hue.setOnSeekBarChangeListener(this);
        seek_saturation.setOnSeekBarChangeListener(this);
        seek_lum.setOnSeekBarChangeListener(this);

        bm = BitmapFactory.decodeResource(getResources(),R.drawable.def);
        img = (ImageView) findViewById(R.id.img);
    }

    private Bitmap handleImageEffect(Bitmap bm,float hue,float saturation,float lum){
        //原始图片是不能修改的,可根据原始图片生成新图进行修改
        Bitmap bmp = Bitmap.createBitmap(bm.getWidth(),bm.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint();

        //色调
        ColorMatrix hueMatrix = new ColorMatrix();
        hueMatrix.setRotate(0,hue);
        hueMatrix.setRotate(1,hue);
        hueMatrix.setRotate(2,hue);

        //保饱和度
        ColorMatrix saturationMatrix = new ColorMatrix();
        saturationMatrix.setSaturation(saturation);

        //亮度
        ColorMatrix lumMatrix = new ColorMatrix();
        lumMatrix.setScale(lum,lum,lum,1);

        ColorMatrix imageMatrix = new ColorMatrix();
        imageMatrix.postConcat(hueMatrix);
        imageMatrix.postConcat(saturationMatrix);
        imageMatrix.postConcat(lumMatrix);

        paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));
        canvas.drawBitmap(bm,0,0,paint);
        return bmp;
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        Log.i(TAG, "============================ ");
        switch (seekBar.getId()){
            case R.id.seek_hue:
                mHue = progress;
                break;
            case R.id.seek_saturation:
                mSaturation = progress;
                break;
            case R.id.seek_lum:
                mLum = progress;
                break;
        }
        Log.i(TAG, "mHue= "+mHue+" mSaturation= "+mSaturation+" mLum= "+mLum);
        img.setImageBitmap(handleImageEffect(bm,mHue,mSaturation,mLum));
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
    }
}
  • 颜色矩阵

通过调整颜色矩阵可以改变一副图像的色彩效果,图像处理很大程度上就是寻找处理图像的颜色矩阵,除了使用系统提供的ColorMatrix,也可以精确的修改矩阵值来实现颜色效果处理。
4x5 最右侧为偏移量,左侧4x4为颜色系数RGBA


颜色矩阵
public class Main2Activity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "Main2Activity";
    private GridLayout mGroup;
    private EditText[] edts = new EditText[20];
    private Bitmap bitmap;
    private float[] mColorMatrix = new float[20];
    private ImageView img;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.def);
        img = (ImageView) findViewById(R.id.img);
        img.setImageBitmap(bitmap);
        mGroup = (GridLayout) findViewById(R.id.group);

        Button btn1 = (Button) findViewById(R.id.btn1);
        Button btn2 = (Button) findViewById(R.id.btn2);
        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);

        initEdts();
        initMatrix();
    }

    //初始化EditText
    private void initEdts(){
        Log.i(TAG, "mGroup.getHeight(): "+mGroup.getHeight());
        for (int i=0;i<20;i++){
            EditText editText = new EditText(this);
            edts[i] = editText;
            mGroup.addView(editText,getResources().getDisplayMetrics().widthPixels/5,100);
        }
    }

    //初始化矩阵值
    private void initMatrix(){
        for (int i=0; i<20; i++){
            if (i%6 == 0){
                edts[i].setText("1");
            }else {
                edts[i].setText("0");
            }
        }
    }

    //获取矩阵值
    private void getMatrix(){
        for (int i=0;i<20;i++){
            mColorMatrix[i] = Float.parseFloat(edts[i].getText().toString());
        }
    }

    //将矩阵设置到图像
    private void setImageMatrix(){
        //原始图片是不能修改的,可根据原始图片生成新图进行修改
        Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint();
        paint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
        canvas.drawBitmap(bitmap,0,0,paint);
        img.setImageBitmap(bmp);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            //设置值
            case R.id.btn1:
                getMatrix();
                setImageMatrix();
                break;
            //重置
            case R.id.btn2:
                initMatrix();
                getMatrix();
                setImageMatrix();
                break;
        }
    }
}
  • 常用图像颜色矩阵处理效果

  • 灰度效果


    灰度效果
  • 图像反转


    图像反转
  • 怀旧效果


    怀旧效果
  • 去色效果


    去色效果
  • 高饱和度


    高饱和度
  • 像素点分析

Android中还提供了Bitmao.getPixels()方法来帮我们提取整个Bitmap的像素点。

底片效果

怀旧效果

浮雕效果
public class PixelsActivity extends AppCompatActivity {

    private  Bitmap bitmap;
    private ImageView img;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pixels);
        bitmap= BitmapFactory.decodeResource(getResources(),R.drawable.def);
        img = (ImageView) findViewById(R.id.img);

        handleImage();
    }

    private void handleImage(){
        //创建临时bitmap
        Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);

        int[] oldPx = new int[bitmap.getWidth()*bitmap.getHeight()];
        int[] newPx = new int[bitmap.getWidth()*bitmap.getHeight()];

        //pixels - 接收位图颜色值的数组
        //offset - 写入到pixels[]中的第一个像素索引值
        //stride - pixels[]中的行间距
        // x     - 从位图中读取的第一个像素x坐标值
        // y     - 从位图中读取的第一个像素y坐标值
        //width  - 从每一行中读取的像素宽度
        //height - 读取的行数
        bitmap.getPixels(oldPx,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());

        for (int i=1;i<bitmap.getWidth()*bitmap.getHeight();i++){
            int color = oldPx[i-1];
            int r = Color.red(color);
            int g = Color.green(color);
            int b = Color.blue(color);
            int a = Color.alpha(color);

            int color2 = oldPx[i];
            int r2 = Color.red(color2);
            int g2 = Color.green(color2);
            int b2 = Color.blue(color2);
            int a2 = Color.alpha(color2);

            //底片效果
//            r = 255-r;
//            g = 255-g;
//            b = 255-b;
            //老照片效果
//            r = (int) (0.393*r+0.769*g+0.189*b);
//            g = (int) (0.349*r+0.686*g+0.168*b);
//            b = (int) (0.272*r+0.534*g+0.131*b);
            //浮雕效果
            r = r-r2+127;
            g = g-g2+127;
            b = b-b2+127;

            if (r>255){
                r=255;
            }else if (r<0){
                r=0;
            }
            if (g>255){
                g=255;
            }else if (g<0){
                g=0;
            }
            if (b>255){
                b=255;
            }else if (b<0){
                b=0;
            }
            newPx[i] = Color.argb(a,r,g,b);
        }
        bmp.setPixels(newPx,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());
        img.setImageBitmap(bmp);
    }
}

6)图像处理-图形特效处理

7)图像处理-画笔特效处理

  • PorterDuffXfermode
    PorterDuffXfermode设置的是两个图层交集区域的显示方式,dst是先画的图形,而src为后画的图形。
    先用一个普通画笔画一个mask遮罩层,再用带PorterDuffXfermode的画笔将图像花在遮罩层上。
  1. 例:使用PorterDuffXfermode实现的圆头像


    PorterDuff.Mode.SRC_IN
        ImageView img = (ImageView) findViewById(R.id.img);
        Bitmap mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.def);
        Bitmap bmp = Bitmap.createBitmap(mBitmap.getWidth(),mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        Paint mPaint = new Paint();
        mPaint.setAntiAlias(true);

        RectF rectF = new RectF(0,0,mBitmap.getWidth(),mBitmap.getHeight());
        //画圆
        canvas.drawRoundRect(rectF,mBitmap.getWidth()/2,mBitmap.getHeight()/2,mPaint);

        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(mBitmap,0,0,mPaint);

        img.setImageBitmap(bmp);

还有其他类型可以自己尝试:

SRC,SRC_IN,SRC_OVER,SRC_OUT,SRC_ATOP
DST,DST_IN,DST_OVER,DST_OUT,DST_ATOP
XOR,DARKEN,LIGHTEN,MULTIPLY,SCREEN

2)例:实现刮刮卡效果


刮刮卡
public class XfermodeView extends View {
    private Paint mPaint;
    private Canvas canvas;
    private Bitmap mBgBitmap, mFgBitmap;
    private Path mPath;
    public XfermodeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mPaint = new Paint();

        //重要! 用于绘制透明path
        mPaint.setAlpha(0);
        //遮罩层类型
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

        mPaint.setStrokeWidth(50);
        mPaint.setStrokeCap(Paint.Cap.ROUND); //笔触风格
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND); // 接合处形态

        mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.def);
        mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        canvas = new Canvas(mFgBitmap);
        canvas.drawColor(Color.GRAY);

        mPath = new Path();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(event.getX(),event.getY());
                break;
            case MotionEvent.ACTION_MOVE:
                mPath.lineTo(event.getX(),event.getY());
                break;
        }

        //其实就是绘制透明路径, 因mPaint设置的Alpha为0
        canvas.drawPath(mPath,mPaint);
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mBgBitmap,0,0,null);
        canvas.drawBitmap(mFgBitmap,0,0,null);
    }
}
  • Shader
说明
BitmapShader 位图
LinearGradient 线性
RadialGradient 光束
SweepGradient 梯度
ComposeShader 混合
Shader.TileMode 说明
Shader.TileMode.CLAMP 拉伸。的是图片最后的那一个元素,不断重复
REPEAT 重复。横向、纵向不断重复
MIRROR 镜像。横向不断翻转重复,纵向不断翻转重复

1)例:线性渐变着色器


线性渐变着色器
public class MyTextView1 extends TextView {
    private Paint mPaint = new Paint();
    private int mViewWidth;
    private LinearGradient mLinearGradient;
    private Matrix mGradientMatrix;
    private int mTranslate;
    public MyTextView1(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (mViewWidth == 0){
            mViewWidth = getMeasuredWidth();
            if (mViewWidth > 0){
                //重要!获取当前绘制用的Paint对象
                mPaint = getPaint();
                //线性渐变着色器
                mLinearGradient = new LinearGradient(0,0,mViewWidth,0,new int[]{Color.RED,Color.GREEN,Color.CYAN},null, Shader.TileMode.CLAMP);
                mPaint.setShader(mLinearGradient);
                mGradientMatrix= new Matrix();
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mGradientMatrix != null){
            mTranslate += mViewWidth/5;
            if (mTranslate >2*mViewWidth){
                mTranslate = -mViewWidth;
            }
            mGradientMatrix.setTranslate(mTranslate,0);
            mLinearGradient.setLocalMatrix(mGradientMatrix);
            postInvalidateDelayed(100);
        }
    }
}

2)例:使用Shader实现圆形头像


BitmapShader
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.def);
Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
Paint mPaint = new Paint();
mPaint.setShader(bitmapShader);
canvas.drawCircle(100,100,100,mPaint);

8)SurfaceView

View通过刷新来重绘视图,刷新的间隔为16ms,如果在16ms内完成了需要操作的逻辑,则用户在视觉上不会产生卡顿的感觉。但逻辑复杂会卡顿,阻塞主线程。

View SurfaceView
适用主动刷新 适用被动更新
主线程中刷新 子线程中刷新
无双缓冲 有双缓冲

若自定义View需要频繁刷新,或刷新时数据量大,可以用SurfaceView取代View。
Android 屏幕刷新机制
android屏幕刷新显示机制

  • 例1)正弦曲线


    正弦曲线
public class SurfaceViewDemo extends SurfaceView implements SurfaceHolder.Callback,Runnable {
    private int x;
    private int y;
    private SurfaceHolder mHolder;
    private Canvas mCanvas;
    private boolean mIsDrawing; //控制子线程
    private Path mPath;
    private Paint mPaint;

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

    //初始化
    private void init(){
        mHolder = getHolder(); //初始化SurfaceHolder
        mHolder.addCallback(this); //注册SurfaceHolder的回调方法
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setStyle(Paint.Style.STROKE);
    }

    //开启子线程进行绘制
    @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不断绘制
        while (mIsDrawing){
            draw();
            x += 1;
            y = (int) (Math.sin(x*2*Math.PI/180)*100)+400;
            mPath.lineTo(x,y);
        }
    }

    private void draw(){
        try {
            mCanvas = mHolder.lockCanvas(); //获得canvas对象
            mCanvas.drawColor(Color.WHITE);
            mCanvas.drawPath(mPath,mPaint);
        }catch (Exception e){

        }finally {
            if (mCanvas != null){
                mHolder.unlockCanvasAndPost(mCanvas); //提交画布内容
            }
        }
    }
}
  • 例2)绘图板


    绘图板
public class SurfaceViewDemo2 extends SurfaceView implements SurfaceHolder.Callback,Runnable {
    private static final String TAG = "SurfaceViewDemo2";
    private SurfaceHolder mHolder;
    private Canvas mCanvas;
    private boolean mIsDrawing;
    private Path mPath;
    private Paint mPaint;
    public SurfaceViewDemo2(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init(){
        mHolder = getHolder();
        mHolder.addCallback(this);
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(6);
//        setFocusable(true);
//        setFocusableInTouchMode(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();
        }
    }

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

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

        }finally {
            mHolder.unlockCanvasAndPost(mCanvas);
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容