Xfermode和PorterDuff详解、自定义View的属性、涂鸦和悬浮球绘制

Xfermode

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

PorterDuff.Mode类为枚举类,下边有16个枚举值,分别表示不同的效果!
下边是一张效果图!(图片来自网络)
Xfermode和PorterDuff详解、自定义View的属性、涂鸦和悬浮球绘制_第1张图片
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时,我们需要更多的属性,这时就需要我们自定义我们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);
            }
        });
    }
}

运行效果:点击开始测试按钮,内存进度开始增加:

你可能感兴趣的:(涂鸦,xfermode,PorterDuff,悬浮加速球)