经常要和绘图打交道,总是用Canvas,bitmap,Paint,但是对它们的理解总是模糊,这里作下总结,加深对绘图的理解。
查询Canvas的官方解释:Canvas用来实现对绘图的操作。你需要4个组件来实现绘图的操作:
a).bitmap,保存着像素
b).canvas.执行画图的命令(向bitmap执行写操作)
c).drawing primtive(e.g.Rect,Path,text,bitmap).绘图的原始内容。
d).paint.(用来描述绘图时的使用的颜色和风格)
这些解释都很抽象,下面我来说下对它们的形象的理解,说明这4个基本控件,这样加深印象:
我们就用现实中的画图来比拟对android中绘图的理解:
a)bitmap.我们绘图肯定需要一块画布,这个画布承载内容用来表现所要显示的图。它有大小,你可以在上面涂颜料进行绘图,或什么都不涂。
b).canvas.画布本身不会被绘制,只有当画家去操作画笔,画布才会绘制上图画。这里的canvas就像相当于画家执行画图的操作,绘图的过程。
c).drawing primitive.画家绘图需要参照物,比如你绘图,肯定是有个目标,比如绘制一个字体,一个圆圈,一个矩形(Rect),另一幅图(bitmap)。
d).paint.绘图需要画笔,paint就相当于这个画笔,你可以定制颜色,粗细。
下面了解Canvas,bitmap,paint一些常用的方法:
Bitmap
.获取bitmap方法
读取InputStream得到位图
InputStream is = getResources().openRawResource(R.drawable.ic_launcher);
BitmapDrawable bd = new BitmapDrawable(is);
Bitmap bp = bd.getBitmap();
解码位图来获取位图,如果你在解析大图片遇到OOM问题,不妨尝试这个方法,这个方法利用JNI调用来解析bitmap,减轻了java层空间的压力。
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
位图的创建,你可以利用下面方法来创建一个bitmap
matrix 指定对bitmap像素的操作
createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter);
//width,height用来指定创建图片的大小,config用来指定创建图片的质量,
其中config有共有4个标准: ALPHA_8,ARGB_4444,ARGB_8888,RBG_565,这些标准分别定义图片像素存储的情况,RBG_565是比较常用的,
ARBG_8888指定的图片质量最高
createBitmap(int width, int height, Bitmap.Config config);
createBitmap(Bitmap source, int x, int y, int width, int height);
Canvas
Canvas();创建一个空画布,它必须绑定bitmap才有效,一般以bitmap来创建一个bitmap的操作,当canvas进行绘图,他绘制的内容都绘在bitmap上。
常见的Canvas绘制使用场合:
a.自定义View时绘制
@Override
OnDraw(Canvas canvas){
//利用canvas执行绘图操作
}
b.利用surfaceView时绘制
SurfaceHoler holder = SurfaceView.getHolder();
Canvas canvas = holder.lockCanvas();
//利用canvas进行绘图操作
holder.unlockCanvasAndPost(canvas);//对canvas解锁,并更新
绘制图形的方法:
drawCircle(float cx, float cy, float radius, Paint paint);//绘制圆圈
drawLine(float startX, float startY, float stopX, float stopY, Paint paint)//绘制直线
裁剪绘图区域
clipXXX()方法类
从当前区域里裁剪一块新的画图区域,裁剪后的新区域即为绘图的新区域。
save(),restore()方法
在绘图时,我们经常会用到canvas类的save(), restore()方法,它们到底是怎么用的呢?
这里有篇详细讲解这两个方法的文章,和大家分享:
https://developer.mozilla.org/en/Canvas_tutorial/Transformations
*save,用来保存Canvas状态,调用之后,你可以调用canvas对画布进行平移、缩放、裁剪等操作。
*restore.用来恢复Canvas之前保存的状态,这样不仅能够复用先前保存的状态,节约资源,另一方面避免对影响图片后续的绘制。
Paint
Paint(),//创建画笔
setColor(int color)//设置画笔的颜色
setAlpha(int a)//设置图片透明度
setDither(boolean dither)//设置画笔是否反锯齿
setStrokeWidth(float width)//设置画笔的宽度
小例子学习
做一小例子温习巩固绘图的操作,要求实现仿卷帘拉起效果。
先简述一下情景:。首先,手机上显示一张完整的图片,图片顶端固定不变。然后,手指在图片上开始滑动,图片的底部随着手指位置而不断变化。你看到的效果是图片的从顶端显示你手指滑动的所在位置。所以,你看到的总是不完整的图片。先上效果,这样大家更好理解。
为了实现上述效果的复用,我们可以自定义一个view,可以设置背景图片,提示的箭头的图片以及图片顶端保留空间大小。
1.自定义MovingView.java,实现上述功能
package com.lawrence;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class MovingView extends View {
private Bitmap backgroundBitmap;
private Bitmap handleBitmap;
private Bitmap showBitmap;
private int backgroundWidth;
private int backgroundHeight;
private int handleWidth;
private int handleHeight;
private int currentY;
private int showHeight;
private int topSpace;
private Canvas mCanvas;
private Rect backgroundSrc;
public MovingView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Movingview);
//获取背景图片
Drawable backgroundDrawable = a.getDrawable(R.styleable.Movingview_movingbackground);
//获取拉手图片
Drawable handleDrawable = a.getDrawable(R.styleable.Movingview_handlebackground);
//获取顶端保留的高度大小。这个高度用做滑动到顶部保留的最小高度。
topSpace = (int) a.getDimension(R.styleable.Movingview_extraspace, 10);
backgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
handleBitmap = ((BitmapDrawable) handleDrawable).getBitmap();
//获取图片高宽
backgroundWidth = backgroundBitmap.getWidth();
backgroundHeight = backgroundBitmap.getHeight();
handleWidth = handleBitmap.getWidth();
handleHeight = handleBitmap.getHeight();
//根据图片大小创建一个相同大小的bitmap
showBitmap = Bitmap.createBitmap(backgroundWidth, backgroundHeight + (handleHeight >> 1), Config.RGB_565);
//创建一个canvas,并绑定bitmap。
mCanvas = new Canvas(showBitmap);
//绘制定backgroundBitmap到showBitmap。
mCanvas.drawBitmap(backgroundBitmap, 0, 0, null);
//在backgroundBitmap底部的中间位置绘制拉手图片
mCanvas.drawBitmap(handleBitmap, (backgroundWidth - handleWidth) >> 1, backgroundHeight - (handleHeight >> 1), null);
}
public MovingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MovingView(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//设置图片的大小为此View的大小
setMeasuredDimension(backgroundWidth, backgroundHeight + (handleHeight >> 1));
}
@Override
protected void onDraw(Canvas canvas) {
//更新绘制图片
canvas.drawBitmap(showBitmap, 0, 0, null);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
currentY = (int) event.getY();
showHeight = currentY;
if(showHeight > backgroundHeight)
showHeight = backgroundHeight;
if(showHeight < topSpace)
showHeight = topSpace;
//清除图片
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//根据滑动位置确定新绘制区域
backgroundSrc = new Rect(0, 0, backgroundWidth, showHeight);
//绘制背景
mCanvas.drawBitmap(backgroundBitmap, backgroundSrc, backgroundSrc, null);
//绘制拉手
mCanvas.drawBitmap(handleBitmap, (backgroundWidth - handleWidth) >> 1, showHeight - (handleHeight >> 1), null);
invalidate();//更新
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
2.自定义MovingView的属性:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Movingview">
<attr name="movingbackground" format="reference"/>
<attr name="handlebackground" format="reference"/>
<attr name="extraspace" format="dimension"/>
</declare-styleable>
</resources>
3.布局文件main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:moving="http://schemas.android.com/apk/res/com.lawrence"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<com.lawrence.MovingView
android:id="@+id/imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
moving:extraspace="30dp"
moving:movingbackground="@drawable/bofucur"
moving:handlebackground="@drawable/updownsel"
/>
</LinearLayout>
主Activity就不贴了,很简单,只要setcontentView(R.layout.main).
附:Demo源码位置:http://download.youkuaiyun.com/detail/a2758963/4459566