**首先在这里声明下,此博客用到了一些博主的博客内容,但是因为电脑系统重装丢了写文件,找不到当时的博客的地址了,如果有知道的还请想告。**
有写博客这个想法很久了,但是因为各种原因没有执行,现在终于下定决心去努力做好这件事了。主要是这不仅可以分享我在学习工作中的心得,而且能够很好的整理我自己的知识思路可谓是双赢的局面。那么闲话好说开始我的第一篇技术博客吧!
既然是图片编辑那么就要有图片我们的图片可以是直接拍照,也可以从相册里选,相关代码比较简单,如下:
/**
* 在这里我设置了requestWindowFeature(Window.FEATURE_NO_TITLE)属性,但是发现失效,后来研究发现是因为
* 我们以前继承的是Activity ,而现在继承的是AppCompatActivity,所以此代码失效。生效代码如下
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
if (getSupportActionBar()!=null){
getSupportActionBar().hide();
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.take_photo:
if (isSdcardExisting()){
Intent intent=new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT,getImageUri());
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY,0);
startActivityForResult(intent,CAMERA_REQUEST_CODE);
}else {
Toast.makeText(v.getContext(), "请插入sd卡", Toast.LENGTH_LONG)
.show();
}
break;
case R.id.photos:
Intent intent =new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent,IMAGE_REQUEST_CODE);
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode!=RESULT_OK){
return;
}else{
if (requestCode==IMAGE_REQUEST_CODE){
EditImage(data.getData());
}else if (requestCode==CAMERA_REQUEST_CODE){
if (isSdcardExisting()){
EditImage(getImageUri());
}else {
Toast.makeText(this, "请插入sd卡", Toast.LENGTH_LONG)
.show();
}
}else if (requestCode==RESIZE_REQUEST_CODE){
if (data!=null){
showImage(data);
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* 跳转至照片编辑页面
* @param uri
*/
public void EditImage(Uri uri) {
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 150);
intent.putExtra("outputY", 150);
intent.putExtra("return-data", true);
startActivityForResult(intent, RESIZE_REQUEST_CODE);
}
这里只贴出来需要跳转的三个系统类,至于如何判断是否存在sd卡以及跳转至主页面的具体操作大家可以自己写下,比较简单。
好了,下面就是我们关键地方了,我们自定义的view,一开始就是一堆初始化:
private final String TAG=this.getClass().getSimpleName();
//背景位图
private Bitmap bitmap_bg=null;
//前景位图
private Bitmap bitmap_fg=null;
//用于画图的画布
private Canvas mCanvas;
//画笔
private Paint mPaint;
//橡皮
private Paint mEraser;
private Path mPath;
private Matrix mMatrix;
//画笔的宽度
private int paintWidth=6;
//橡皮的大小
private int eraserWidth=15;
//图片的规定宽高
private int bitmapWidth=488;
private int bitmapHeight=680;
//图片的原宽高
private int oldBitmapWidth;
private int oldBitmapHeight;
private float scaleX;
private float scaleY;
private boolean isStart = true;
private float lastX;
private float lastY;
private paint paintState=paint.PEN;
/**
* 判断笔的状态
*/
private enum paint{
PEN,ERASER;
}
private void init(){
setFocusableInTouchMode(true); // 确保能接收到触屏事件
setFocusable(true);
mMatrix=new Matrix();
mPath=new Path();
initPaint();
initEraser();
}
/**
* 初始化画笔
*/
private void initPaint(){
mPaint=new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.RED);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(paintWidth);
}
private void initEraser(){
mEraser=new Paint();
mEraser.setAntiAlias(true);
mEraser.setDither(true);
mEraser.setStyle(Paint.Style.STROKE);
mEraser.setStrokeJoin(Paint.Join.ROUND);
mEraser.setStrokeCap(Paint.Cap.ROUND);
mEraser.setStrokeWidth(eraserWidth);
mEraser.setColor(Color.TRANSPARENT);
mEraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
}
通过代码我们可以看出我们是分了前景和后景图的,这样我们在后期的时候操作起来就比较方便了,可以单独的处理前景图和后景图,发现这不是和我们的面向对象语言的封装的特性不谋而合,哈哈。这里面还有一个关键对我们以后的功能拓展起到关键的作用,就是我们将mCanvas中的所有内容通过构造方法保存到了前景图中,这样我们后续的只需要将前景图呈现到初始画布上就可以了,而前景图的放大缩小,平移等操作将只在mCanvas画布上进行,最重要的是我们的画笔不在需要在进行边界的检测了,因为他只会画到我们的前景图上,是不是发现很爽,实现起来非常简单了。
好了图片有了,也初始化了,那么我们就将进行绘画了,说道绘画那肯定少不了我们的触摸事件:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:
onTouchDown(event);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
onTouchMove(event);
if (paintState==paint.PEN){
mCanvas.drawPath(mPath,mPaint);
}else if (paintState==paint.ERASER){
mCanvas.drawPath(mPath,mEraser);
}
invalidate();
break;
case MotionEvent.ACTION_UP:
onTouchUp(event);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
return true;
}
private void onTouchDown(MotionEvent event){
isStart=true;
onTouchMove(event);
}
private void onTouchMove(MotionEvent event){
float x;
float y;
x=event.getX();
y=event.getY();
drawPoint(x,y);
}
private void onTouchUp(MotionEvent event){
onTouchMove(event);
isStart=true;
}
private void drawPoint(float x, float y){
//判断是否是起始点
if (isStart){
setStartPoint(x,y);
}else {
float dx=Math.abs(x-lastX);
float dy=Math.abs(y-lastY);
if (dx>=3 || dy>=3){
mPath.quadTo(lastX, lastY, x, y);
}
Log.e(TAG,"move....");
lastX = x;
lastY = y;
}
}
private void setStartPoint(float x, float y){
lastX=x;
lastY=y;
mPath.reset();
mPath.moveTo(x,y);
isStart=false;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bitmap_bg!=null){
canvas.drawBitmap(bitmap_bg,mMatrix,mPaint);
}
if (bitmap_fg!=null){
canvas.drawBitmap(bitmap_fg,0,0,mPaint);
}
}
好了,一气呵成,基本实现了绘画的功能了,大家会发现我们的绘画操作基本是在前景图上进行的,我们拓展起功能来就方便多了,接下来我们就要给他添加更加强大的功能了,缩放和平移功能,而且我们的画图板可是在缩放平移后还可以继续正常绘画呢,希望大家继续关注我的下一篇博客。
源码下载链接:画图板(一)