繪圖中的其他知識(shí)點(diǎn)總結(jié)

Draw

Canvas 可以繪制的對(duì)象:弧線(arcs)纯丸、填充顏色(argb和color)晌畅、圓(circle和oval)但指、Bitmap、點(diǎn)(point)、線(line)棋凳、矩形(Reat)拦坠、圖片(Picture)、圓形矩形(RoundRect)剩岳、文本(text)贞滨、頂點(diǎn)(Vertices)、路徑(path)拍棕。

Canvas不僅僅可以draw一些圖形晓铆、圖片,其本身也提供了可操作的方法:rorate(旋轉(zhuǎn))绰播、scale(壓縮)骄噪、translate(平移)、skew(扭曲)等蠢箩。

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

 for (int i = 0; i < 360; i = i + 6) {
     canvas.save();
     canvas.rotate(i, 100, 100);
     canvas.drawLine(100, 0, 100, 10, new Paint());
     canvas.restore();
 }
}
效果

Matrix

canvas 類當(dāng)中drawBitmap(Bitmap bitmap,Matrix matrix,Paint paint)方法當(dāng)中有個(gè)參數(shù)是matrix類型链蕊,它是個(gè)3x3矩陣。Matrix在android中的主要作用是圖像變換忙芒、如平移示弓、旋轉(zhuǎn)、縮放呵萨、扭曲等奏属。
Matrix提供了Translate(平移)、Scale(縮放)潮峦、Rotate(旋轉(zhuǎn))囱皿、Skew(扭曲)四中變換操作,這4種操作實(shí)質(zhì)上都是調(diào)用了setValues方法來設(shè)置矩陣數(shù)組來達(dá)到效果忱嘹。除translate外嘱腥,scale、rotate拘悦、skew 都是圍繞一個(gè)中心點(diǎn)來進(jìn)行的齿兔,如果不指定,默認(rèn)情況下是圍繞(0础米,0)來進(jìn)行相應(yīng)的變換的分苇。
Matrix提供的四種操作,每一種都有pre屁桑、set医寿、post三種形式。原因是矩陣乘法不滿足乘法交換律蘑斧,因此左乘還是右乘最終的效果都不一樣靖秩。我們可以把Matrix變換想象成一個(gè)隊(duì)列须眷,隊(duì)列里面包含了若干個(gè)變換操作,隊(duì)列中每個(gè)操作按照先后順序操作變換目標(biāo)完成變換沟突,pre相當(dāng)于向隊(duì)首增加一個(gè)操作花颗,post相當(dāng)于向隊(duì)尾增加一個(gè)操作,set相當(dāng)于清空當(dāng)前隊(duì)列重新設(shè)置事扭。
下面的例子和解釋很重要捎稚。
例1

//這段代碼只有translate(100, 100)生效乐横,因?yàn)榈诙€(gè)set會(huì)把之前隊(duì)列中的操作清除求橄。
Matrix m = new Matrix();
m.setRotate(100); 
m.setTranslate(100, 100);```
例2

//這段代碼先執(zhí)行translate(100, 100),后執(zhí)行rotate(100)
Matrix m = new Matrix();
m.setTranslate(100, 100);
m.postRotate(100);```
例3

//這段代碼先執(zhí)行rotate(100)葡公,后執(zhí)行translate(100, 100)
Matrix m = new Matrix();
m.setTranslate(100, 100);
m.preRotate(100);```
例4

//這段代碼的執(zhí)行順序:translate(100f, 100f) -> scale(2f, 2f) -> scale(0.5f, 0.5f) -> translate(50f, 50f)
Matrix m = new Matrix();
m.preScale(2f, 2f);
m.preTranslate(100f, 100f);
m.postScale(0.5f, 0.5f);
m.postTranslate(50f, 50f);```
例5

