自定义View实现擦除蒙版显示图片功能
1.首先找到一张图片,利用BitmapFactory.decodeResource()方法得到它,作为画布的最底层将其绘制到画布的最底层
canvas.drawBitmap(mBitmapBackground,new Rect(0,0,mBitmapBackground.getWidth(),mBitmapBackground.getHeight()),
new Rect(0,0,width,heigth),null);//将第一个Rect的图片作为来源,来填充第二个Rect的区域
2.创建一个和画布大小一模一样的Bitmap,但里面什么都没有,然后以这个Bitmap为新的画布创建一个新的画布
//创建一个位图
mBitmap=Bitmap.createBitmap(width,heigth, Bitmap.Config.ARGB_8888);
mcanvasBit=new Canvas(mBitmap);//创建一个新的画布,是基于Bitmap即把Bitmap当做一个画布
//在Ondraw方法中
canvas.drawBitmap(mBitmap,0,0,null);
3.在Bitmap的画布上画上一层蒙版
mcanvasBit.drawRect(0, 0, width, heigth, mPaintCircle);//在Bitmap上画一个矩形。相当于一个蒙版//mPaintCircle只需要设置颜色即可
4.在Bitmap的画布上绘制一个路径,该路径代表用手点击屏幕,并在屏幕上移动的位置,该路径的画笔一定要设置成为XOR模型,同时还需要设置宽度(因为绘制的路径是线性的路径在第5部里)同时需要设置起始位置的形状(即点击屏幕不动时系统默认的形状,在这里设置为圆形)设置沿路径填充的形状(即手在屏幕上移动时,会不停的绘制该形状用来填充移动过的区域在这里设置为圆形,这样比方形好看的多)最后将画笔Style设置为FILL_AND_STROKE(填充和描边)
mPaintRect.setStrokeJoin(Paint.Join.ROUND);//起始位置的形状,设置为圆形
mPaintRect.setStrokeCap(Paint.Cap.ROUND);//绘制中间时使用的形状
mPaintRect.setStrokeWidth(60);
mPaintRect.setStyle(Paint.Style.FILL_AND_STROKE);//将格式设置为填充并且空心
//PorterDuff.Mode为枚举类,一共有16个枚举值:每个枚举值代表不同的model
PorterDuffXfermode mode=new PorterDuffXfermode(PorterDuff.Mode.XOR);
mPaintRect.setXfermode(mode);
5.确定移动的位置和移动距离需要在onTouchEvent上面case:ACTION_DOWN和ACTION_MOVE,当ACTION_DOWN时需要记录当前这个点的位置,然后保存
当ACTION_MOVE时首先需要找到上个点的位置,然后得到最新点的位置,然后用mPath.lineTo来得到该路径,最后重新保存这个最新的点,这样就完成了不断更新触摸位置,同时能够记录该路径,最后调用 mcanvasBit.drawPath(mPath,mPaintRect);画出该路径
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
X=event.getX();//得到当前点击的位置
Y=event.getY();
mPath.moveTo(X,Y);//将该位置设置为Path的起点
XOld=X;//将该位置保存
YOld=Y;
invalidate();
return true;//必须要return true,否则不做处理
case MotionEvent.ACTION_MOVE:
//得到移动到的位置
X=event.getX();
Y=event.getY();
mPath.moveTo(XOld,YOld);//首先移动到之前的那个点
mPath.lineTo(X,Y);//然后移动到新的位置
invalidate();
//重新保存这个点
XOld=X;
YOld=Y;
return true;
}
return super.onTouchEvent(event);
}
自定义View的存储
1.首先在Activity中找到这个自定义的View,然后设置绘画缓存为true
2.创建一个Bitmap然后得到绘画的缓存
mBitPicture.setDrawingCacheEnabled(true);//设置自定义View可以当做缓存
Bitmap bit=mBitPicture.getDrawingCache(true);//创建一个Bitmap,得到自定义View的缓存
3.最后创建一个File将Bitmap写入该File文件里就能保存自定义View的图片了
//第一个参数设置文件压缩格式
//第二个参数设置文件压缩尺寸大小(0~100),PNG格式图片可以忽略这个设置
//第三个参数是输出的位置
bit.compress(Bitmap.CompressFormat.PNG,100,new FileOutputStream(file));//设置为Png格式
自定义View的属性
1.首先在res的values文件夹下创建一个xml文件,在文件里面创建declare-styleable,表示自定义的属性,然后创建属性,包括属性的name和format,其中format表示接受的数据类型,xml文件如下:
<declare-styleable name="customView">
<attr name="change_picture" format="reference"></attr>
<attr name="change_paint" format="dimension|reference"></attr>
</declare-styleable>
2.在布局xml文件下声明已经创建的属性集合,在Androidstudio中只需要在后面加上res-auto就可以自己去寻找了,但是在eclipse中需要加上包名
xmlns:customview="http://schemas.android.com/apk/res-auto"
3.声明之后就可以在自己创建的View上添加属性了
customview:change_picture="@mipmap/ali"
customview:change_paint="50sp"
4.在View类中找到该属性
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.customView);
BitmapDrawable drawable= (BitmapDrawable) a.getDrawable(R.styleable.customView_change_picture);
if (drawable!=null){
mBitmapBackground=drawable.getBitmap();
}else {
mBitmapBackground= BitmapFactory.decodeResource(getResources(), R.mipmap.cluo);//为Bitmap设置来源
}
int paintWidth=a.getDimensionPixelOffset(R.styleable.customView_change_paint,30);
所有代码
public class BitmapPicture extends View {
private int width;
private int heigth;
private float X;
private float Y;
private float XOld;
private float YOld;
private Path mPath;
private Bitmap mBitmap;
private Bitmap mBitmapBackground;
private Paint mPaintBackground;
private Paint mPaintCircle;
private Paint mPaintRect;
private Canvas mcanvasBit;
public BitmapPicture(Context context) {
super(context);
}
public BitmapPicture(Context context, AttributeSet attrs) {
super(context, attrs);
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.customView);
BitmapDrawable drawable= (BitmapDrawable) a.getDrawable(R.styleable.customView_change_picture);//找到图片并强制造型成为BitmapDrawable
if (drawable!=null){
mBitmapBackground=drawable.getBitmap();
}else {
mBitmapBackground= BitmapFactory.decodeResource(getResources(), R.mipmap.cluo);//为Bitmap设置来源
}
int paintWidth=a.getDimensionPixelOffset(R.styleable.customView_change_paint,30);//得到xml文档中chang_paint属性的值,第二个参数是默认值
mPath=new Path();
mPaintBackground=new Paint();
mPaintBackground.setColor(Color.GRAY);
mPaintCircle=new Paint();
mPaintCircle.setColor(Color.CYAN);
mPaintCircle.setAntiAlias(true);
mPaintRect=new Paint();
mPaintRect.setColor(Color.GRAY);
mPaintRect.setAntiAlias(true);//抗锯齿
mPaintRect.setStrokeJoin(Paint.Join.ROUND);//起始位置的形状,设置为圆形//设置画笔进入的形状,当画笔的Style设置为Stroke或者FillAndStroke时使用
mPaintRect.setStrokeCap(Paint.Cap.ROUND);//绘制中间时使用的形状//设置笔帽的形式,当画笔的Style设置为Stroke或者FillAndStroke时使用
mPaintRect.setStrokeWidth(paintWidth);
mPaintRect.setStyle(Paint.Style.FILL_AND_STROKE);//将格式设置为填充并且空心
//PorterDuff.Mode为枚举类,一共有16个枚举值:每个枚举值代表不同的model
PorterDuffXfermode mode=new PorterDuffXfermode(PorterDuff.Mode.XOR);
mPaintRect.setXfermode(mode);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width=getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
heigth=getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width,heigth);
//创建一个位图
mBitmap=Bitmap.createBitmap(width,heigth, Bitmap.Config.ARGB_8888);
mcanvasBit=new Canvas(mBitmap);//创建一个新的画布,是基于Bitmap即把Bitmap当做一个画布
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// canvas.drawColor(Color.GRAY);//设置一个背景色
// canvas.drawBitmap(mBitmap,0,0,mPaintBackground);//将Bitmap画到画布上
canvas.drawBitmap(mBitmapBackground,new Rect(0,0,mBitmapBackground.getWidth(),mBitmapBackground.getHeight()),
new Rect(0,0,width,heigth),null);//将第一个Rect的图片作为来源,来填充第二个Rect的区域
canvas.drawBitmap(mBitmap,0,0,null);
// mcanvasBit.drawCircle(width/2,heigth/2,width/2,mPaintCircle);//Bitmap上画一个圆
mcanvasBit.drawRect(0, 0, width, heigth, mPaintCircle);//在Bitmap上画一个矩形。相当于一个蒙版
mcanvasBit.drawPath(mPath,mPaintRect);//画出路径,经路径上的设置为透明
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
X=event.getX();//得到当前点击的位置
Y=event.getY();
mPath.moveTo(X,Y);//将该位置设置为Path的起点
XOld=X;//将该位置保存
YOld=Y;
invalidate();
return true;//必须要return true,否则不做处理
case MotionEvent.ACTION_MOVE:
//得到移动到的位置
X=event.getX();
Y=event.getY();
mPath.moveTo(XOld,YOld);//首先移动到之前的那个点
mPath.lineTo(X,Y);//然后移动到新的位置
invalidate();
//重新保存这个点
XOld=X;
YOld=Y;
return true;
}
return super.onTouchEvent(event);
}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:customview="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.my.mywidget.widget.BitmapPicture
android:id="@+id/bitmap_picture"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
customview:change_picture="@mipmap/ali"
customview:change_paint="50sp"
/>
<Button
android:id="@+id/button_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存"/>
</LinearLayout>
Acitvity
public class BitmapPictureActivity extends AppCompatActivity {
private Button mBtnSave;
private BitmapPicture mBitPicture;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.btimap_picture);
mBitPicture= (BitmapPicture) findViewById(R.id.bitmap_picture);
mBtnSave= (Button) findViewById(R.id.button_save);
mBtnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(BitmapPictureActivity.this, "成功保存图片", Toast.LENGTH_SHORT).show();
// mBitPicture.setBackgroundResource(R.mipmap.aixi);
//这两句话是将自定义View设置为可以保存
mBitPicture.setDrawingCacheEnabled(true);//设置自定义View可以当做缓存
Bitmap bit=mBitPicture.getDrawingCache(true);//创建一个Bitmap,得到自定义View的缓存
File file=new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis()+".png");//文件保存路径
if (!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
//第一个参数设置文件压缩格式
//第二个参数设置文件压缩尺寸大小(0~100),PNG格式图片可以忽略这个设置
//第三个参数是输出的位置
bit.compress(Bitmap.CompressFormat.PNG,100,new FileOutputStream(file));//设置为Png格式
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
});
}
}
customView
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="customView">
<attr name="change_picture" format="reference"></attr>
<attr name="change_paint" format="dimension|reference"></attr>
</declare-styleable>
</resources>