Canvas开篇之drawBitmap方法讲解

尊重原创,欢迎转载,转载请注明: FROM 
GA_studio   http://blog.csdn.net/tianjian4592

前面讲了paint,后面会花几篇主要讲讲canvas,并且由于最近项目比较紧,所以近期的文章都会“短小精悍”;

paint 作为画笔,里面有非常多而强大的设置方法,比如设置颜色过滤器,设置位图渲染、渐变,设置图像的混合模式等等,而canvas呢?里面提供了哪些利器可以为我们所用,一起来看看:

通过上图我们可以看到,canvas 里的方法基本可以分为这么几类:

1. save、restore 等与层的保存和回滚相关的方法;

2. scale、rotate、clipXXX 等对画布进行操作的方法;

3. drawXXX 等一系列绘画相关的方法;

所以canvas 我们也就可以分上面三块逐个击破,今天咱们主要看 drawXXX里的drawBitmap,看完之后一起做一个漂浮星空的小栗子;

在Canvas 里 drawBitmap 有如下方法可用 :

而咱们也主要讲其中的 drawBitmap(Bitmap,Rect,Rect,Paint);

首先咱们创建一个View,照旧重写里面的 onMeasure、onDraw、onSizeChanged,并且在 onSizeChanged 里拿到view的宽高:

public class DrawBitmapView extends View {
    private Resources mResources;
    private Paint mBitPaint;
    private Bitmap mBitmap;
    private Rect mSrcRect, mDestRect;

    // view 的宽高
    private int mTotalWidth, mTotalHeight;

    public DrawBitmapView(Context context) {
        super(context);
        mResources = getResources();
        initBitmap();
        initPaint();
    }

    private void initPaint() {
        mBitPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBitPaint.setFilterBitmap(true);
        mBitPaint.setDither(true);
    }

    private void initBitmap() {
        mBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.<span style="font-family: Arial, Helvetica, sans-serif;">beautiful_girl</span>))
                .getBitmap();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mTotalWidth = w;
        mTotalHeight = h;
    }
}

上面我们通过

mBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.<span style="font-family: Arial, Helvetica, sans-serif;">beautiful_girl</span>))
                .getBitmap();

拿到了对应的bitmap,这时候我们如果要将它绘制在屏幕上,需要创建两个Rect,其实只要明白了这两个Rect的意义并会灵活运用就可以做出不少效果;

第一个Rect 代表要绘制的bitmap 区域,第二个 Rect 代表的是要将bitmap 绘制在屏幕的什么地方,我们一起来看下:

此时我先定义两个Rect,mSrcRect 取值为整个Bitmap 区域 ,mDestRect 取值为view左上方和bitmap同样大小;

private Rect mSrcRect, mDestRect;
mSrcRect = new Rect(0, 0, mBitWidth, mBitHeight);
mDestRect = new Rect(0, 0, mBitWidth, mBitHeight);

在onDraw 里绘制该位图:

canvas.drawBitmap(mBitmap, mSrcRect, mDestRect, mBitPaint);

此时绘制效果如下,在屏幕的左上方出现了个美女:

画在左上方似乎缺乏美感,我们把美女画在view的中心,没错,我们只需要改变mDestRect:

// 计算左边位置
int left = mHalfWidth - mBitWidth / 2;
// 计算上边位置
int top = mHalfHeight - mBitHeight / 2;
mDestRect = new Rect(left, top, left + mBitWidth, top + mBitHeight);

位置计算的时候,只需要注意在android屏幕坐标系里,左上角的位置是(0,0),往右往下为正,此时效果如下:

既然可以如此轻易的改变绘制的位置,那咱们不断的改变bitmap绘制的位置,模拟一下translate效果;

我们向外提供两个接口:

