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

来源:互联网 发布:炉石传说录像软件 编辑:程序博客网 时间:2024/06/04 19:27

本章将介绍关于Andorid绘图机制的一些高级技巧与分析

6.1、屏幕的尺寸信息

一般我们以720x1280为标准原稿,那么1dp = 2px

6.2、2D绘图基础

Canvas提供了很多api
如:

drawPoint(点)drawLine(线)drawRect(矩形)drawVertices(多边形)drawarc(弧形)drawCircle(圆)

Paint作为画笔,也提供了很多api

如:

setAntiAlias() //设置画笔的锯齿效果setColor//设置画笔的颜色setARGB()//设置画笔的ARGB值setAlpha()//设置画笔的透明度setStyle()//设置画笔的风格

绘图api+画笔api,就可以画出任意的图形了

6.3、Andorid XML绘图

1、bitmap2、shape3、layer(图层效果)4Selector(按下,抬起的效果)

6.4、Andorid 绘图技巧

Canvas作为绘图直接的对象,提供了几个有用的方法Canvas.save  保存Canvas.restore 与save之前的图像合并Canvas.translate 平移Canvas.rotate 旋转

下面我们创建一个仪表盘:
这里写图片描述

我们分析下这个图形:
1、仪表盘——外面的大圆盘
2、刻度线——4个长的刻度线和其他短的刻度线
3、刻度值——长刻度线对应的大刻度值和其他小刻度值
4、指针——一粗一细2根指针

圆盘:canvas.drawCircle()绘制一个圆就可以了
刻度线:直角线直接确定2点坐标就可以了,斜线就要通过三角函数实现了,不,可以利用旋转角度实现,每画一条线就旋转相应的角度,然后把画布再重新还原到旋转前的位置
指针:通过translate 方法来进行平移
完整代码如下:

package com.yishengxu.myapplication;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.util.AttributeSet;import android.view.View;public class Clock extends View {    private int mHeight, mWidth;    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 onDraw(Canvas canvas) {        // 获取宽高参数        mWidth = getMeasuredWidth();        mHeight = getMeasuredHeight();        // 画外圆        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 painDegree = new Paint();        paintCircle.setStrokeWidth(3);        for (int i = 0; i < 24; i++) {            // 区分整点与非整点            if (i == 0 || i == 6 || i == 12 || i == 18) {                painDegree.setStrokeWidth(5);                painDegree.setTextSize(30);                canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,                        mWidth / 2, mHeight / 2 - mWidth / 2 + 60,                        painDegree);                String degree = String.valueOf(i);                canvas.drawText(degree,                        mWidth / 2 - painDegree.measureText(degree) / 2,                        mHeight / 2 - mWidth / 2 + 90,                        painDegree);            } else {                painDegree.setStrokeWidth(3);                painDegree.setTextSize(15);                canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,                        mWidth / 2, mHeight / 2 - mWidth / 2 + 30,                        painDegree);                String degree = String.valueOf(i);                canvas.drawText(degree,                        mWidth / 2 - painDegree.measureText(degree) / 2,                        mHeight / 2 - mWidth / 2 + 60,                        painDegree);            }            // 通过旋转画布简化坐标运算            canvas.rotate(15, mWidth / 2, mHeight / 2);        }        // 画圆心        Paint paintPointer = new Paint();        paintPointer.setStrokeWidth(30);        canvas.drawPoint(mWidth / 2, mHeight / 2, paintPointer);        // 画指针        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();    }}

6.4.2 Layer图层

saveLayer()——创建图层saveLayerAlpha——入栈restore()restoreToCount()——出栈

例如在onDraw方法中绘制2个相交重合的圆

我们要知道:
图层透明度是0~255不同的数值
127为半透明
255完全不透明
0完全透明

6.4、Andorid 图像处理之色彩特效处理

在Andorid中封装了一个类:ColorMatrix来控制色调、饱和度、亮度

色调:setRotate(int a,float b)a:系统用0,1,2来代表Red,Green,Blue三种颜色的处理b:需要处理的值饱和度:setSaturation(float a)a :饱和度的值亮度:setScale

最后系统也提供了postConcat()将效果混合

ColorMatrix   cm = new ColorMatrix();cm. postConcat(matrix1);cm. postConcat(matrix2);cm. postConcat(matrix3);

下面我们写个例子:通过seekBar来改变不同的数值,来控制整个图片里面的这3个数值

完整代码如下:

package com.imooc.myapplication;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Bundle;import android.widget.ImageView;import android.widget.SeekBar;public class PrimaryColor extends Activity implements SeekBar.OnSeekBarChangeListener {    private static int MAX_VALUE = 255;    private static int MID_VALUE = 127;    private ImageView mImageView;    private SeekBar mSeekbarhue, mSeekbarSaturation, mSeekbarLum;    private float mHue, mStauration, mLum;    private Bitmap bitmap;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.primary_color);        bitmap = BitmapFactory.decodeResource(getResources(),                R.drawable.test3);        mImageView = (ImageView) findViewById(R.id.imageview);        mSeekbarhue = (SeekBar) findViewById(R.id.seekbarHue);        mSeekbarSaturation = (SeekBar) findViewById(R.id.seekbarSaturation);        mSeekbarLum = (SeekBar) findViewById(R.id.seekbatLum);        mSeekbarhue.setOnSeekBarChangeListener(this);        mSeekbarSaturation.setOnSeekBarChangeListener(this);        mSeekbarLum.setOnSeekBarChangeListener(this);        mSeekbarhue.setMax(MAX_VALUE);        mSeekbarSaturation.setMax(MAX_VALUE);        mSeekbarLum.setMax(MAX_VALUE);        mSeekbarhue.setProgress(MID_VALUE);        mSeekbarSaturation.setProgress(MID_VALUE);        mSeekbarLum.setProgress(MID_VALUE);        mImageView.setImageBitmap(bitmap);    }    @Override    public void onProgressChanged(SeekBar seekBar,                                  int progress, boolean fromUser) {        switch (seekBar.getId()) {            case R.id.seekbarHue:                mHue = (progress - MID_VALUE) * 1.0F / MID_VALUE * 180;                break;            case R.id.seekbarSaturation:                mStauration = progress * 1.0F / MID_VALUE;                break;            case R.id.seekbatLum:                mLum = progress * 1.0F / MID_VALUE;                break;        }        mImageView.setImageBitmap(ImageHelper.handleImageEffect(                bitmap, mHue, mStauration, mLum));    }    @Override    public void onStartTrackingTouch(SeekBar seekBar) {    }    @Override    public void onStopTrackingTouch(SeekBar seekBar) {    }}布局文件:<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <ImageView        android:id="@+id/imageview"        android:layout_width="300dp"        android:layout_height="300dp"        android:layout_centerHorizontal="true"        android:layout_marginBottom="24dp"        android:layout_marginTop="24dp" />    <SeekBar        android:id="@+id/seekbarHue"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_below="@id/imageview" />    <SeekBar        android:id="@+id/seekbarSaturation"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_below="@id/seekbarHue" />    <SeekBar        android:id="@+id/seekbatLum"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_below="@id/seekbarSaturation" /></RelativeLayout>

