首页 > 系统 > Android > 正文

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

2019-11-08 00:24: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(图层效果)4、Selector(按下,抬起的效果)

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; }}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表