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的宽高:

  1. public class DrawBitmapView extends View {  
  2.     private Resources mResources;  
  3.     private Paint mBitPaint;  
  4.     private Bitmap mBitmap;  
  5.     private Rect mSrcRect, mDestRect;  
  6.   
  7.     // view 的宽高  
  8.     private int mTotalWidth, mTotalHeight;  
  9.   
  10.     public DrawBitmapView(Context context) {  
  11.         super(context);  
  12.         mResources = getResources();  
  13.         initBitmap();  
  14.         initPaint();  
  15.     }  
  16.   
  17.     private void initPaint() {  
  18.         mBitPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  19.         mBitPaint.setFilterBitmap(true);  
  20.         mBitPaint.setDither(true);  
  21.     }  
  22.   
  23.     private void initBitmap() {  
  24.         mBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.<span style="font-family: Arial, Helvetica, sans-serif;">beautiful_girl</span>))  
  25.                 .getBitmap();  
  26.     }  
  27.   
  28.     @Override  
  29.     protected void onDraw(Canvas canvas) {  
  30.         super.onDraw(canvas);  
  31.     }  
  32.   
  33.     @Override  
  34.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  35.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  36.     }  
  37.   
  38.     @Override  
  39.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  40.         super.onSizeChanged(w, h, oldw, oldh);  
  41.         mTotalWidth = w;  
  42.         mTotalHeight = h;  
  43.     }  
  44. }  
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;
    }
}

上面我们通过

  1. mBitmap = ((BitmapDrawable) mResources.getDrawable(R.drawable.<span style="font-family: Arial, Helvetica, sans-serif;">beautiful_girl</span>))  
  2.                 .getBitmap();  
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同样大小;

  1. private Rect mSrcRect, mDestRect;  
private Rect mSrcRect, mDestRect;

  1. mSrcRect = new Rect(0, 0, mBitWidth, mBitHeight);  
  2. mDestRect = new Rect(0, 0, mBitWidth, mBitHeight);  
mSrcRect = new Rect(0, 0, mBitWidth, mBitHeight);
mDestRect = new Rect(0, 0, mBitWidth, mBitHeight);

在onDraw 里绘制该位图:

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

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



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

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

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


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

我们向外提供两个接口:

  1. public void startTranslate() {  
  2.         startTranslate(0, 0, 200, 200, 1000);  
  3.     }  
  4.   
  5.     /**  
  6.      * 移动位图  
  7.      *   
  8.      * @param startLeft 起始左边距  
  9.      * @param startTop 起始距上边距离  
  10.      * @param toLeft 到达左边距  
  11.      * @param toTop 到达上边距  
  12.      * @param duration 时长  
  13.      */  
  14.     public void startTranslate(int startLeft, int startTop, int toLeft, int toTop, long duration) {  
  15.         mStartLeft = startLeft;  
  16.         mStartTop = startTop;  
  17.   
  18.         mToLeft = toLeft;  
  19.         mToTop = toTop;  
  20.   
  21.         // 使用ValueAnimator创建一个过程  
  22.         ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);  
  23.         valueAnimator.setDuration(duration);  
  24.         valueAnimator.setInterpolator(new AccelerateInterpolator());  
  25.         valueAnimator.addUpdateListener(new AnimatorUpdateListener() {  
  26.   
  27.             @Override  
  28.             public void onAnimationUpdate(ValueAnimator animator) {  
  29.                 // 不断重新计算上下左右位置  
  30.                 float fraction = (Float) animator.getAnimatedValue();  
  31.                 int currentLeft = (int) ((mToLeft - mStartLeft) * fraction + mStartLeft);  
  32.                 int currentTop = (int) ((mToTop - mStartTop) * fraction + mStartTop);  
  33.                 if (mDestRect == null) {  
  34.                     mDestRect = new Rect(currentLeft, currentTop, currentLeft + mBitWidth,  
  35.                             currentTop + mBitHeight);  
  36.                 }  
  37.                 mDestRect.left = currentLeft;  
  38.                 mDestRect.right = currentLeft + mBitWidth;  
  39.                 mDestRect.top = currentTop;  
  40.                 mDestRect.bottom = currentTop + mBitHeight;  
  41.                 // 重绘  
  42.                 postInvalidate();  
  43.             }  
  44.         });  
  45.         valueAnimator.start();  
  46.     }  
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的移动:

  1. final DrawBitmapView drawBitmapView = new DrawBitmapView(this);  
  2.         setContentView(drawBitmapView, new LayoutParams(LayoutParams.MATCH_PARENT,  
  3.                 LayoutParams.MATCH_PARENT));  
  4.         drawBitmapView.startTranslate();  
  5.         drawBitmapView.setOnTouchListener(new OnTouchListener() {  
  6.   
  7.             @Override  
  8.             public boolean onTouch(View v, MotionEvent event) {  
  9.                 Random random = new Random();  
  10.                 int startLeft = random.nextInt(200);  
  11.                 int startTop = random.nextInt(250);  
  12.                 int toLeft = random.nextInt(550) + 200;  
  13.                 int toBottom = random.nextInt(1000) + 250;  
  14.                 drawBitmapView.startTranslate(startLeft, startTop, toLeft, toBottom, 1000);  
  15.                 return true;  
  16.             }  
  17.         });  
  18.   
  19.     }  
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的小例子:

  1. public void startScale(long duration) {  
  2.   
  3.         // 使用ValueAnimator创建一个过程  
  4.         ValueAnimator valueAnimator = ValueAnimator.ofFloat(1, 0);  
  5.         valueAnimator.setDuration(duration);  
  6.         valueAnimator.setInterpolator(new AccelerateInterpolator());  
  7.         valueAnimator.addUpdateListener(new AnimatorUpdateListener() {  
  8.   
  9.             @Override  
  10.             public void onAnimationUpdate(ValueAnimator animator) {  
  11.                 // 不断重新计算上下左右位置  
  12.                 float fraction = (Float) animator.getAnimatedValue();  
  13.                 if (mDestRect == null) {  
  14.                     mDestRect = new Rect(0, 0, mBitWidth,  
  15.                             mBitHeight);  
  16.                 }  
  17.                 mDestRect.right = (int) (fraction * mBitWidth);  
  18.                 // 重绘  
  19.                 postInvalidate();  
  20.             }  
  21.         });  
  22.         valueAnimator.start();  
  23.     }  
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,感觉失真比较严重!

  • 11
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值