6.4、Andorid 图像处理之图形特效处理

图像的变形处理通常有4类:

Translate——平移变换Rotate——旋转变换Scale——缩放变换Skew——错切变换

Android使用Matrix来封装矩阵变换:

setTranslate——平移变换setRotate——旋转变换setScale——缩放变换setSkew——错切变换pre()和post()提供矩阵前乘和后乘的运算

拓展:drawBitmapMesh方法可以创建很多负责的图形特效

6.7、Andorid 图像处理之图形特效处理

6.7.1、porterDuffXfermode来实现刮刮乐

完整代码如下:

package com.imooc.myapplication;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;public class XfermodeView extends View {    private Bitmap mBgBitmap, mFgBitmap;    private Paint mPaint;    private Canvas mCanvas;    private Path mPath;    public XfermodeView(Context context) {        super(context);        init();    }    public XfermodeView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    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.test);        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);    }}

注意:在绘图时,将硬件加速关闭,有些模式不支持硬件加速

6.7.2、Shader 着色器、渲染器

BitmapShader——位图ShaderLinearGradient——线型ShaderRadialGradient——光速ShaderSweepGradient——梯度ShaderComposeShader——混合Shader

6.7.3、PathEffect 绘制路径

6.8、View的孪生兄弟:SurfaceView

在View的绘制过程中,处理的逻辑太多容易耗时,为了避免这个问题,andorid提供SurfaceView来解决这个问题
总结:
如果你的自定义view需要频繁的刷新,或者刷新时数据处理量比较大,那么就可以使用surfaceview取代view了。

自定义surface接口需要实现2个接口SurfaceHolder.Callback和Runnable
SurfaceHolder.Callback接口对应创建、改变、销毁的过程
通常需要定义3个成员变量

