除了常用的畫筆屬性,比如普通的畫筆(Paint)绑咱,帶邊框显拜、填充的style烙如,顏色(Color),寬度(StrokeWidth)苹祟,抗鋸齒(ANTI_ALIAS_FLAG)等砸抛,Android還提供了各種各樣專業(yè)的畫筆工具,如記號筆树枫、毛筆直焙、蠟筆等,使用它們可以實現(xiàn)更加豐富的效果砂轻。
PorterDuffXfermode
下圖中列舉了16種PorterDuffXfermode奔誓,有點像數(shù)學中集合的交集、并集這樣的概念搔涝,它控制的是兩個圖像間的混合顯示模式厨喂。
PorterDuffXfermode設置的是兩個圖層交集區(qū)域的顯示方式和措,dst是先畫的圖形,而src是后畫的圖形杯聚。
這些模式也不是經(jīng)常使用臼婆,用的最多的是抒痒,使用一張圖片作為另一張圖片的遮罩層幌绍,通過控制遮罩層的圖形,來控制下面被遮罩圖形的顯示效果故响。其中最常用的就是通過DST_IN傀广、SRC_IN模式來實現(xiàn)將一個矩形圖片變成圓角圖片或者圓形圖片的效果。
- 圓角矩形
要使用PorterDuffXfermode非常簡單彩届,只需要讓畫面擁有這個屬性就可以了伪冰。比如要實現(xiàn)下面圓角矩形的效果:
先用一個普通畫筆畫一個Mask遮罩層,再用帶PorterDuffXfermode的畫筆將圖像畫在遮罩層上樟蠕,這樣就可以通過上面所說的效果來混合兩個圖像了贮聂,代碼如下:
mPaint=new Paint();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.iu);
mBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas(mBitmap);
mPaint.setAntiAlias(true);
canvas.drawRoundRect(0,0,bitmap.getWidth(),bitmap.getHeight(),80,80,mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap,0,0,mPaint);
mImageView= (ImageView) findViewById(R.id.img);
mImageView.setImageBitmap(mBitmap);
- 刮刮卡效果
若要實現(xiàn)刮刮卡效果(刮刮卡一般有兩個圖層,即上面的用來被刮掉的圖層和下面隱藏的圖層)寨辩。在初始狀態(tài)下吓懈,上面的圖層會將下面整個圖層覆蓋,當你用手刮上面的圖層的時候靡狞,下面的圖層會慢慢顯示出來耻警,類似于畫圖工具中的橡皮擦效果。
首先要做一些初始化工作甸怕,例如準備好圖片甘穿,設置好Paint的一些屬性,代碼如下:
mPaint = new Paint();
/**
* 將畫筆的透明度設置為0梢杭,這樣才能顯示出擦除效果温兼。
* 在使用PorterDuffXfermode進行圖層混合時,并不是簡單地只進行圖層的計算武契,同時也會計算透明通道的值妨托。
* 正是由于混合了透明通道,才形成了這樣的效果吝羞。
*/
mPaint.setAlpha(0);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPaint.setStyle(Paint.Style.STROKE);
//使Paint的筆觸更加圓滑一點
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeWidth(50);
//使Paint的連接處更加圓滑一點
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPath = new Path();
mBgBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mFgBitmap);
mCanvas.drawColor(Color.GRAY);
獲取用戶手指滑動所產(chǎn)生的路徑并將使用DST_IN模式將路徑繪制到前面覆蓋的圖層上兰伤,代碼如下所示。使用Path保存用戶手指劃過的痕跡钧排。當然敦腔,如果使用貝塞爾曲線來做優(yōu)化則會得到更好的顯示效果,這里為了簡化功能恨溜,就不使用貝塞爾曲線了符衔。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPath.reset();
mPath.moveTo(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
mPath.lineTo(event.getX(), event.getY());
break;
}
//使用DST_IN模式將路徑繪制到前面覆蓋的圖層上
mCanvas.drawPath(mPath, mPaint);
invalidate();
return true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(600,400);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBgBitmap, 0, 0, null);
canvas.drawBitmap(mFgBitmap, 0, 0, null);
}
效果圖如下所示:
在使用PorterDuffXfermode時還有一點需要注意找前,那就是最好在繪圖時,將硬件加速關閉判族,因為有些模式并不支持硬件加速躺盛。
Shader
Shader又被稱之為著色器、渲染器形帮,它用來實現(xiàn)一系列的漸變槽惫、渲染效果。Android中的Shader包括以下幾種辩撑。
BitmapShader——位圖Shader
LinearGradient——線性Shader
RadialGradient——光束Shader
SweepGradient——梯度Shader
ComposeGradient——混合Shader
- BitmapShader
與其他的Shader所產(chǎn)生的漸變不同界斜,BitmapShader產(chǎn)生的是一個圖像,這有點像Photoshop中的圖像填充漸變合冀。它的作用就是通過Paint對畫布進行指定Bitmap的填充各薇,填充時有以下幾種模式可以選擇。
CLAMP拉伸——拉伸的是圖片最后的那一個像素君躺,不斷重復
REPEAT重復——橫向峭判、縱向不斷重復
MIRROR鏡像——橫向不斷翻轉重復,縱向不斷翻轉重復
這里最常用的就是CLAMP拉伸模式棕叫,雖然它會拉伸最后一個像素林螃,但是只要將圖像設置為一定的大小,就可以避免這種拉伸谍珊。下面將一個矩形的圖片變成一張圓形的圖片治宣,效果如下:
代碼如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint();
//用一張圖片創(chuàng)建一支具有圖像填充功能的畫筆,并使用這只畫筆繪制一個圓形
paint.setShader(bitmapShader);
canvas.drawCircle(500, 250, 200, paint);
}
如果把TileMode改為REPEAT砌滞,并將Bitmap改為較小的ic_launcher圖標侮邀,就可以看到幾種模式的區(qū)別了,代碼如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
Paint paint = new Paint();
paint.setShader(bitmapShader);
canvas.drawCircle(500, 250, 200, paint);
}
運行程序贝润,效果圖如下:
- LinearGradient
要使用LinearGradient非常簡單绊茧,只需要指定漸變的起始顏色即可,代碼如下:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setShader(new LinearGradient(0, 0, 400, 400, Color.BLUE, Color.YELLOW, Shader.TileMode.REPEAT));
canvas.drawRect(0, 0, 400, 400, paint);
}
效果圖如下圖打掘,他是一個從(0, 0)到(400, 400)的由藍色到黃色的漸變效果华畏。
LinearGradient方法參數(shù)中的TileMode與在BitmapShader中的含義基本相同,這里將繪制矩形的大小設置為與漸變圖像大小相同尊蚁,所以沒有看見REPEAT的效果亡笑。如果將圖形擴大,REPEAT的效果就出來了横朋,如下圖所示:
倒影效果
這些漸變效果通常不會直接使用在程序里仑乌。通常情況下,把這種漸變效果作為一個遮罩層來使用,同時結合PorterDuffXfermode晰甚。這樣處理后衙传,遮罩層就不再是一個生硬的圖形,而是一個具有漸變效果的圖層厕九。這樣處理的效果會更加柔和蓖捶、更加自然。下面用LinearGradient和PorterDuffXfermode來創(chuàng)建一個具有倒影效果的圖片扁远,效果圖如下:
代碼如下:
public class ReflectView extends View {
private Bitmap mSrcBitmap, mRefBitmap;
private Paint mPaint;
private PorterDuffXfermode mXfermode;
public ReflectView(Context context) {
super(context);
initRes(context);
}
public ReflectView(Context context, AttributeSet attrs) {
super(context, attrs);
initRes(context);
}
public ReflectView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initRes(context);
}
private void initRes(Context context) {
mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
Matrix matrix = new Matrix();
//實現(xiàn)圖片的垂直翻轉俊鱼,避免使用旋轉變換的復雜計算
matrix.setScale(1, -1);
mRefBitmap = Bitmap.createBitmap(mSrcBitmap, 0, 0, mSrcBitmap.getWidth(),
mSrcBitmap.getHeight(), matrix, true);
mPaint = new Paint();
mPaint.setShader(new LinearGradient(0, mSrcBitmap.getHeight(), 0,
mSrcBitmap.getHeight() + mSrcBitmap.getHeight() / 2, 0XDD000000, 0X10000000,
Shader.TileMode.CLAMP));
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
//繪制原圖
canvas.drawBitmap(mSrcBitmap, 0, 0, null);
//繪制倒影圖
canvas.drawBitmap(mRefBitmap, 0, mSrcBitmap.getHeight(), null);
mPaint.setXfermode(mXfermode);
//繪制漸變效果矩形
canvas.drawRect(0, mSrcBitmap.getHeight(), mSrcBitmap.getWidth(), mSrcBitmap.getHeight() * 2, mPaint);
mPaint.setXfermode(null);
}
}
PathEffect
PathEffect就是指用用各種筆觸效果來繪制路徑。Android系統(tǒng)提供了如下中展示的幾種繪制PathEffect的方式穿香,從上到下依次是:
沒效果
CornerPathEffect——就是將拐角處變得圓滑亭引,具體圓滑的程度绎速,則由參數(shù)決定
DiscretePathEffect——使用這個效果之后皮获,線段上就會產(chǎn)生許多雜點
DashPathEffect——這個效果可以用來繪制虛線,用一個數(shù)組來設置各個點之間的間隔纹冤。此后繪制虛線時就重復這樣的間隔進行繪制洒宝,另一個參數(shù)phase則用來控制繪制時數(shù)組的一個偏移量,通趁染可以通過設置值來實現(xiàn)路徑的動態(tài)效果雁歌。
PathDashPathEffect——這個效果與DashPathEffect類似,只不過它的功能更加強大知残,可以設置顯示點的圖形靠瞎,即方形點的虛線、圓形點的虛線求妹。
ComposePathEffect——通過ComposePathEffect來組合PathEffect乏盐,就是將任意兩種路徑特性組合起來形成一個新的效果。
上圖實現(xiàn)代碼如下:
public class PathEffectView extends View {
private Paint mPaint;
private Path mPath;
private PathEffect[] mEffects;
public PathEffectView(Context context) {
super(context);
init();
}
public PathEffectView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PathEffectView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.DKGRAY);
//使用隨機數(shù)來生成一些隨機的點形成一條路徑
mPath = new Path();
mPath.moveTo(0, 0);
for (int i = 0; i < 30; i++) {
mPath.lineTo(i * 35, (float) (Math.random() * 100));
}
mEffects = new PathEffect[6];
mEffects[0] = null;
mEffects[1] = new CornerPathEffect(30);
mEffects[2] = new DiscretePathEffect(3.0f, 5.0f);
mEffects[3] = new DashPathEffect(new float[]{20, 10, 5, 10}, 0);
Path path = new Path();
path.addRect(0, 0, 8, 8, Path.Direction.CCW);
mEffects[4] = new PathDashPathEffect(path, 12, 0, PathDashPathEffect.Style.ROTATE);
mEffects[5] = new ComposePathEffect(mEffects[3], mEffects[1]);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < mEffects.length; i++) {
mPaint.setPathEffect(mEffects[i]);
canvas.drawPath(mPath, mPaint);
canvas.translate(0, 200);
}
}
}