Canvas開篇之drawBitmap方法講解,canvasdrawbitmap
尊重原創,歡迎轉載,轉載請註明: 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,感覺失真比較嚴重!