【Android知识点精讲】(2)2D绘制与控件绘制
2014-12-22 10:33
211 查看
文章出处:http://write.blog.csdn.net/postedit/42077065
画实心圆:
最后我们来看一下完整的文件:
我们在触摸屏幕的时候改变了画笔的颜色,并调用invalidate()方法,对整个屏幕进行刷新。 在onDraw方法中如果重复不停的调用invalidate()方法可以实现简单的动画效果。我们这里使用r来定义半径,在未达到100时会一直扩大显示圆,实现了一个简单的动画效果。
canvas.drawBitmap(bitmap, 10, 10, null);
方法二:
drawable.draw(canvas);
两种都可以达到同样的的效果。
直接上代码:
创建一个二维数组来保存bitmap2所读取到的像素值。getPixels第1个参数是我们创建的二维数组,第2个参数是写入到pixels[]中的第一个像素索引值 ,第3个是用来表示pixels[]数组中每行的像素个数,用与行于行之间区分,其绝对值必须大于第6个参数,但不必大于要读取的图片的宽度,第4、5个参数是从位图中读取的第一个像素坐标的x、y值,第6个参数是每一行读取的像素宽度,第7个参数是读取的行数。如果pixels太小将会抛出异常。
其中有一些关于计算角度的算法也是很简单的,稍微看看就明白了。
效果图如下:
源码下载
一 绘制图形和文本的基本方法
首先指定好画笔的颜色: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被销毁的时候调用的,此时我们移除此进程。
其中有一些关于计算角度的算法也是很简单的,稍微看看就明白了。
效果图如下:
源码下载
相关文章推荐
- android 2D图像的绘制
- Android 中使用OpenGL ES进行2D开发(绘制第一个三角形番外篇)
- Android控件绘制过程
- 【边做项目边学Android】知识点:Android控件系列之Toast
- 图表控件TeeChart干货分享(绘制2D、3D实时曲线---VC++示例源代码--网络首发)
- Direct2D绘制的MFC控件
- 【边做项目边学Android】知识点:Android控件系列之ProgressDialog与ProgressBar
- Android下简单2D图像绘制
- Android开发中目前流行控件和知识点总结
- chromium for android v34 2dCanvas硬件绘制实现分析
- Android 中使用OpenGL ES进行2D开发(绘制矩形)
- Android控件绘制过程
- Android开发中目前流行控件和知识点总结
- android实现按钮拖动效果及路径的绘制等2D绘图功能---待完善
- Direct2D绘制的MFC控件
- Android 中使用OpenGL ES进行2D开发(绘制第一个三角形)
- Android 3D学习之利用NDK绘制2D四边形
- android图形图表绘制控件
- [Android]OpenGL ES实现三角形/四边形2D/3D绘制
- Android控件绘制过程