// SurfaceHolderprivate SurfaceHolder mHolder;// 用于绘图的Canvasprivate Canvas mCanvas;// 子线程标志位private boolean mIsDrawing;

完整代码如下:

package com.imooc.surfaceviewtest;import android.content.Context;import android.graphics.Canvas;import android.util.AttributeSet;import android.view.SurfaceHolder;import android.view.SurfaceView;public class SurfaceViewTemplate extends SurfaceView        implements SurfaceHolder.Callback, Runnable {    // SurfaceHolder    private SurfaceHolder mHolder;    // 用于绘图的Canvas    private Canvas mCanvas;    // 子线程标志位    private boolean mIsDrawing;    public SurfaceViewTemplate(Context context) {        super(context);        initView();    }    public SurfaceViewTemplate(Context context, AttributeSet attrs) {        super(context, attrs);        initView();    }    public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        initView();    }    private void initView() {        //1、初始化SurfaceHolder        mHolder = getHolder();        mHolder.addCallback(this);        setFocusable(true);        setFocusableInTouchMode(true);        this.setKeepScreenOn(true);        //mHolder.setFormat(PixelFormat.OPAQUE);    }    //创建    @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();            // draw sth        } catch (Exception e) {        } finally {            if (mCanvas != null)                //对画布内容进行提交                mHolder.unlockCanvasAndPost(mCanvas);        }    }}

注意:在绘制方法中将mHolder.unlockCanvasAndPost(mCanvas);方法放在finally代码块中,来保证每次都能将内容提交。

6.8.3、SurfaceView绘图实例

实例一:绘制正弦曲线
不断的修改横纵坐标的值
这里写图片描述

完整代码如下:

package com.imooc.surfaceviewtest;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.view.SurfaceHolder;import android.view.SurfaceView;public class SinView extends SurfaceView        implements SurfaceHolder.Callback, Runnable {    private SurfaceHolder mHolder;    private Canvas mCanvas;    private boolean mIsDrawing;    private int x = 0;    private int y = 0;    private Path mPath;    private Paint mPaint;    public SinView(Context context) {        super(context);        initView();    }    public SinView(Context context, AttributeSet attrs) {        super(context, attrs);        initView();    }    public SinView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        initView();    }    private void initView() {        mHolder = getHolder();        mHolder.addCallback(this);        setFocusable(true);        setFocusableInTouchMode(true);        this.setKeepScreenOn(true);        mPath = new Path();        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mPaint.setColor(Color.RED);        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setStrokeWidth(10);        mPaint.setStrokeCap(Paint.Cap.ROUND);        mPaint.setStrokeJoin(Paint.Join.ROUND);    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        mIsDrawing = true;        mPath.moveTo(0, 400);        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();            // SurfaceView背景            mCanvas.drawColor(Color.WHITE);            mCanvas.drawPath(mPath, mPaint);        } catch (Exception e) {        } finally {            if (mCanvas != null)                mHolder.unlockCanvasAndPost(mCanvas);        }    }}

实例二:
这里写图片描述
完整代码如下:

package com.imooc.surfaceviewtest;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.SurfaceHolder;import android.view.SurfaceView;public class SimpleDraw extends SurfaceView        implements SurfaceHolder.Callback, Runnable {    private SurfaceHolder mHolder;    private Canvas mCanvas;    private boolean mIsDrawing;    private Path mPath;    private Paint mPaint;    public SimpleDraw(Context context) {        super(context);        initView();    }    public SimpleDraw(Context context, AttributeSet attrs) {        super(context, attrs);        initView();    }    public SimpleDraw(Context context, AttributeSet attrs,                      int defStyle) {        super(context, attrs, defStyle);        initView();    }    private void initView() {        mHolder = getHolder();        mHolder.addCallback(this);        setFocusable(true);        setFocusableInTouchMode(true);        this.setKeepScreenOn(true);        mPath = new Path();        mPaint = new Paint();        mPaint.setColor(Color.RED);        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setStrokeWidth(40);    }    @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() {        long start = System.currentTimeMillis();        while (mIsDrawing) {            draw();        }        long end = System.currentTimeMillis();        // 50 - 100        if (end - start < 100) {            try {                Thread.sleep(100 - (end - start));            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    private void draw() {        try {            mCanvas = mHolder.lockCanvas();            mCanvas.drawColor(Color.WHITE);            mCanvas.drawPath(mPath, mPaint);        } catch (Exception e) {        } finally {            if (mCanvas != null)                mHolder.unlockCanvasAndPost(mCanvas);        }    }    @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;    }}
0 0
原创粉丝点击