您的位置:首页 > 移动开发 > Android开发

【Android知识点精讲】(2)2D绘制与控件绘制

2014-12-22 10:33 211 查看
文章出处:http://write.blog.csdn.net/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被销毁的时候调用的,此时我们移除此进程。
其中有一些关于计算角度的算法也是很简单的,稍微看看就明白了。

效果图如下:



源码下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: