Android的绘图机制学习---自定义View

一、自定义View的使用:


通常我们为界面做UI设计时使用xml中的布局文件,这些布局文件及方法是androidAPI中所提供的。如果我们要实现一些复杂的布局或者一些特别的效果这样就会用到自定义View了。自定义View就是建立一个类继承自View或View的子类并实现一定的方法。下面是一些例子和系统类中的方法使用:
1.1 刮刮乐代码的实现

* 这个例子主要用来学习android绘图机制中的一些方法
* 刮刮乐的实现思路:
* 1、刮刮乐相当于是两个图片,上面的图片是一个遮罩层,用户通过滑动屏幕使得上面的图层消失,进而将下面的
* 图层显示出来
* 实现步骤:
* 1、首先用普通画笔绘画一个与原图大小相同的bitmap图像。
* 2、将bitmap图像绘制在原图上方,
* 3、监听用户手势的滑动,描绘出路径,将画笔的透明度设置为0,模拟橡皮擦的效果,进而将原图显示出来。
*
* 所用到的API类:
* bitmap,path,canvas,paint
自定义View的代码实现

public class GuaGuaLeView extends View {
    private Bitmap mDownBitmap,mUpBitmap;
    private Paint mPaint;
    private Path mPath;
    private Canvas mCanvas;
    public GuaGuaLeView(Context context) {
       this(context,null);
    }

    public GuaGuaLeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        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();
        //获取图片资源
        mDownBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.image01);
        //按照原图大小绘制一个遮罩层
        mUpBitmap = Bitmap.createBitmap(mDownBitmap.getWidth(),mDownBitmap.getHeight(),
                Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mUpBitmap);
        mCanvas.drawColor(Color.GRAY);
    }

    /**
     * 获取用户的触摸监听事件
     * @param event
     * @return
     */
    @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) {
        super.onDraw(canvas);
        canvas.drawBitmap(mDownBitmap,0,0,null); //先绘制出下面的图片
        canvas.drawBitmap(mUpBitmap,0,0,null);
    }
}

布局文件中引入自定义View控件

<lzl.edu.com.canvasdemo.view.GuaGuaLeView
    android:id="@+id/guaGuaLeImageView"
    android:layout_width="300dp"
    android:layout_height="300dp"
    android:layout_centerHorizontal="true"/>
1.2 绘制不同形状的图片

将图片改变成圆角的图片。
* 先用普通画笔画一个遮罩层,再用带PorterDuffXfermode的画笔将图像画在遮罩层上
自定义View代码的实现

public class RoundRectView extends ImageView {
    private Bitmap mBitmap,outBitmap;
    private Canvas mCanvas;
    private Paint mPaint;
    public RoundRectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    private void init(){
        Log.i("执行顺序,","init()");
        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.image01);
        outBitmap = Bitmap.createBitmap(mBitmap.getWidth(),
                mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(outBitmap);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
       RectF mRound = new RectF(0,0,mBitmap.getWidth(),mBitmap.getHeight());
//        mCanvas.drawRoundRect(mRound,80,80,mPaint);  //画圆角矩形
        mCanvas.drawCircle(mBitmap.getWidth()/2,mBitmap.getHeight()/2,150,mPaint); //画圆形
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        mCanvas.drawBitmap(mBitmap,0,0,mPaint);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.i("执行顺序,","onDraw()");
        canvas.drawBitmap(outBitmap,0,0,null);
    }
}

在布局文件中引入自定义View

 <lzl.edu.com.canvasdemo.view.RoundRectView
        android:layout_below="@+id/guaGuaLeImageView"
        android:layout_width="300dp"
        android:layout_height="300dp" />

二、SurfaceView的使用模板

**SurfaceView与View类似,他们的不同之处在于
1、View主要使用于主动更新的情况下,而SurfaceView主要适用于被动更新,例如频繁的刷新。
2、View在主线程中对画面进行刷新,而SurfaceView通常会通过一个子线程来进行页面的刷新。
3、View在绘图时没有使用双缓冲机制,而SurfaceView在底层实现机制中就已经实现了双缓冲机制。
   总之,当自定义View需要频繁的刷新,或者刷新时数据处理量很大时,就考虑用SurfaceView来替代View。**
使用步骤:
—| 创建一个类继承自SurfaceView并实现SurfaceHolder.Callback和Runnable接口。
—| 分别实现以下三个方法:

 @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.i("执行。。。","surfaceCreated()");
        mIsDrawing = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.i("执行。。。","surfaceChanged()");

    }

    /**
     * view销毁时结束多线程的执行
     * @param holder
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false;
        Log.i("执行。。。","surfaceDestroyed()");

    }

—| 在surfaceCreated方法中获取SurfaceHolder对象,开启子线程执行任务。
—| 在run()方法中使用lockCanvas()方法获得Canvas对象。可以进行绘图操作。并通过unlockCanvasAndPost()方法进行提交画布内容。
以下是代码的实现

public class DrawLineSurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable {
    //通过SurfaceHolder处理
    private SurfaceHolder mHolder;
    //用于绘图的Canvas
    private Canvas mCanvas;
    //子线程的标志位
    private boolean mIsDrawing;
    //绘制路径
    private Path mPath;
    //定义画笔
    private Paint mPaint;
    private int x = 0;
    private int y =0;

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

    public DrawLineSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();

    }

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

    /**
     * 初始化View
     */
    private void initView(){
        Log.i("执行。。。","initView()");
        //初始化SurfaceHolder对象,并注册SurfaceHolder回调方法
        mHolder = getHolder();
        mHolder.addCallback(this);
        //设置该View接收焦点
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
        mPath = new Path();
        mPath.moveTo(x,y);  //一定 要有一个开始点
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(12);
    }

    /**
     * 在此方法中开启子线程进行绘制。
     * @param holder
     */
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.i("执行。。。","surfaceCreated()");
        mIsDrawing = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.i("执行。。。","surfaceChanged()");

    }

    /**
     * view销毁时结束多线程的执行
     * @param holder
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false;
        Log.i("执行。。。","surfaceDestroyed()");

    }

    @Override
    public void run() {
        while(mIsDrawing){
            //执行绘制任务----这里绘制一个正弦的曲线
            draw();
            x +=1;
            y = (int)(100*Math.sin(x*2*Math.PI/180)+400);
            mPath.lineTo(x,y);
            Log.i("----",getWidth()+"----");
            if(x>getWidth())
                mIsDrawing = false;
        }
    }
    private void draw(){
        try {
        mCanvas = mHolder.lockCanvas();//获得Canvas对象![这里写图片描述](https://img-blog.csdn.net/20151128183623335)
        mCanvas.drawColor(Color.WHITE);
        mCanvas.drawPath(mPath,mPaint);
        }catch (Exception e){
        }finally {
            if(mCanvas!=null)
                mHolder.unlockCanvasAndPost(mCanvas);  //将Canvas提交
        }
    }

}

在xml布局文件中引入自定义View

<lzl.edu.com.canvasdemo.view.DrawLineSurfaceView
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

刮刮乐例子的图片展示
SurfaceView绘制正弦函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值