編輯:關於Android編程
除了常用的畫筆屬性,比如普通的畫筆(Paint),帶邊框、填充的style,顏色(Color),寬度(StrokeWidth),抗鋸齒(ANTI_ALIAS_FLAG)等,Android還提供了各種各樣專業的畫筆工具,如記號筆、毛筆、蠟筆等,使用它們可以實現更加豐富的效果。
下圖中列舉了16種PorterDuffXfermode,有點像數學中集合的交集、並集這樣的概念,它控制的是兩個圖像間的混合顯示模式。
vcq9o6xkc3TKx8/Iu621xM280M6jrLb4c3Jjyse687uttcTNvNDOoaM8L3A+DQo8cD7V4tCpxKPKvdKysrvKx76ts6PKudPDo6zTw7XE1+624LXEysejrMq508PSu9XFzbzGrNf3zqrB7dK71cXNvMastcTV2tXWsuOjrM2ouf2/2NbG1drV1rLjtcTNvNDOo6zAtL/Y1sbPwsPmsbvV2tXWzbzQzrXEz9TKvtCnufuho8bk1tDX7rOj08O1xL7NysfNqLn9RFNUX0lOoaJTUkNfSU7Eo8q9wLTKtc/WvavSu7j2vtjQzs28xqyx5LPJ1LK9x828xqy78tXf1LLQzs28xqy1xNCnufuhozwvcD4NCjxoNCBpZD0="圓角矩形">圓角矩形
要使用PorterDuffXfermode非常簡單,只需要讓畫面擁有這個屬性就可以了。比如要實現下面圓角矩形的效果:
先用一個普通畫筆畫一個Mask遮罩層,再用帶PorterDuffXfermode的畫筆將圖像畫在遮罩層上,這樣就可以通過上面所說的效果來混合兩個圖像了,代碼如下:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test1);
mBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mBitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawRoundRect(0, 0, bitmap.getWidth(), bitmap.getHeight(), 80, 80, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, 0, 0, paint);
若要實現刮刮卡效果(刮刮卡一般有兩個圖層,即上面的用來被刮掉的圖層和下面隱藏的圖層)。在初始狀態下,上面的圖層會將下面整個圖層覆蓋,當你用手刮上面的圖層的時候,下面的圖層會慢慢顯示出來,類似於畫圖工具中的橡皮擦效果。
首先要做一些初始化工作,例如准備好圖片,設置好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);
獲取用戶手指滑動所產生的路徑並將使用DST_IN模式將路徑繪制到前面覆蓋的圖層上,代碼如下所示。使用Path保存用戶手指劃過的痕跡。當然,如果使用貝塞爾曲線來做優化則會得到更好的顯示效果,這裡為了簡化功能,就不使用貝塞爾曲線了。
@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 onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBgBitmap, 0, 0, null);
canvas.drawBitmap(mFgBitmap, 0, 0, null);
}
效果圖如下所示:
在使用PorterDuffXfermode時還有一點需要注意,那就是最好在繪圖時,將硬件加速關閉,因為有些模式並不支持硬件加速。
Shader又被稱之為著色器、渲染器,它用來實現一系列的漸變、渲染效果。Android中的Shader包括以下幾種。
BitmapShader——位圖Shader LinearGradient——線性Shader RadialGradient——光束Shader SweepGradient——梯度Shader ComposeGradient——混合Shader與其他的Shader所產生的漸變不同,BitmapShader產生的是一個圖像,這有點像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();
//用一張圖片創建一支具有圖像填充功能的畫筆,並使用這只畫筆繪制一個圓形
paint.setShader(bitmapShader);
canvas.drawCircle(500, 250, 200, paint);
}
如果把TileMode改為REPEAT,並將Bitmap改為較小的ic_launcher圖標,就可以看到幾種模式的區別了,代碼如下:
@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非常簡單,只需要指定漸變的起始顏色即可,代碼如下:
@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方法參數中的TileMode與在BitmapShader中的含義基本相同,這裡將繪制矩形的大小設置為與漸變圖像大小相同,所以沒有看見REPEAT的效果。如果將圖形擴大,REPEAT的效果就出來了,如下圖所示:
這些漸變效果通常不會直接使用在程序裡。通常情況下,把這種漸變效果作為一個遮罩層來使用,同時結合PorterDuffXfermode。這樣處理後,遮罩層就不再是一個生硬的圖形,而是一個具有漸變效果的圖層。這樣處理的效果會更加柔和、更加自然。下面用LinearGradient和PorterDuffXfermode來創建一個具有倒影效果的圖片,效果圖如下:
代碼如下:
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();
//實現圖片的垂直翻轉,避免使用旋轉變換的復雜計算
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就是指用用各種筆觸效果來繪制路徑。Android系統提供了如下中展示的幾種繪制PathEffect的方式,從上到下依次是:
沒效果 CornerPathEffect——就是將拐角處變得圓滑,具體圓滑的程度,則由參數決定 DiscretePathEffect——使用這個效果之後,線段上就會產生許多雜點 DashPathEffect——這個效果可以用來繪制虛線,用一個數組來設置各個點之間的間隔。此後繪制虛線時就重復這樣的間隔進行繪制,另一個參數phase則用來控制繪制時數組的一個偏移量,通常可以通過設置值來實現路徑的動態效果。 PathDashPathEffect——這個效果與DashPathEffect類似,只不過它的功能更加強大,可以設置顯示點的圖形,即方形點的虛線、圓形點的虛線。 ComposePathEffect——通過ComposePathEffect來組合PathEffect,就是將任意兩種路徑特性組合起來形成一個新的效果。上圖實現代碼如下:
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);
//使用隨機數來生成一些隨機的點形成一條路徑
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);
}
}
}
分頁大家都會用Android的TabHost和TabActivity的組合,今天我這裡實現的是GridView和ActivityGroup實現的分頁,這裡需要將Activ
閒來無事,做了一個簡單的抽獎轉盤的ui實現,供大家參考package com.microchange.lucky; import android.content.Con
寫在前面的話:接觸Android的時間也不短了,聽了視頻、看了書、敲了代碼,寫了博客,做了demo。。。但是想做出一款優秀的APP(哪怕是封裝一個不錯的功能)還有很長的路
Google在2015的IO大會上,給我們帶來了更加詳細的Material Design設計規范,同時,也給我們帶來了全新的Android Design Support