public void startTranslate() {
        startTranslate(0, 0, 200, 200, 1000);
    }

    /**
     * 移动位图
     *
     * @param startLeft 起始左边距
     * @param startTop 起始距上边距离
     * @param toLeft 到达左边距
     * @param toTop 到达上边距
     * @param duration 时长
     */
    public void startTranslate(int startLeft, int startTop, int toLeft, int toTop, long duration) {
        mStartLeft = startLeft;
        mStartTop = startTop;

        mToLeft = toLeft;
        mToTop = toTop;

        // 使用ValueAnimator创建一个过程
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
        valueAnimator.setDuration(duration);
        valueAnimator.setInterpolator(new AccelerateInterpolator());
        valueAnimator.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animator) {
                // 不断重新计算上下左右位置
                float fraction = (Float) animator.getAnimatedValue();
                int currentLeft = (int) ((mToLeft - mStartLeft) * fraction + mStartLeft);
                int currentTop = (int) ((mToTop - mStartTop) * fraction + mStartTop);
                if (mDestRect == null) {
                    mDestRect = new Rect(currentLeft, currentTop, currentLeft + mBitWidth,
                            currentTop + mBitHeight);
                }
                mDestRect.left = currentLeft;
                mDestRect.right = currentLeft + mBitWidth;
                mDestRect.top = currentTop;
                mDestRect.bottom = currentTop + mBitHeight;
                // 重绘
                postInvalidate();
            }
        });
        valueAnimator.start();
    }

Activity 里控制view的移动:

final DrawBitmapView drawBitmapView = new DrawBitmapView(this);
        setContentView(drawBitmapView, new LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT));
        drawBitmapView.startTranslate();
        drawBitmapView.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Random random = new Random();
                int startLeft = random.nextInt(200);
                int startTop = random.nextInt(250);
                int toLeft = random.nextInt(550) + 200;
                int toBottom = random.nextInt(1000) + 250;
                drawBitmapView.startTranslate(startLeft, startTop, toLeft, toBottom, 1000);
                return true;
            }
        });

    }

点击之后起始点和到达点随机生成,此时效果如下:

相信到这里大家已经能灵活控制bitmap的位置了,顺势咱们再做个水平缩放为0的小例子:

public void startScale(long duration) {

        // 使用ValueAnimator创建一个过程
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(1, 0);
        valueAnimator.setDuration(duration);
        valueAnimator.setInterpolator(new AccelerateInterpolator());
        valueAnimator.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animator) {
                // 不断重新计算上下左右位置
                float fraction = (Float) animator.getAnimatedValue();
                if (mDestRect == null) {
                    mDestRect = new Rect(0, 0, mBitWidth,
                            mBitHeight);
                }
                mDestRect.right = (int) (fraction * mBitWidth);
                // 重绘
                postInvalidate();
            }
        });
        valueAnimator.start();
    }

只需要不断减小mDestRect.right即可,非常简单,看下效果:

上面两个例子都是通过改变mDestRect ,在哪些时候我们需要动态改变mSrcRect 呢?我前面讲过一个水波纹的例子,那里面就是不断截取水波纹的一部分,进行展示,由于是连续截取,所以视觉感受上是连续波纹效果,有兴趣的同学可以看看参考下;

好了本篇就讲这么多,有些同学可能会想,尼玛,这么简单的玩意儿能做毛线牛逼动效啊,其实往往再复杂的动效也就是由一个个小点组成的,而思路和方案的选取就已经决定了能否成功的做出酷炫又如丝般顺滑的效果,好的思路又往往来源于对简单方法的深刻理解;

下篇我们就一起完成一个星球浮动的效果!

转成gif之后失真严重,并且感觉有卡顿,真实情况如梦如幻,如丝般顺滑!

好了,还是不吹牛逼了,这个栗子只是为了加深对这个方法的理解,同时扩展动效绘制的思路!

注:哪位大神有好的制作gif的方法,还望不吝赐教,目前我是采用的电脑录屏,然后PS转gif,感觉失真比较严重!

时间: 2024-10-13 16:17:52

Canvas开篇之drawBitmap方法讲解的相关文章

HTML5 canvas save和restore方法讲解

源:http://www.silverlightchina.net/html/HTML_5/study/2012/0326/14828.html save()和restore()方法是绘制复杂图形必不可少的方法.它们分别是用来 保存和恢复 canvas 状态的,都没有参数. Canvas 状态是以堆(stack)的方式保存的,每一次调用 save 方法,当前的状态就会被推入堆中保存起来.这种状态包括:当前应用的变形(即移动,旋转和缩放,见下): strokeStyle, fillStyle, g

