文章出处:http://write.blog.youkuaiyun.com/postedit/42077065
一 绘制图形和文本的基本方法
首先指定好画笔的颜色:
Paint paint = new Paint();
paint.setColor(Color.RED);
1. drawPoint:绘制点
canvas.drawPoint(10, 20, paint);
前两个参数是坐标的位置,第3个是我们创建好的画笔。2. drawLine:绘制直线
canvas.drawLine(0, 10, 20, 10, paint);
前4个参数是两个点的坐标,最后是画笔。3. drawCircle:绘制圆
canvas.drawCircle(100, 50, 20, paint);
前两个参数是点的坐标,第3个是半径,第4个是画笔。
画实心圆:
paint.setStyle(Style.FILL);
画实线空心圆:
paint.setStyle(Style.STROKE);
4. drawArc:绘制弧
RectF rectF = new RectF();
rectF.left = 30;
rectF.top = 190;
rectF.right = 120;
rectF.bottom = 280;
canvas.drawArc(rectF, 0, 200, true, paint);
绘制弧需要制定一个矩形的外框。第1个参数是矩形,第2个和第3个参数是起始和终止的度数,从0-360顺时针旋转,第4个参数若是true,则弧需要练圆心,若为false,则弧不连圆心。
5. drawText:绘制文本
canvas.drawText("测试", 120, 300, paint);
第1个参数是绘制内容,第2个核3个是x、y偏移量,最后一个是画笔。
最后我们来看一下完整的文件:
package com.thr.testandroid;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class MyView extends View {
private Paint paint = new Paint();
private boolean showFirst = true;
private int r = 10;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
showFirst = !showFirst;
invalidate();
return super.onTouchEvent(event);
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
if (showFirst) {
paint.setColor(Color.RED);
} else {
paint.setColor(Color.BLUE);
}
// 画点
canvas.drawPoint(10, 20, paint);
// 画线
canvas.drawLine(0, 10, 20, 10, paint);
// 画圆
canvas.drawCircle(100, 50, r, paint);
paint.setStyle(Style.STROKE);
canvas.drawCircle(300, 50, 40, paint);
paint.setStyle(Style.FILL);
canvas.drawCircle(300, 50, 20, paint);
RectF rectF = new RectF();
rectF.left = 30;
rectF.top = 190;
rectF.right = 120;
rectF.bottom = 280;
canvas.drawArc(rectF, 0, 200, true, paint);
rectF.left = 130;
rectF.top = 290;
rectF.right = 220;
rectF.bottom = 380;
canvas.drawArc(rectF, 0, 300, false, paint);
paint.setTextSize(22);
canvas.drawText("测试", 100, 300, paint);
if (r != 100) {
r++;
invalidate();
}
super.onDraw(canvas);
}
}
我们在触摸屏幕的时候改变了画笔的颜色,并调用invalidate()方法,对整个屏幕进行刷新。 在onDraw方法中如果重复不停的调用invalidate()方法可以实现简单的动画效果。我们这里使用r来定义半径,在未达到100时会一直扩大显示圆,实现了一个简单的动画效果。
二 绘制图像的两种方法
方法一:
canvas.drawBitmap(bitmap, 10, 10, null);
方法二:
drawable.draw(canvas);
两种都可以达到同样的的效果。
直接上代码:
package com.thr.testandroid;
import java.io.InputStream;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Paint.Style;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class MyView extends View {
private Bitmap bitmap1;
private Bitmap bitmap2;
private Bitmap bitmap3;
private Bitmap bitmap4;
private Drawable drawable;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(Color.WHITE);
InputStream is = context.getResources().openRawResource(
R.drawable.ic_launcher);
// 第一种
Options opts = new BitmapFactory.Options();
opts.inSampleSize = 4;
bitmap1 = BitmapFactory.decodeStream(is, null, opts);
// 第二种
bitmap2 = BitmapFactory.decodeStream(is);
int width = bitmap2.getWidth();
int height = bitmap2.getHeight();
int[] pixels = new int[width * height];
bitmap2.getPixels(pixels, 0, width, 0, 0, width, height);
// 第三种
bitmap3 = Bitmap.createBitmap(pixels, width, height,
Bitmap.Config.ARGB_8888);
// 第四种
bitmap4 = Bitmap.createBitmap(pixels, 0, width, width, height,
Bitmap.Config.ARGB_4444);
drawable = context.getResources().getDrawable(R.drawable.ic_launcher);
drawable.setBounds(50, 300, 180, 420);
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap1, 10, 10, null);
canvas.drawBitmap(bitmap2, 10, 200, null);
canvas.drawBitmap(bitmap3, 110, 200, null);
canvas.drawBitmap(bitmap4, 210, 200, null);
drawable.draw(canvas);
}
}
opts.inSampleSize = 4;
是用来控制图像缩小4倍的。
int[] pixels = new int[width * height];
bitmap2.getPixels(pixels, 0, width, 0, 0, width, height);
创建一个二维数组来保存bitmap2所读取到的像素值。getPixels第1个参数是我们创建的二维数组,第2个参数是写入到pixels[]中的第一个像素索引值 ,第3个是用来表示pixels[]数组中每行的像素个数,用与行于行之间区分,其绝对值必须大于第6个参数,但不必大于要读取的图片的宽度,第4、5个参数是从位图中读取的第一个像素坐标的x、y值,第6个参数是每一行读取的像素宽度,第7个参数是读取的行数。如果pixels太小将会抛出异常。
三 自定义绘制时钟控件
我们创建自定义ClockView继承View,自定了几个属性,关于自定义属性还不太清楚的,请看上一篇博客:【Android进阶】(1)用继承和组合方式自定义控件,在绘制完第一次图像后,立刻加入到Handler中,每隔1秒刷新一次界面,实现时钟的效果,代码如下:
package com.thr.testandroid;
import java.util.Calendar;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.thr.draw2d.R;
public class ClockView extends View implements Runnable {
private static final String TAG = "ClockView";
/**
* 图片资源id
*/
private int clockResourceId;
private Bitmap bitmap;
/**
* 缩放程度
*/
private float scale;
private float centerWidthScale;
private float centerHeightScale;
/**
* 秒针长度
*/
private int secondLength;
/**
* 分针长度
*/
private int minuteLength;
/**
* 时针长度
*/
private int hourLength;
private Handler handler = new Handler();
@SuppressLint("Recycle")
public ClockView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.ClockView);
int count = typedArray.getIndexCount();
for (int i = 0; i < count; i++) {
// 遍历类型数组,取得我们设置的值
int attr = typedArray.getIndex(i);
switch (attr) {
// 图片资源属性
case R.styleable.ClockView_clockImageSrc:
clockResourceId = typedArray.getResourceId(
R.styleable.ClockView_clockImageSrc, 0);
bitmap = BitmapFactory.decodeResource(getResources(),
clockResourceId);
break;
// 自定义缩放属性
case R.styleable.ClockView_scale:
scale = typedArray.getFloat(R.styleable.ClockView_scale, 0);
break;
// 表盘宽度缩放
case R.styleable.ClockView_centerWidthScale:
centerWidthScale = typedArray.getFloat(
R.styleable.ClockView_centerWidthScale,
bitmap.getWidth() / 2);
break;
// 表盘高度缩放
case R.styleable.ClockView_centerHeightScale:
centerHeightScale = typedArray.getFloat(
R.styleable.ClockView_centerHeightScale,
bitmap.getHeight() / 2);
break;
// 秒针长度缩放
case R.styleable.ClockView_sencondSize:
secondLength = (int) (typedArray.getInt(
R.styleable.ClockView_sencondSize, 0) * scale);
break;
// 分针长度缩放
case R.styleable.ClockView_minuteSize:
minuteLength = (int) (typedArray.getInt(
R.styleable.ClockView_minuteSize, 0) * scale);
break;
// 时针长度缩放
case R.styleable.ClockView_hourSize:
hourLength = (int) (typedArray.getInt(
R.styleable.ClockView_hourSize, 0) * scale);
break;
}
}
// 最后指定整分钟数开始执行
// int currentSecond = Calendar.getInstance().get(Calendar.SECOND);
// handler.post(this, (60 - currentSecond) * 1000);
// 立刻执行
handler.post(this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.i(TAG, "onMeasure");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 根据图像的实际大小等比例设置View的大小
setMeasuredDimension((int) (bitmap.getWidth() * scale),
(int) (bitmap.getHeight() * scale));
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
Log.i(TAG, "onDraw");
Paint paint = new Paint();
Rect src = new Rect();
Rect target = new Rect();
src.left = 0;
src.top = 0;
src.right = bitmap.getWidth();
src.bottom = bitmap.getHeight();
target.left = 0;
target.top = 0;
target.right = (int) (src.right * scale);
target.bottom = (int) (src.bottom * scale);
// 画表盘
canvas.drawBitmap(bitmap, src, target, paint);
// 计算表盘中心点的坐标
float centerX = target.right * centerWidthScale;
float centerY = target.bottom * centerHeightScale;
// 画中心圆
canvas.drawCircle(centerX, centerY, 5, paint);
Calendar calendar = Calendar.getInstance();
int currenSecond = calendar.get(Calendar.SECOND);
int currentMinute = calendar.get(Calendar.MINUTE);
int currentHour = calendar.get(Calendar.HOUR);
// 计算秒针、时针和分针与水平(3点方向)夹角
double secondRadian = Math
.toRadians((360 - ((currenSecond * 6) - 90)) % 360);
double minuteRadian = Math
.toRadians((360 - ((currentMinute * 6) - 90)) % 360);
double hourRadian = Math.toRadians((360 - ((currentHour * 30) - 90))
% 360 - (30 * currentMinute / 60));
// 设置秒针的粗细
paint.setStrokeWidth(1);
// 画秒针
paint.setColor(Color.GREEN);
canvas.drawLine(centerX, centerY, (float) (centerX + secondLength
* Math.cos(secondRadian)), (float) (centerY - secondLength
* Math.sin(secondRadian)), paint);
// 设置分针的粗细
paint.setStrokeWidth(3);
// 画分针
paint.setColor(Color.RED);
canvas.drawLine(centerX, centerY, (float) (centerX + minuteLength
* Math.cos(minuteRadian)), (float) (centerY - minuteLength
* Math.sin(minuteRadian)), paint);
// 设置时针粗细
paint.setStrokeWidth(4);
// 画时针
paint.setColor(Color.BLUE);
canvas.drawLine(centerX, centerY,
(float) (centerX + hourLength * Math.cos(hourRadian)),
(float) (centerY - hourLength * Math.sin(hourRadian)), paint);
}
@Override
public void run() {
invalidate();
// 一分钟后重绘
handler.postDelayed(this, 1000);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
// 删除回调类
handler.removeCallbacks(this);
}
}
onDetachedFromWindow是在该View被销毁的时候调用的,此时我们移除此进程。
其中有一些关于计算角度的算法也是很简单的,稍微看看就明白了。
效果图如下: