先说要求:实现如下效果
(由于电脑特别卡,看起来不流畅,在手机上试过很流畅的)
接下来说一下具体做法:
1、layout下的布局文件
</RelativeLayout><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mystyle="http://schemas.android.com/apk/res-auto"//声明自定义属性
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.my.administrator.zdyview.BitmapView2//这个名字BitmapView2要与view类名相同
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="match_parent"
mystyle:myview_background="@mipmap/meinv4"//设置自定义属性
mystyle:myview_paintwidth="30dp" />//设置自定义属性
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffccff"
android:text="保存" />//点击保存按钮
</RelativeLayout>
2、写BitmapView2 类继承View,加构造器,重写onMeasure,onDraw方法,在onDraw方法里面画东西
public class BitmapView2 extends View {
private int width;
private int height;
private Paint mPaintMeng;
private Paint mPaintRect;
private Bitmap mBitmap;
private Canvas mCanvas;
private Bitmap mBitmapImage;
private Matrix matrix;
private Path mPath;
//两个构造器
public BitmapView2(Context context) {
super(context);
}
public BitmapView2(Context context, AttributeSet attrs) {
super(context, attrs);
//下面几行代码是对自定义属性的设置
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.mystyle);//解析xml,解析方式为value文件夹下mystyle
BitmapDrawable dra = (BitmapDrawable) a.getDrawable(R.styleable.mystyle_myview_background);//得到layout下xml文件传的myview_background
if (dra != null) {//如果在xml中传入了图片
mBitmapImage = dra.getBitmap();
} else {//若没有传图片,则在下面设置添加
mBitmapImage = BitmapFactory.decodeResource(getResources(), R.mipmap.me);
}
int paintWidth = a.getDimensionPixelOffset(R.styleable.mystyle_myview_paintwidth, 10);//得到xml中设置的画笔宽度属性,设置默认10
mPaintMeng = new Paint();//画蒙板的画笔
mPaintMeng.setColor(Color.GREEN);
mPaintRect = new Paint();//画bitmap的画笔
PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.XOR);
mPaintRect.setXfermode(mode);
mPaintRect.setStrokeJoin(Paint.Join.ROUND);//设置连接部分样式
mPaintRect.setStrokeCap(Paint.Cap.ROUND);//设置中间部分的样式
mPaintRect.setStrokeWidth(paintWidth);//设置画笔宽度属性
mPaintRect.setStyle(Paint.Style.FILL_AND_STROKE);//去掉锯齿
matrix = new Matrix();//初始化矩阵
mPath = new Path();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//设置Bitmap的大小
mCanvas = new Canvas(mBitmap);//自定义的画布
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
matrix.reset();
matrix.postScale((float) width / mBitmapImage.getWidth(), (float) height / mBitmapImage.getHeight());//放大到整个bitview界面
canvas.drawBitmap(mBitmapImage, matrix, null);//图片
mCanvas.drawRect(0, 0, width, height, mPaintMeng);//蒙板
mCanvas.drawPath(mPath, mPaintRect);//画点击时的圆,画笔为XOR透明
canvas.drawBitmap(mBitmap, 0, 0, null);//相当于底版
}
float x;
float y;
float old_x;
float old_y;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = event.getX();
y = event.getY();
mPath.moveTo(x, y);
invalidate();
old_x = x;
old_y = y;
return true;
case MotionEvent.ACTION_MOVE:
x = event.getX();
y = event.getY();
mPath.moveTo(old_x, old_y);
mPath.lineTo(x, y);//也可以写下面的代码
// mPath.quadTo((x+old_x)/2,(y+old_y)/2,x,y);
invalidate();
old_x = x;
old_y = y;
break;
}
return super.onTouchEvent(event);
}
}
3、Activity文件,布局文件中有保存按钮,实现点击按钮时保存当前的图片
public class MainActivity extends AppCompatActivity {
private Button mButton;
private BitmapView2 bitmapView2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bitmapView2 = (BitmapView2) findViewById(R.id.view);
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bitmapView2.setDrawingCacheEnabled(true);//要获取cache首先要通过setDrawingCacheEnable方法开启cache,
// 然后再调用getDrawingCache方法就可以获得view的cache图片了。
Bitmap bit = bitmapView2.getDrawingCache(true);
File file = new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis()+".jpg");//保存图片到这个路径
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
bit.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
});
4、在这个布局文件中用到了自定义属性,那么,怎样实现自定义属性?
《1》在values文件夹下新建resource file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="mystyle">//styleable的名字
//下面定义几个属性,包括名字和参数值类型
<attr name="myview_background" format="reference"></attr>
<attr name="myview_paintwidth" format="dimension|reference"></attr>
</declare-styleable>
</resources>
《2》在layout的xml布局文件中声明,设置自定义属性,见layout下的布局文件加注释的部分
《3》在view类中解析layout下xml布局文件,解析方式为value文件夹下mystyle,并找到xml布局文件的设置。详情见上面BitmapView2类中对自定义属性的设置代码