Xfermode下边有三个子类 :
1. AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。
2. PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素异或操作。
3. PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有Canvas图像进行交互。
我们在上一篇博客中也提到,如果要使用这些转换模式,就要先得到这三个子类的对象mode,然后调用setXfermode(),传入mode即可。
PorterDuffXfermode mode=new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP);
paint.setXfermode(mode);
PorterDuff.Mode类为枚举类,下边有16个枚举值,分别表示不同的效果!
下边是一张效果图!(图片来自网络)
1. PorterDuff.Mode.CLEAR
所绘制不会提交到画布上。
2. PorterDuff.Mode.SRC
显示上层绘制图片
3. PorterDuff.Mode.DST
显示下层绘制图片
4. PorterDuff.Mode.SRC_OVER
正常绘制显示,上下层绘制叠盖。
5. PorterDuff.Mode.DST_OVER
上下层都显示。下层居上显示。
6. PorterDuff.Mode.SRC_IN
取两层绘制交集。显示上层。
7. PorterDuff.Mode.DST_IN
取两层绘制交集。显示下层。
8. PorterDuff.Mode.SRC_OUT
取上层绘制非交集部分。
9. PorterDuff.Mode.DST_OUT
取下层绘制非交集部分。
10. PorterDuff.Mode.SRC_ATOP
取下层非交集部分与上层交集部分
11. PorterDuff.Mode.DST_ATOP
取上层非交集部分与下层交集部分
12. PorterDuff.Mode.XOR
异或:去除两图层交集部分
13. PorterDuff.Mode.DARKEN
取两图层全部区域,交集部分颜色加深
14. PorterDuff.Mode.LIGHTEN
取两图层全部,点亮交集部分颜色
15. PorterDuff.Mode.MULTIPLY
取两图层交集部分叠加后颜色
16. PorterDuff.Mode.SCREEN
取两图层全部区域,交集部分变为透明色
有时在自定义View时,我们需要更多的属性,这时就需要我们自定义我们View的属性。一般步骤:
①在res->values->路径下新建attrs.xml ,这里仅以画笔尺寸为例,写了一个属性。
注意:styleable标签的name,可以随意起一个名字,标签下attr name表示属性的名字,format表示该属性的取值类型。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyBitmapView">
<attr name="paintsize" format="dimension"></attr>
</declare-styleable>
</resources>
②引入到我们的布局,注意:后边apk/res/后边必须跟上完整的包名,但是如果我们用的Android Studio,直接写apk/res-auto即可。
xmlns:mybitmapview="http://schemas.android.com/apk/res/com.example.administrator.mydiywidget.MyBitmapView"
<com.example.administrator.mydiywidget.MyBitmapView android:id="@+id/mybitmapview" android:layout_width="match_parent" android:layout_height="match_parent" mybitmapview:paintsize="50dp"/>
③然后在我们自定义View的构造器中得到我们的属性
TypedArray array=context.obtainStyledAttributes(attrs,R.styleable.MyBitmapView);
int paintWidth=array.getDimensionPixelSize(R.styleable.MyBitmapView_paintsize,30);
paint_touch.setStrokeWidth(paintWidth);
下面我们就上边的讲解,写一个涂鸦效果的展示,然后将涂鸦的图片保存到手机内存卡里边:
这里我们对画笔又做了一些属性上的控制:
1. setStrokeJoin(Join join)设置结合处的样子 Miter:结合处为锐角, Round:结合处为圆弧:BEVEL:结合处为直线。
2. setStrokeCap(Paint.Cap.ROUND);设置画笔的形状
3. setPathEffect(new CornerPathEffect(360));
是用来控制绘制轮廓(线条)的方式,一般new一个具体的子类传入。
CornerPathEffect:这个类的作用就是将Path的各个连接线段之间的夹角用一种更平滑的方式连接,类似于圆弧与切线的效果。
DiscretePathEffect:这个类的作用是打散Path的线段,使得在原来路径的基础上发生打散效果。
PathDashPathEffect:这个类的作用是使用Path图形来填充当前的路径。
自定义属性:attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyBitmapView2">
<attr name="paintsize" format="dimension"></attr>
</declare-styleable>
</resources>
创建MyBitmapView继承View
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/** * Created by Administrator on 2015/9/17. */
public class MyBitmapView extends View{
private int width;
private int height;
private Bitmap bitmap;
private Bitmap bitmap_background;
private Canvas canvasBitmap;
private Paint paint_touch;
private Paint paint_rect;
private Path path;
private float x_old;
private float y_old;
public MyBitmapView(Context context) {
super(context);
}
public MyBitmapView(Context context, AttributeSet attrs) {
super(context, attrs);
//得到自定义属性
TypedArray array=context.obtainStyledAttributes(attrs,R.styleable.MyBitmapView2);
int paintWidth=array.getDimensionPixelSize(R.styleable.MyBitmapView2_paintsize,30);
paint_touch=new Paint();
paint_touch.setColor(Color.BLUE);
paint_touch.setStrokeWidth(paintWidth);//将画笔设置为自定义大小
paint_touch.setStrokeJoin(Paint.Join.ROUND);
paint_touch.setStrokeCap(Paint.Cap.ROUND);
paint_touch.setStyle(Paint.Style.FILL_AND_STROKE);
paint_touch.setPathEffect(new CornerPathEffect(360));
PorterDuffXfermode mode=new PorterDuffXfermode(PorterDuff.Mode.XOR);
paint_touch.setXfermode(mode);
paint_rect=new Paint();
paint_rect.setColor(Color.GREEN);
bitmap_background= BitmapFactory.decodeResource(getResources(),R.mipmap.meinv);
path=new Path();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
float x1=event.getX();
float y1=event.getY();
path.moveTo(x1, y1);
invalidate();
x_old=x1;
y_old=y1;
return true;
case MotionEvent.ACTION_MOVE:
float x2=event.getX();
float y2=event.getY();
path.moveTo(x_old,y_old);
path.lineTo(x2,y2);
invalidate();
x_old=x2;
y_old=y2;
return true;
}
return super.onTouchEvent(event);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); width=getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec); height=getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec);
setMeasuredDimension(width,height);
bitmap=Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
canvasBitmap=new Canvas(bitmap);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap_background,new Rect(0,0,bitmap_background.getWidth(),bitmap_background.getHeight()),new Rect(0,0,width,height),null);//这里将图片放大跟画布一样大小,当然用Matrix设置比例来画也是可以的。
canvas.drawBitmap(bitmap,0,0,null);
canvasBitmap.drawRect(0,0,width,height,paint_rect);
canvasBitmap.drawPath(path,paint_touch);
}
}
然后加入到我们的布局中
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:mybitmapview="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity">
<Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="生成图片" />
<com.example.administrator.mydiywidget.MyBitmapView android:id="@+id/mybitmapview" android:layout_width="match_parent" android:layout_height="match_parent" mybitmapview:paintsize="50dp"/>
</LinearLayout>
最后是主活动中的代码:
import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/** * Created by Administrator on 2015/9/18. */
public class MainActivity extends Activity{
private MyBitmapView myBitmapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myBitmapView= (MyBitmapView) findViewById(R.id.mybitmapview);
Button button= (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
myBitmapView.setDrawingCacheEnabled(true);
Bitmap bitmap=myBitmapView.getDrawingCache(true);//将一个View转换为bitmap
File file=new File(Environment.getExternalStorageDirectory(),"tuya.jpg");
if (!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try {
//压缩bitmap
bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
});
}
}
运行效果:
点击生成图片,就可以在内存卡文件夹中找到我们涂鸦后的图片了!
下面我们再写一个应用,加深我们对这块内容的认知。
首先绘制悬浮球,创建MyBubble类继承View
import android.content.Context;
import android.graphics.Bitmap;
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.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
/** * Created by Administrator on 2015/9/18. */
public class MyBubble extends View{
private int width;
private int height;
private Paint paint_circle;
private Paint paint_bolang;
private Paint paint_text;
private Bitmap bitmap_background;
private int index=-40;
private int bolangheight;
private static final int NEED_INVALIDATE=0x23;
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case NEED_INVALIDATE:
index++;
if (index>0){
index=-40;
}
invalidate();
handler.sendEmptyMessage(NEED_INVALIDATE);
break;
}
}
};
public int getBolangheight() {
return bolangheight;
}
public void setBolangheight(int bolangheight) {
this.bolangheight = bolangheight;
}
public MyBubble(Context context) {
super(context);
}
public MyBubble(Context context, AttributeSet attrs) {
super(context, attrs);
paint_circle=new Paint();//绘制球
paint_circle.setColor(Color.LTGRAY);
paint_circle.setAntiAlias(true);
paint_bolang=new Paint();//绘制正弦波浪
paint_bolang.setAntiAlias(true);
PorterDuffXfermode mode=new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP);
paint_bolang.setXfermode(mode);
paint_text=new Paint();//绘制文本
paint_text.setTextSize(50);
paint_text.setAntiAlias(true);
paint_text.setTextAlign(Paint.Align.CENTER);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
handler.sendEmptyMessage(NEED_INVALIDATE);
bitmap_background=Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Canvas canvas_bitmap=new Canvas(bitmap_background);
canvas.drawBitmap(bitmap_background,0,0,null);
canvas_bitmap.drawCircle(width/2,height/2,150,paint_circle);
Path path = new Path();
path.reset();
path.moveTo(width/2+150,height/2+150-bolangheight);
path.lineTo(width/2+150,height/2+150);
path.lineTo(index,height/2+150);
path.lineTo(index,height/2+150-bolangheight);
for (int i = 0; i < 50; i++) {
path.rQuadTo(10, 5, 20, 0);
path.rQuadTo(10, -5, 20, 0);
}
path.close();
//随着位置的升高,改变画笔的颜色
if(bolangheight<=210){
paint_bolang.setColor(Color.GREEN);
}else if (210<bolangheight&bolangheight<=270){
paint_bolang.setColor(Color.YELLOW);
}else if (bolangheight>270){
paint_bolang.setColor(Color.RED);
}
canvas_bitmap.drawPath(path, paint_bolang); canvas_bitmap.drawText(bolangheight*100/300+"%",width/2,height/2,paint_text);
}
}
然后加入布局,添加一个按钮,点击开始测试,模拟内存占用不断升高!
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity">
<Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="开始测试"/>
<com.example.administrator.mydiywidget.MyBubble android:id="@+id/mybuddle" android:layout_width="match_parent" android:layout_height="match_parent" />
</LinearLayout>
然后是主活动的代码:
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
/** * Created by Administrator on 2015/9/18. */
public class MainActivity extends Activity{
private MyBubble myBubble;
private int bolangheight;
private static final int DOWNLOAD=0x23;
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case DOWNLOAD:
bolangheight++;
if (bolangheight<=300){
myBubble.setBolangheight(bolangheight);
handler.sendEmptyMessageDelayed(DOWNLOAD,50);
}
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myBubble= (MyBubble) findViewById(R.id.mybuddle);
Button button= (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.sendEmptyMessage(DOWNLOAD);
}
});
}
}
运行效果:点击开始测试按钮,内存进度开始增加: