除了常用的画笔属性,比如普通的画笔(Paint),带边框、填充的style,颜色(Color),宽度(StrokeWidth),抗锯齿(ANTI_ALIAS_FLAG)等,Android还提供了各种各样专业的画笔工具,如记号笔、毛笔、蜡笔等,使用它们可以实现更加丰富的效果。
先看一张非常经典的图,出自API Demo,基本上所有讲porterDuffXfermode的文章都会使用这张图片做说明。
图中列举了16种PorterDuffXfermode,有点像数学中集合的交集、并集这样的概念,它控制的是两个图像间的混合显示模式。
PorterDuffXfermode设置的是两个图层交集区域的显示方式,dst是先画的图形,而src是后画的图形。
这些模式也不是经常使用,用的最多的是,使用一张图片作为另一张图片的遮罩层,通过控制遮罩层的图形,来控制下面被遮罩图形的显示效果。其中最常用的就是通过DST_IN、SRC_IN模式来实现将一个矩形图片变成圆角图片或者圆形图片的效果。
简单示例(圆角图片):界面代码省略(就是一个ImageView组件)...
思路:先用普通画笔画一个圆角矩形(专业一点就叫Mask遮罩层),再用带PorterDuffXfermode的画笔将图像画在圆角矩形上。
package com.example.androidpaint; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.os.Bundle; import android.widget.ImageView; public class SimpleCircleImageActivity extends Activity { private ImageView imageView; private Bitmap bitmap; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simplecircleimage); imageView = (ImageView) findViewById(R.id.imageView); imageView.setImageBitmap(getBitmap()); } private Bitmap getBitmap(){ bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.we); Bitmap out = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(out); Paint paint = new Paint(); RectF rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()); canvas.drawRoundRect(rect, 80, 80, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(bitmap, 0, 0, paint); return out; } }效果图:
刮刮卡效果:
思路:先使用普通画笔绘制一个图片,再绘制一个同样大小的灰色在图片上,使用Path保存手指划过的路径,使用带PorterDuffXfermode的画笔画出路径并设置画笔透明度为0.
主要代码:
package com.mfc.myview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import com.example.androidpaint.R; public class XfermodeView extends View { private Bitmap mBgBitmap,mFgBitmap; private Paint mPaint; private Canvas mCanvas; private Path mPath; public XfermodeView(Context context) { super(context); init(); } public XfermodeView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } //初始化界面样式 private void init(){ mPaint = new Paint(); mPath = new Path(); mPaint.setAlpha(0); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND); mPaint.setStrokeWidth(50); mPaint.setStrokeCap(Paint.Cap.ROUND); mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.we); mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mFgBitmap); mCanvas.drawColor(Color.GRAY); } @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; } mCanvas.drawPath(mPath, mPaint); invalidate(); return true; } @Override protected void onDraw(Canvas canvas) { canvas.drawBitmap(mBgBitmap, 0, 0, null); canvas.drawBitmap(mFgBitmap, 0, 0, null); } } 效果图:
Shader又被称之为着色器、渲染器,它用来实现一系列的渐变、渲染效果。android中的Shader包括以下几种。
BitmapShader——位图ShaderLinearGradient——线性ShaderRadialGradient——光束ShaderSweepGradient——梯度ShaderComposeGradient——混合Shader与其他的Shader所产生的渐变不同,BitmapShader产生的是一个图像,这有点像Photoshop中的图像填充渐变。它的作用就是通过Paint对画布进行指定Bitmap的填充,填充时有以下几种模式可以选择。
CLAMP拉伸——拉伸的是图片最后的那一个像素,不断重复REPEAT重复——横向、纵向不断重复MIRROR镜像——横向不断翻转重复,纵向不断翻转重复 最常用的就是CLAMP拉伸模式,虽然它会拉伸最后一个像素,但是只要将图像设置为一定的大小,就可以避免这种拉伸。下面将一个矩形的图片变成一张圆形的图片。主要代码:
package com.mfc.myview; import com.example.androidpaint.R; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Shader; import android.util.AttributeSet; import android.view.View; public class BitmapShaderView extends View { private Bitmap bitmap; private BitmapShader bitmapShader; private Paint paint; public BitmapShaderView(Context context, AttributeSet attrs) { super(context, attrs); //这里只需更换初始化方法测试几种模式的效果 initClamp(); } private void initClamp(){ bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.we); bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); paint = new Paint(); } private void initRepeat(){ bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); paint = new Paint(); } private void initMiror(){ bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher); bitmapShader = new BitmapShader(bitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR); paint = new Paint(); } protected void onDraw(Canvas canvas) { super.onDraw(canvas); paint.setShader(bitmapShader); canvas.drawCircle(300, 300, 200, paint); } }效果图:
其他几种Shader:
主要代码:
package com.mfc.myview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.SweepGradient; import android.util.AttributeSet; import android.view.View; public class LinearGirdientView extends View { private Paint paint; public LinearGirdientView(Context context, AttributeSet attrs) { super(context, attrs); paint = new Paint(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //线性shader //paint.setShader(new LinearGradient(0, 0, 400, 400, Color.BLUE, Color.YELLOW, Shader.TileMode.REPEAT)); //光束shader //paint.setShader(new RadialGradient(0, 0, 500, Color.BLUE, Color.YELLOW, Shader.TileMode.REPEAT)); //梯度shader paint.setShader(new SweepGradient(0, 0, Color.BLUE, Color.YELLOW)); //混合shader //paint.setShader(new ComposeShader(shaderA, shaderB, mode)) canvas.drawRect(0, 0, 400, 400, paint); } } 效果图:
这些渐变效果通常不会直接使用在程序里。通常情况下,把这种渐变效果作为一个遮罩层来使用,同时结合PorterDuffXfermode。这样处理后,遮罩层就不再是一个生硬的图形,而是一个具有渐变效果的图层。这样处理的效果会更加柔和、更加自然。下面用LinearGradient和PorterDuffXfermode来创建一个具有倒影效果的图片。
主要代码:
package com.mfc.myview; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Shader; import android.util.AttributeSet; import android.view.View; import com.example.androidpaint.R; public class ReflectView extends View { public ReflectView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.we); Paint paint = new Paint(); Matrix matrix = new Matrix(); // 将图片垂直翻转 matrix.setScale(1F, -1F); Bitmap bmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); canvas.drawBitmap(bitmap, 0, 0, null); canvas.drawBitmap(bmp, 0, bitmap.getHeight(), null); paint.setShader(new LinearGradient(0, bitmap.getHeight(), 0, bitmap .getHeight() + bitmap.getHeight() / 4, 0XDD000000, 0X10000000, Shader.TileMode.CLAMP)); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); canvas.drawRect(0, bitmap.getHeight(), bitmap.getWidth(), bitmap.getHeight() * 2, paint); } }效果图:
效果图:
以上图形对应下面的几种情况:
没效果CornerPathEffect——就是将拐角处变得圆滑,具体圆滑的程度,则由参数决定DiscretePathEffect——使用这个效果之后,线段上就会产生许多杂点DashPathEffect——这个效果可以用来绘制虚线,用一个数组来设置各个点之间的间隔。此后绘制虚线时就重复这样的间隔进行绘制,另一个参数phase则用来控制绘制时数组的一个偏移量,通常可以通过设置值来实现路径的动态效果。PathDashPathEffect——这个效果与DashPathEffect类似,只不过它的功能更加强大,可以设置显示点的图形,即方形点的虚线、圆形点的虚线。ComposePathEffect——通过ComposePathEffect来组合PathEffect,就是将任意两种路径特性组合起来形成一个新的效果。 主要代码: package com.mfc.myview; import android.content.Context; import android.graphics.Canvas; import android.graphics.ComposePathEffect; import android.graphics.CornerPathEffect; import android.graphics.DashPathEffect; import android.graphics.DiscretePathEffect; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.PathDashPathEffect; import android.graphics.PathEffect; import android.util.AttributeSet; import android.view.View; public class PathEffectView extends View { private Path path; private Paint paint; public PathEffectView(Context context, AttributeSet attrs) { super(context, attrs); path = new Path(); paint = new Paint(); path.moveTo(0, 0); for (int i = 0; i <= 30; i++) { path.lineTo(i*35, (float) (Math.random()*100)); } } @Override protected void onDraw(Canvas canvas) { paint.setStyle(Style.STROKE); paint.setStrokeWidth(5); PathEffect[] effects = new PathEffect[6]; effects[0] = null; //拐角处变得圆滑,参数决定圆滑程度 effects[1] = new CornerPathEffect(30); //线段上会多出很多杂点 effects[2] = new DiscretePathEffect(3.0F, 5.0F); //绘制虚线,数据设置各个点之间的间隔,第二个参数是偏移量,用来设置动态效果 effects[3] = new DashPathEffect(new float[]{20,10,5,10}, 0); Path path2 = new Path(); path2.addRect(0, 0, 8, 8, Path.Direction.CCW); effects[4] = new PathDashPathEffect(path2, 12, 0, PathDashPathEffect.Style.ROTATE); effects[5] = new ComposePathEffect(effects[3], effects[1]); for (int i = 0; i < effects.length; i++) { paint.setPathEffect(effects[i]); canvas.drawPath(path, paint); canvas.translate(0, 200); } } } 代码下载: http://download.csdn.net/detail/fancheng614/9922550