//這段代碼的執(zhí)行順序:translate(50f, 50f) -> scale(0.8f, 0.8f) -> scale(3f, 3f)
Matrix m = new Matrix();
m.postTranslate(100, 100);   
m.preScale(0.5f, 0.5f);
m.setScale(0.8f, 0.8f);   
m.postScale(3f, 3f);
m.preTranslate(50f, 50f);```
Matrix 映射方法
Matrix提供了mapPoints()罐农,mapRects(),mapVectors()等映射方法催什,用來獲取經(jīng)Matrix映射后的值涵亏。

//這段代碼的作用是獲取經(jīng)過平移后該bitmap四個(gè)點(diǎn)的坐標(biāo)
Matrix m = new Matrix();
m.postTranslate(100f, 100f);

float[] src = {
0, 0,
0, bitmap.getHeight(),
bitmap.getWidth(), 0,
bitmap.getWidth(), bitmap.getHeight()
};
float[] dst = new float[8];

m.mapPoints(dst, src);


### Xfermode
XferMode主要是將2張圖片合在一起,由用戶自己決定是選中圖片重疊的部分還是非重疊的部分蒲凶,可以參考Android官方提供的圖片,圖中 圓為Dst,正方形為Src
![](http://upload-images.jianshu.io/upload_images/2154124-796ead1b6739ea1b?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

Xfermode的類型有16種:
private static final Xfermode[] sModes = {
new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
new PorterDuffXfermode(PorterDuff.Mode.SRC),
new PorterDuffXfermode(PorterDuff.Mode.DST),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.XOR),
new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
};

如果我們想要實(shí)現(xiàn)一個(gè)圓心圖像的話可以使用圖中dstin的方式气筋,即在我們的原圖上面再畫一個(gè)實(shí)心圓形圖。首先我們需要生成iyge圓形的bitmap:

private Bitmap mCircleBitmap;

//生成一個(gè)實(shí)心圓形Bitmap,這個(gè)Bitmap寬高要與當(dāng)前的View的寬高相同
private Bitmap getCircleBitmap() {
    if (mCircleBitmap == null) {
        mCircleBitmap = Bitmap.createBitmap(2 * mRadius, 2 * mRadius,
                Config.ARGB_8888);
        Canvas canvas = new Canvas(mCircleBitmap);

        mPaint.reset();
        mPaint.setStyle(Style.FILL);
        canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
    }
    return mCircleBitmap;

}

在將這個(gè)圓形圖片蓋在原圖上面

//將兩張圖片以XferMode(DST_IN)的方式組合到一張照片中
private Bitmap combineBitmap(Drawable drawable, Bitmap maskBitmap) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
// 將drawable轉(zhuǎn)bitmap
Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
//將圖片自動(dòng)放縮到View的寬高旋圆,即2倍的半徑
drawable.setBounds(0, 0, mRadius2, mRadius2);
drawable.draw(canvas);

    // 先將XferMode設(shè)置好宠默,然后將蓋在上面的bitmap繪制出來
    mPaint.reset();
    mPaint.setXfermode(xfermode);
    canvas.drawBitmap(maskBitmap, 0, 0, mPaint);
    mPaint.setXfermode(null);

    return bitmap;
}

//然后將組合得到的bitmaptoin通過canvas繪制到界面上
@Override
protected void onDraw(Canvas canvas) {
//獲取設(shè)置的src圖片
Drawable drawable = getDrawable();
//獲取蓋在src上面的實(shí)心圓形Bitmap
Bitmap circleBitmap = getCircleBitmap();

    //兩張圖片以XferMode(DST_IN)的方式組合
    Bitmap bitmap = combineBitmap(drawable, circleBitmap);

    //將最終的bitmap畫到畫板上面
    canvas.drawBitmap(bitmap, 0, 0, mPaint);

}
完整的類代碼

package com.hc.circleimage;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Xfermode;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;

public class XfermodeCircleImage extends ImageView {
private int mRadius;
private Paint mPaint;
private Xfermode xfermode;
private Bitmap mCircleBitmap;

public XfermodeCircleImage(Context context) {
    super(context);
    init();
}

public XfermodeCircleImage(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

private void init() {
    mPaint = new Paint();
    xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width = getMeasuredWidth();
    int height = getMeasuredHeight();

    if (width > height) {
        mRadius = height / 2;
    } else {
        mRadius = width / 2;
    }

    setMeasuredDimension(mRadius * 2, mRadius * 2);
}

//生成一個(gè)實(shí)心圓形Bitmap,這個(gè)Bitmap寬高要與當(dāng)前的View的寬高相同
private Bitmap getCircleBitmap() {
    if (mCircleBitmap == null) {
        mCircleBitmap = Bitmap.createBitmap(2 * mRadius, 2 * mRadius,
                Config.ARGB_8888);
        Canvas canvas = new Canvas(mCircleBitmap);

        mPaint.reset();
        mPaint.setStyle(Style.FILL);
        canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
    }
    return mCircleBitmap;
}

//將兩張圖片以XferMode(DST_IN)的方式組合到一張照片中
private Bitmap combineBitmap(Drawable drawable, Bitmap maskBitmap) {
    int width = drawable.getIntrinsicWidth();
    int height = drawable.getIntrinsicHeight();
    // 將drawable轉(zhuǎn)bitmap
    Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    //將圖片自動(dòng)放縮到View的寬高,即2倍的半徑
    drawable.setBounds(0, 0, mRadius*2, mRadius*2);
    drawable.draw(canvas);

    // 先將XferMode設(shè)置好灵巧,然后將蓋在上面的bitmap繪制出來
    mPaint.reset();
    mPaint.setXfermode(xfermode);
    canvas.drawBitmap(maskBitmap, 0, 0, mPaint);
    mPaint.setXfermode(null);

    return bitmap;
}

@Override
protected void onDraw(Canvas canvas) {
    //獲取設(shè)置的src圖片
    Drawable drawable = getDrawable();
    //獲取蓋在src上面的實(shí)心圓形Bitmap
    Bitmap circleBitmap = getCircleBitmap();

    //兩張圖片以XferMode(DST_IN)的方式組合
    Bitmap bitmap = combineBitmap(drawable, circleBitmap);

    //將最終的bitmap畫到畫板上面
    canvas.drawBitmap(bitmap, 0, 0, mPaint);

}

}

這樣就可以將一個(gè)方形的圖片搀矫,變成圓形了。

### BitmapShader

BitmapShader是Shader的子類刻肄,可以通過Paint.setShader(Shader shader)進(jìn)行設(shè)置瓤球、,BitmapShader有啥作用呢,它可以根據(jù)你設(shè)置的方式(下面介紹)將圖片鋪滿你所選的區(qū)域敏弃,有哪幾種方式“鋪”呢卦羡?有以下幾種:
(1)CLAMP:拉伸,在x方向上是圖片的最后一列像素重復(fù)平鋪麦到,而y方向是最后一行往下拉伸(當(dāng)bitmap比要繪制的圖形小時(shí)拉伸位圖的最后一個(gè)像素绿饵;當(dāng)bitmap比要繪制的圖形大時(shí),根據(jù)繪制圖形剪裁bitmap )
(2)REPEAT: 重復(fù)隅要,很容易理解蝴罪,圖片重復(fù)平鋪過去(當(dāng)繪畫的區(qū)域比圖片本身要大時(shí)會(huì)重復(fù),從設(shè)置的x或者y軸方向復(fù)制bitmap)
(3)MIRROR:鏡像步清,就是將圖片翻轉(zhuǎn)要门。
CLAMP的方式:![](http://upload-images.jianshu.io/upload_images/2154124-401f6c0f76db8d57?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
REPEAT方式:![](http://upload-images.jianshu.io/upload_images/2154124-10d19cc1356a10be?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
MIRROR方式:![](http://upload-images.jianshu.io/upload_images/2154124-e469691be7d7e470?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

使用BitmapShader制作圓形圖片的方法非常簡單虏肾,只需通過Bitmap構(gòu)造出一個(gè)BitmapShader,并將這個(gè)BitmapShader設(shè)置到當(dāng)前的Paint當(dāng)中欢搜,用這個(gè)Paint繪制一個(gè)圓就可以了

package com.hc.circleimage;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;

public class ShaderCircleImage extends ImageView {

private int mRadius;
private Paint mPaint;

public ShaderCircleImage(Context context) {
    super(context);
    init();
}

public ShaderCircleImage(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

private void init() {
    mPaint = new Paint();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width = getMeasuredWidth();
    int height = getMeasuredHeight();

    if (width > height) {
        mRadius = height / 2;
    } else {
        mRadius = width / 2;
    }

    setMeasuredDimension(mRadius * 2, 2 * mRadius);
}

private Bitmap drawableToBitmap(Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    } else {
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        Bitmap bitmap = Bitmap.createBitmap(width, height,
                Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, mRadius*2, mRadius*2);
        drawable.draw(canvas);
        return bitmap;
    }
}

@Override
protected void onDraw(Canvas canvas) {
    // 將Drawable轉(zhuǎn)為Bitmap
    Bitmap bmp = drawableToBitmap(getDrawable());
    // 通過Bitmap和指定x,y方向的平鋪方式構(gòu)造出BitmapShader對(duì)象
    BitmapShader mBitmapShader = new BitmapShader(bmp, TileMode.CLAMP,
            TileMode.CLAMP);
    // 將BitmapShader設(shè)置到當(dāng)前的Paint對(duì)象中
    mPaint.setShader(mBitmapShader);
    // 繪制出一個(gè)圓
    canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);

}

}

之前我的文章當(dāng)中介紹的一個(gè)圓形圖像也就是用這種方式實(shí)現(xiàn)的[CircleImageView](http://www.reibang.com/p/ef6c5b871db5)
### ClipPath
ClipPath 也可以用來繪制圓形

package com.hc.circleimage;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;

public class ClipCircleImage extends ImageView {
private int mRadius;
private Paint mPaint;

public ClipCircleImage(Context context) {
    super(context);
    init();
}

public ClipCircleImage(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

private void init() {
    mPaint = new Paint();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width = getMeasuredWidth();
    int height = getMeasuredHeight();

    if (width > height) {
        mRadius = height / 2;
    } else {
        mRadius = width / 2;
    }

    setMeasuredDimension(mRadius * 2, 2 * mRadius);
}

private Bitmap drawableToBitmap(Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    } else {
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        Bitmap bitmap = Bitmap.createBitmap(width, height,
                Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, mRadius*2, mRadius*2);
        drawable.draw(canvas);
        return bitmap;
    }
}

@Override
protected void onDraw(Canvas canvas) {
    // 將Drawable轉(zhuǎn)為Bitmap
    Bitmap bmp = drawableToBitmap(getDrawable());
    Path path = new Path(); 
    //按照逆時(shí)針方向添加一個(gè)圓
    path.addCircle(mRadius, mRadius, mRadius, Direction.CCW);
    //先將canvas保存
    canvas.save();
    //設(shè)置為在圓形區(qū)域內(nèi)繪制
    canvas.clipPath(path);
    //繪制Bitmap
    canvas.drawBitmap(bmp, 0, 0, mPaint);
    //恢復(fù)Canvas
    canvas.restore();
}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末封豪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子炒瘟,更是在濱河造成了極大的恐慌吹埠,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疮装,死亡現(xiàn)場離奇詭異缘琅,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)廓推,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門刷袍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人樊展,你說我怎么就攤上這事呻纹。” “怎么了专缠?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵雷酪,是天一觀的道長。 經(jīng)常有香客問我涝婉,道長哥力,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任嘁圈,我火速辦了婚禮省骂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘最住。我一直安慰自己钞澳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布涨缚。 她就那樣靜靜地躺著轧粟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪脓魏。 梳的紋絲不亂的頭發(fā)上兰吟,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音茂翔,去河邊找鬼混蔼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛珊燎,可吹牛的內(nèi)容都是我干的惭嚣。 我是一名探鬼主播遵湖,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼晚吞!你這毒婦竟也來了延旧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤槽地,失蹤者是張志新(化名)和其女友劉穎迁沫,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捌蚊,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡集畅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逢勾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牡整。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡藐吮,死狀恐怖溺拱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谣辞,我是刑警寧澤迫摔,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站泥从,受9級(jí)特大地震影響句占,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜躯嫉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一纱烘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧祈餐,春花似錦擂啥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蜒谤,卻和暖如春山宾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鳍徽。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國打工资锰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人阶祭。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓绷杜,卻偏偏與公主長得像翎猛,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子接剩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容