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

来源:互联网 发布:distinct sql 用法 编辑:程序博客网 时间:2024/06/05 07:50

文章出处: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")@Overridepublic boolean onTouchEvent(MotionEvent event) {showFirst = !showFirst;invalidate();return super.onTouchEvent(event);}@SuppressLint("DrawAllocation")@Overrideprotected 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")@Overrideprotected 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);}@Overrideprotected 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")@Overrideprotected 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);}@Overridepublic void run() {invalidate();// 一分钟后重绘handler.postDelayed(this, 1000);}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();// 删除回调类handler.removeCallbacks(this);}}
onDetachedFromWindow是在该View被销毁的时候调用的,此时我们移除此进程。
其中有一些关于计算角度的算法也是很简单的,稍微看看就明白了。

效果图如下:


源码下载



0 0
原创粉丝点击