Canvas之translate、scale、rotate、skew方法讲解!

尊重原创,欢迎转载,转载请注明: FROM  GA_studio   http://blog.csdn.net/tianjian4592 前面说Canvas大致可以分为三类: 1. save.restore 等与层的保存和回滚相关的方法: 2. scale.rotate.clipXXX 等对画布进行操作的方法: 3. drawXXX 等一系列绘画相关的方法: 前面主要讲了drawBitmap方法,并举了一个星球浮动的栗子,在那个例子中,星球有大有小,需要移动,有时候可能需求上还需要旋转或错切,有

HTML5 canvas 中的arcTo()方法的用法

除了arc()之外,Canvas的绘图环境对象还提供了另一个用于创建圆弧路径的方法,那就是arcTo().改方法接受了5个参数: arcTo(x1,x2,y1,y2,radius) arcTo()方法的参数分别代表两个点击圆形半径.该方法一指定的半径来绘制一条圆弧,此圆弧与当前点到第一个点(x1,y1)的连线相切,而且第一个点到第二点(x2,y2)的连线也相切.该方法的这些特性,使得它非常适合用了绘制矩形的原角. 使用arcTo()方法: html: <!Doctyp html> <ht

canvas入门-3渐变方法

1.canvas中渐变方式有2种:线性渐变和径向梯度渐变 createLineGradient() createRadialGradient() addColorStop(var1,var2); var1 取值在0-1之间 var2 是颜色值 前面有讲述线性渐变的作用,渐变是一个渐变对象 径向渐变方法createRadialGradient创建放射状/圆形渐变对象 context.createRadialGradient(x0,y0,r0,x1,y1,r1); 渐变可用于填充矩形.圆形.线条.文

NSArray排序方法讲解

给数组排序有着多种方式 最麻烦的是sortedArrayUsingSelector:,其次是sortedArrayUsingDescriptors:,最容易使用的就是sortedArrayUsingComparator: 从最容易使用的开始吧: // 原始数组 NSArray *array = @[@"b", @"a", @"x", @"o", @"g", @"o"]; // 排序数组

HTML5 canvas绘图基本使用方法

<canvas></canvas>是HTML5中新增的标签,用于绘制图形,实际上,这个标签和其他的标签一样,其特殊之处在于该标签可以获取一个CanvasRenderingContext2D对象,我们可以通过JavaScript脚本来控制该对象进行绘图. <canvas></canvas>只是一个绘制图形的容器,除了id.class.style等属性外,还有height和width属性.在<canvas>>元素上绘图主要有三步: 获取<

【转】Nginx服务器的反向代理proxy_pass配置方法讲解

[转]Nginx服务器的反向代理proxy_pass配置方法讲解 转自:http://www.jb51.net/article/78746.htm 就普通的反向代理来讲Nginx的配置还是比较简单的,如: location ~ /* { proxy_pass http://127.0.0.1:8008; } 或者可以 location / { proxy_pass http://127.0.0.1:8008; } Apache2的反向代理的配置是: ProxyPass /ysz/ http://

Camera图片特效处理综述(Bitmap的Pixels处理、Canvas/paint的drawBitmap处理、旋转图片、裁截图片、播放幻灯片浏览图片&lt;线程固定时间显示一张&gt;)

一种是直接对Bitmap的像素进行操作,如:叠加.边框.怀旧.(高斯)模糊.锐化(拉普拉斯变换). Bitmap.getPixels(srcPixels, 0, width, 0, 0, width, height); newR = (int) (pixR * alpha + layR * (1 - alpha));    newR = Math.min(255, Math.max(0, newR));    ...    srcPixels[pos] = Color.argb(newA, ne

canvas的两个方法说明

今天在用canvas的时候,发现有两个方法比较陌生,在此记录详细说明一下. (1)文本绘制的一个方法 canvas.drawTextOnPath(text, path, hOffset, vOffset, paint) 参数说明:text是所绘制的文本 path是所绘制文本遵循的路径 hOffset是沿路径方向上的偏移量 vOffset是与路径方向垂直的那个方向上的偏移量 paint是画笔 为了彻底说明hOffset和vOffset的意思,你可以看下图: 红色箭头为文本绘制方向,那么hOffse