Android简易的涂鸦软件

先上图:


主要代码:

package com.example.administrator.androidtest;

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.Environment;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TuyaView extends View {

    private int view_width=0;//屏幕宽
    private int view_height=0;//屏幕高
    private float preX;//起点X
    private float preY;//起点Y;

    private Path path;//路径
    public Paint paint=null;//画笔
    public Paint mBitmapPaint;// 画布的画笔
    Bitmap cacheBitmap=null;//定义一个内存图片为缓存区
    Canvas cacheCanvas=null;//定义cacheBitmap上的canvas对象

    private static List<DrawPath> savePath;// 保存Path路径的集合,List集合来模拟栈
    private DrawPath dp;// 记录Path 路径的对象

    int name=100;
    public TuyaView(Context context, AttributeSet set){
        super(context,set);

        view_width=context.getResources().getDisplayMetrics().widthPixels;
        view_height=context.getResources().getDisplayMetrics().heightPixels;
        cacheBitmap=Bitmap.createBitmap(view_width, view_height,
                Bitmap.Config.ARGB_8888);
        // 保存一次一次绘制出来的图形
        cacheCanvas=new Canvas(cacheBitmap);
        mBitmapPaint=new Paint(Paint.DITHER_FLAG);
        paint=new Paint();
        paint.setColor(Color.RED);//默认画笔颜色

        //设置画笔风格
        paint.setStyle(Paint.Style.STROKE);//设置填充方式为描边
        paint.setStrokeJoin(Paint.Join.ROUND);//设置笔刷的图形样式
        paint.setStrokeCap(Paint.Cap.ROUND);//设置画笔转弯处的连接风格
        paint.setStrokeWidth(5);//设置默认宽度5像素
        paint.setAntiAlias(true);//抗锯齿功能
        paint.setDither(true);//抖动效果

        savePath=new ArrayList<DrawPath>();
    }

    public void clear()
    {
//        paint.setAlpha(0);  //设置透明度为0
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        paint.setStrokeWidth(25);//设置笔触宽度
    }

    //清空画布
    public void clearall()
    {
        cacheBitmap=Bitmap.createBitmap(view_width,view_height, Bitmap.Config.ARGB_8888);
        cacheCanvas.setBitmap(cacheBitmap);// 重新设置画布,相当于清空画布
        invalidate();// 刷新
    }

    @Override
    public void onDraw(Canvas canvas) {
//        canvas.drawColor(0xFFFFFFFF);//默认背景颜色白色
        canvas.drawColor(MainActivity.getcolor());//选择背景颜色
        // 将前面已经画过得显示出来
        canvas.drawBitmap(cacheBitmap, 0, 0, mBitmapPaint);
        if (path!=null) {
            // 实时的显示
            canvas.drawPath(path, paint);
        }
    }

   // 撤销就是将画布清空,将保存下来的Path路径最后一个移除掉,重新将路径画在画布上面。
    public void undo() {
        cacheBitmap = Bitmap.createBitmap(view_width, view_height,
                Bitmap.Config.ARGB_8888);
        cacheCanvas.setBitmap(cacheBitmap);// 重新设置画布,相当于清空画布
        // 清空画布,但是如果图片有背景的话,则使用上面的重新初始化的方法,用该方法会将背景清空掉...
        if (savePath != null && savePath.size() > 0) {
            // 移除最后一个path,相当于出栈操作
            savePath.remove(savePath.size() - 1);
            Iterator<DrawPath> iter = savePath.iterator();
            while (iter.hasNext()) {
                DrawPath drawPath = iter.next();
                cacheCanvas.drawPath(drawPath.path, drawPath.paint);
            }
            invalidate();// 刷新
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 每次down下去重新new一个Path
                path = new Path();
                //每一次记录的路径对象是不一样的
                dp = new DrawPath();
                dp.path = path;
                dp.paint = paint;
                path.moveTo(x, y);
                preX = x;
                preY = y;
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                float dx=Math.abs(x - preX);
                float dy=Math.abs(preY - y);
                if (dx>=4||dy>=4) {
                    // x1,y1x2,y2画一条贝塞尔曲线,更平滑(直接用path.lineTo也是可以的)
                    path.quadTo(preX, preY,(x + preX) / 2,(y + preY) / 2);
                    preX=x;
                    preY=y;
                }
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                path.lineTo(preX, preY);
                cacheCanvas.drawPath(path, paint);
                //将一条完整的路径保存下来(相当于入栈操作)
                savePath.add(dp);
                path = null;// 重新置空
                invalidate();
                break;
        }
        return true;
    }

    public void save()
    {
        try{
            saveBitmap("myPicture");
        }catch(IOException e){
            e.printStackTrace();
        }
    }

    //保存绘制好的位图
    public void saveBitmap(String fileName) throws IOException{

        if (cacheBitmap!= null) {
            String path = Environment.getExternalStorageDirectory().getPath();
            name+=1;
            File file = new File(path+"/Pictures/",name+".png");
            file.createNewFile();//创建一个新文件
            try {
                FileOutputStream bos= new FileOutputStream(file);
                cacheBitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
                bos.flush();
                bos.close();
                Toast.makeText(getContext(), "保存成功!", Toast.LENGTH_SHORT).show();

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public class DrawPath {
        public Path path;// 路径
        public Paint paint;// 画笔
    }
}
//-----------------------------------------------------------------
package com.example.administrator.androidtest;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Main3Activity extends Activity implements OnClickListener, OnTouchListener
{
    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            "android.permission.READ_EXTERNAL_STORAGE",
            "android.permission.WRITE_EXTERNAL_STORAGE" };
    String num,col;
    int dw,dh;  //显示部分宽高
    int w=0,h=0;//矩形宽高
    int r=0;//圆半径
    int judge=0,judge1=0,name=0;

    String col1,col2;//填充颜色
    private ImageView ima;
    private Button choose, save,choose_width,choose_color,choose_shape,clear,undo,
            rect,rect1,cicle,cicle1,back;

    private Bitmap bitmap, mbitmap;
    private Canvas canvas;

    private Paint paint,mpaint;
    private Path path;//路径

    private Matrix matrix;
    private float preX=0,preY=0;
    int hRatio,wRatio;

    private DrawPath dp;// 记录Path 路径的对象
    private static List<DrawPath> savePath;// 保存Path路径的集合,List集合来模拟栈
    public class DrawPath {
        public Path path;// 路径
        public Paint paint;// 画笔
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);

        verifyStoragePermissions(this);
        ima=(ImageView)findViewById(R.id.choosePic);
        choose=(Button)findViewById(R.id.btn_choose);
        save=(Button)findViewById(R.id.btn_save);
        choose_width=(Button)findViewById(R.id.choose_width);
        choose_color=(Button)findViewById(R.id.choose_color);
        choose_shape=(Button)findViewById(R.id.rotate);
        clear=(Button)findViewById(R.id.btn_clear);
        undo=(Button)findViewById(R.id.btn_undo);
        back=(Button)findViewById(R.id.back);
        rect=(Button)findViewById(R.id.rect);
        rect1=(Button)findViewById(R.id.rect1);
        cicle=(Button)findViewById(R.id.cicle);
        cicle1=(Button)findViewById(R.id.cicle1);

        //设置事件监听
        choose.setOnClickListener(this);
        save.setOnClickListener(this);
        choose_width.setOnClickListener(this);
        choose_color.setOnClickListener(this);
        choose_shape.setOnClickListener(this);
        clear.setOnClickListener(this);
        undo.setOnClickListener(this);
        back.setOnClickListener(this);
        ima.setOnTouchListener(this);
        rect.setOnClickListener(this);
        rect1.setOnClickListener(this);
        cicle.setOnClickListener(this);
        cicle1.setOnClickListener(this);
        //设置save为不可用
        save.setEnabled(false);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_choose:
                // 设置save为不可用
                if (save.isEnabled())
                    save.setEnabled(false);
                // 使用Intent来选择图片
                Intent it=new Intent(
                        Intent.ACTION_PICK,
                        android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                startActivityForResult(it,0);
                break;
            case R.id.btn_clear:
                clear();
                break;
            case R.id.btn_undo:
                undo();
                break;
            case R.id.btn_save:
                if (mbitmap!= null) {
                    String path = Environment.getExternalStorageDirectory().getPath();
                    name+=1;
                    File file = new File(path+"/Pictures/",name+".png");
                    try {

                        verifyStoragePermissions(this);

                        FileOutputStream bos= new FileOutputStream(file);
                        mbitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
                        bos.flush();
                        bos.close();

                        Toast.makeText(this,"保存成功",Toast.LENGTH_SHORT).show();
                        System.out.println(path);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                break;
            case R.id.choose_width:
                Intent it3=new Intent(Main3Activity.this,widthActivity.class);
                startActivityForResult(it3,2);
                break;
            case R.id.choose_color:
                Intent it2=new Intent(Main3Activity.this,colorActivity.class);
                startActivityForResult(it2,3);
                break;
            case R.id.rotate:
                Roate();
                break;
            case R.id.back:
                Intent it1=new Intent(this,Main2Activity.class);
                startActivity(it1);
                break;
            case R.id.rect:
                Intent it4=new Intent(this,rectActivity.class);
                startActivityForResult(it4,4);
                break;
            case R.id.rect1:
                drawRect2(xx,yy,w,h,col1);
                break;
            case R.id.cicle:
                Intent it5=new Intent(this,cicleActivity.class);
                startActivityForResult(it5,5);
                break;
            case R.id.cicle1:
                drawCicle2(xx,yy,r,col2);
                break;
        }
    }

    // 撤销就是将画布清空,将保存下来的Path路径最后一个移除掉,重新将路径画在画布上面。
    public void undo() {

        canvas=new Canvas(mbitmap);
        matrix=new Matrix();
        canvas.drawBitmap(bitmap, matrix, paint);// 绘制bitmap
        ima.setImageBitmap(mbitmap);// mbitmap设置到ima上面
        ima.setOnTouchListener(this);

        if (savePath != null && savePath.size() > 0) {
            // 移除最后一个path,相当于出栈操作
            savePath.remove(savePath.size() - 1);
            Iterator<DrawPath> iter = savePath.iterator();
            while (iter.hasNext()) {
                DrawPath drawPath = iter.next();
                canvas.drawPath(drawPath.path, drawPath.paint);
            }
            ima.invalidate();// 刷新
        }
    }

    //橡皮檫
    public void clear()
    {
        paint.setAlpha(0);  //设置透明度为0
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        paint.setStrokeWidth(25);//设置笔触宽度
    }

    //实现涂鸦实时显示
    public void onDraw(Canvas canvas)
    {
        canvas.drawBitmap(mbitmap, 0, 0, paint);
        if (path!=null) {
            // 实时的显示
            canvas.drawPath(path, paint);
        }
    }

    //图像点击一次做15度旋转
    int angle=0;
    public void Roate()
    {
        matrix=ima.getImageMatrix();
        if(angle>=360)
            angle=0;
        else
            angle+=15;

        matrix.setRotate(angle,ima.getWidth()/2,ima.getHeight()/2);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        canvas.drawPaint(paint);
//        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
//        ima.postInvalidate();
        paint.setXfermode(null);//取消擦除效果
        canvas.drawBitmap(bitmap, matrix, mpaint);// 绘制bitmap

        ima.setImageBitmap(mbitmap);// mbitmap设置到ima上面
        ima.invalidate();
        ima.setOnTouchListener(this);
    }

    int xx,yy;//选点坐标
    private void drawRect2(int x,int y,int w,int h,String col1)//画矩形
    {
        Rect r=new Rect();
        r.left=x;
        r.top=y;
        r.right=x+w;
        r.bottom=y+h;
        paint.setColor(Integer.parseInt(col1));
        if(judge==0)
            paint.setStyle(Paint.Style.STROKE);
        else
             paint.setStyle(Paint.Style.FILL); //实心矩形
        canvas.drawRect(r,paint);
        paint.setStyle(Paint.Style.STROKE);
    }

    private void drawCicle2(int x,int y,int r,String col1)//画圆形
    {
        if(judge1==0)
            paint.setStyle(Paint.Style.STROKE);
        else
            paint.setStyle(Paint.Style.FILL);//实心圆
        paint.setColor(Integer.parseInt(col1));
        canvas.drawCircle(x,y,r,paint);
        paint.setStyle(Paint.Style.STROKE);
    }

    @Override
    protected void onActivityResult(int requestCode,int resultCode,Intent data) {
        super.onActivityResult(requestCode,resultCode,data);

        if (resultCode==RESULT_OK) {
            Uri uri=data.getData();//获取URI
            // 获取默认Display
            Display currentDisplay=getWindowManager().getDefaultDisplay();

            //显示部分的宽高
            dw=currentDisplay.getWidth();
            dh=currentDisplay.getHeight();

            BitmapFactory.Options bmFactoryOptions=new Options();// 使用BitmapFactory创建位图Options
            bmFactoryOptions.inJustDecodeBounds=true;// 设置只是获取图片的尺寸

            // 使用decodeStream方法创建位图,getContentResolver()获取內容提供器
            try {
                bitmap=BitmapFactory.decodeStream(getContentResolver()
                        .openInputStream(uri), null, bmFactoryOptions);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            // 计算图像宽高与当前宽高的比率
            hRatio=(int)Math.ceil(bmFactoryOptions.outHeight/(float)dh);
            wRatio=(int)Math.ceil(bmFactoryOptions.outWidth/(float)dw);
            //判断是按高比率缩放还是宽比例缩放
            if (hRatio>1||wRatio>1) {
                if (hRatio>wRatio) {
                    bmFactoryOptions.inSampleSize=hRatio;
                } else {
                    bmFactoryOptions.inSampleSize=wRatio;
                }
            }
            // 对图像进行解码
            bmFactoryOptions.inJustDecodeBounds=false;
            try {
                bitmap=BitmapFactory.decodeStream(getContentResolver()
                        .openInputStream(uri),null,bmFactoryOptions);
            }catch(FileNotFoundException e){
                e.printStackTrace();
            }

            // 在加载位圆之后,创建一个可变的位图对象mbitmap,并在其中绘制bitmap对象
            mbitmap=Bitmap.createBitmap(bitmap.getWidth(),
                    bitmap.getHeight(), bitmap.getConfig());

            canvas=new Canvas(mbitmap);// 使用mbitmap作为构造参数,创建canvas
            mpaint=new Paint(Paint.DITHER_FLAG);
            paint=new Paint();// 画笔

            paint.setAntiAlias(true);//抗锯齿功能
            paint.setDither(true);//抖动效果
            paint.setStyle(Paint.Style.STROKE);//设置填充方式为描边
            paint.setStrokeJoin(Paint.Join.ROUND);//设置笔刷的图形样式
            paint.setStrokeCap(Paint.Cap.ROUND);//设置画笔转弯处的连接风格

            paint.setColor(Color.BLACK);// 设置画笔颜色为黑色
            paint.setStrokeWidth(5);// 设置画笔大小

            matrix=new Matrix();// 创建matrix,此处对图像不进行任何的缩放,旋转等操作

            canvas.drawBitmap(bitmap, matrix, paint);// 绘制bitmap
            ima.setImageBitmap(mbitmap);// mbitmap设置到ima上面
            save.setEnabled(true);// 此时设置save为可用状态
            ima.setOnTouchListener(this);

            savePath=new ArrayList<DrawPath>();
        }

        if(requestCode==2)
        {
            if(resultCode==3)
            {
                num=data.getStringExtra("num");
                paint.setStrokeWidth(Integer.parseInt(num));
            }
        }

        if(requestCode==3)
        {
            if(resultCode==2)
            {
                col=data.getStringExtra("col");
                paint.setColor(Integer.parseInt(col));
            }
        }

        if(requestCode==4)
        {
            if(resultCode==4)
            {
                w=Integer.parseInt(data.getStringExtra("w"));
                h=Integer.parseInt(data.getStringExtra("h"));
                judge=Integer.parseInt(data.getStringExtra("yn"));
                col1=data.getStringExtra("col1");
            }
        }

        if(requestCode==5)
        {
            if(resultCode==5)
            {
                r=Integer.parseInt(data.getStringExtra("r"));
                judge1=Integer.parseInt(data.getStringExtra("yn"));
                col2=data.getStringExtra("col2");
            }
        }
    }

    @Override
    public boolean onTouch(View v,MotionEvent event) {

        float x=event.getX();
        float y=event.getY();

        xx=(int) Math.ceil(x);
        yy=(int) Math.ceil(y);
        // 判断不同状态
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                // 每次down下去重新new一个Path
                path = new Path();
                //每一次记录的路径对象是不一样的
                dp = new DrawPath();
                dp.path = path;
                dp.paint = paint;
                path.moveTo(x, y);
                // 记录按下坐标
                preX=x;
                preY=y;
                ima.invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                // 移动过程中,不断绘制Line
                float dx=Math.abs(x - preX);
                float dy=Math.abs(preY - y);
                if (dx>=4||dy>=4) {
                    path.quadTo(preX, preY,(x + preX) / 2,(y + preY) / 2);
                    onDraw(canvas);//实时显示
                    preX=x;
                    preY=y;
                }
                ima.invalidate();
                break;
            case MotionEvent.ACTION_UP:

                path.lineTo(preX, preY);
                canvas.drawPath(path, paint);
                //将一条完整的路径保存下来(相当于入栈操作)
                savePath.add(dp);
                path = null;// 重新置空
                ima.invalidate();
                break;
        }
        return true;
    }

    //Android6.0需要设置动态访问权限
    public static void verifyStoragePermissions(Activity activity) {
        try {
            //检测是否有写的权限
            int permission = ActivityCompat.checkSelfPermission(activity,
                    "android.permission.WRITE_EXTERNAL_STORAGE");
            if (permission != PackageManager.PERMISSION_GRANTED) {
                // 没有写的权限,去申请写的权限,会弹出对话框
                ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}


已标记关键词 清除标记
相关推荐
【为什么还需要学习C++?】 你是否接触很多语言,但从来没有了解过编程语言的本质? 你是否想成为一名资深开发人员,想开发别人做不了的高性能程序? 你是否经常想要窥探大型企业级开发工程的思路,但苦于没有基础只能望洋兴叹?   那么C++就是你个人能力提升,职业之路进阶的不二之选。 【课程特色】 1.课程共19大章节,239课时内容,涵盖数据结构、函数、类、指针、标准库全部知识体系。 2.带你从知识与思想的层面从0构建C++知识框架,分析大型项目实践思路,为你打下坚实的基础。 3.李宁老师结合4大国外顶级C++著作的精华为大家推出的《征服C++11》课程。 【学完后我将达到什么水平?】 1.对C++的各个知识能够熟练配置、开发、部署; 2.吊打一切关于C++的笔试面试题; 3.面向物联网的“嵌入式”和面向大型化的“分布式”开发,掌握职业钥匙,把握行业先机。 【面向人群】 1.希望一站式快速入门的C++初学者; 2.希望快速学习 C++、掌握编程要义、修炼内功的开发者; 3.有志于挑战更高级的开发项目,成为资深开发的工程师。 【课程设计】 本课程包含3大模块 基础篇 本篇主要讲解c++的基础概念,包含数据类型、运算符等基本语法,数组、指针、字符串等基本词法,循环、函数、类等基本句法等。 进阶篇 本篇主要讲解编程中常用的一些技能,包含类的高级技术、类的继承、编译链接和命名空间等。 提升篇: 本篇可以帮助学员更加高效的进行c++开发,其中包含类型转换、文件操作、异常处理、代码重用等内容。
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页