自定义画板涂鸦

通常对于在修图或者图片打点的应用中,我们会有像在黑板上画画一样的需求,类似于下面的效果:
自定义画板涂鸦_第1张图片

要实现这样一个功能,首先我们自定义一个画板,画板上操作,它频率很高并且实时性很高的,所以我们需要自定义一个 view继承surfaceview:

public class MyView extends SurfaceView implements Callback, OnTouchListener {

    private Paint p = null;
    private Path path = null;
    public int textSize = 1;
    private SurfaceHolder holder;
    public int currentColor = Color.RED;
    private Bitmap bitmap = null;

    public void setTextSize(int textSize) {
        this.textSize = textSize;
        initPamars();
    }

    public void setCurrentColor(int currentColor) {
        this.currentColor = currentColor;
        initPamars();
    }

    public MyView(Context context) {
        super(context);

        if (holder == null)
            holder = getHolder();
        holder.addCallback(this);
        initPamars();
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);

        if (holder == null)
            holder = getHolder();
        holder.addCallback(this);
        initPamars();
    }

    private void initPamars() {
        p = new Paint();
        path = new Path();
        p.setColor(currentColor);
        p.setStrokeWidth(textSize);
        p.setTextSize(20);
        p.setStyle(Style.STROKE);
        //消除锯齿
        p.setAntiAlias(true);
        // 设置监听
        setOnTouchListener(this);
    }

    public void setBackground(int id) {
        BitmapDrawable bd = (BitmapDrawable) getResources().getDrawable(id);
        Bitmap bm = bd.getBitmap();
        Matrix matrix = new Matrix();
        matrix.setScale(0.655f, 0.65f);
        bitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
    }

    /**
     * 自定义画的方法canvas.drawPath
     */
    public void draw(boolean isTogether) {
        Canvas canvas = holder.lockCanvas();
        canvas.drawColor(Color.GRAY);

        canvas.drawBitmap(bitmap, 0, 0, new Paint());

        if (list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                Action a = list.get(i);
                if (!isTogether) {
                    if (a.isDelete) {
                        list.remove(i);
                        i--;
                    }
                }
                canvas.drawPath(a.getPath(), a.getPaint());
            }
        }

        holder.unlockCanvasAndPost(canvas);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
                               int height) {
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        this.holder = holder;
        draw(false);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    }


    /**
     * 触摸事件
     */
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                initPamars();
                path.moveTo(x, y);
                break;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(x, y);
                Action action = new Action();
                action.setPaint(p);
                action.setDelete(true);
                action.setPath(path);
                list.addLast(action);
                draw(true);
                break;
            case MotionEvent.ACTION_UP:
                if (!list.isEmpty())
                    list.getLast().setDelete(false);
                draw(false);
                break;
            default:
                break;
        }
        return true;
    }

    LinkedList list = new LinkedList<>();

    class Action {
        Path path;
        Paint paint;
        boolean isDelete;

        public void setDelete(boolean delete) {
            isDelete = delete;
        }

        public void setPaint(Paint paint) {
            this.paint = paint;
        }

        public void setPath(Path path) {
            this.path = path;
        }

        public Paint getPaint() {
            return paint;
        }

        public Path getPath() {
            return path;
        }
    }

    /**
     * 回退撤销
     */
    public void back() {
        if (!list.isEmpty()) {
            list.removeLast();
        }
        draw(false);
    }

    /**
     * 清理画布
     */
    public void clear() {
        list = new LinkedList<>();
        draw(false);
    }

     /**
     * 获取图片和历史画笔保存至指定文件
     */
    public boolean savePicToSdcard(String path) {
        try {
            File file = new File(path);
            Bitmap bit = Bitmap.createBitmap(bitmap);
            Canvas canvas = new Canvas(bit);
            if (list.size() > 0) {
                for (int i = 0; i < list.size(); i++) {
                    Action a = list.get(i);
                    canvas.drawPath(a.getPath(), a.getPaint());
                }
            }
            OutputStream stream = new FileOutputStream(file);
            bit.compress(Bitmap.CompressFormat.PNG, 100, stream);
            stream.close();

            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}

然后在布局文件中加上我们基本的功能按钮:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="horizontal">
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <com.widget.huaban.MyView
            android:id="@+id/myview"
            android:layout_width="300dp"
            android:layout_height="300dp" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="20dp"
            android:orientation="horizontal">

            <View
                android:id="@+id/red_view"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:background="@android:color/holo_red_light" />

            <View
                android:id="@+id/blue_view"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_marginLeft="50dp"
                android:background="@android:color/holo_blue_light" />

            <View
                android:id="@+id/black_view"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_marginLeft="50dp"
                android:background="@android:color/black" />

            <View
                android:id="@+id/green_view"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_marginLeft="50dp"            android:background="@android:color/holo_green_light" />
        LinearLayout>

        <TextView
            android:id="@+id/sizeprogress_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="20dp"
            android:text="1"
            android:textSize="20sp" />

        <SeekBar
            android:id="@+id/seekbar"
            android:layout_width="400dp"
            android:layout_height="wrap_content"
            android:max="50"
            android:progress="1" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <Button
                android:layout_width="120dp"
                android:layout_height="50dp"
                android:gravity="center"
                android:onClick="back"
                android:text="撤销" />

            <Button
                android:layout_width="120dp"
                android:layout_height="50dp"
                android:gravity="center"
                android:onClick="clear"
                android:text="清除" />

            <Button
                android:layout_width="120dp"
                android:layout_height="50dp"
                android:gravity="center"
                android:onClick="save"
                android:text="保存" />

        LinearLayout>
    LinearLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="新涂鸦照片:" />
        <ImageView
            android:id="@+id/showview_iv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:scaleType="fitXY" />
    LinearLayout>
LinearLayout>

最后在activity中调用:

 private MyView myView;
    private TextView sizeprogress_tv;
    private SeekBar seekbar;
    private View blue_view, red_view, black_view, green_view;
    private ImageView showview_iv;

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

        showview_iv = (ImageView) findViewById(R.id.showview_iv);
        blue_view = findViewById(R.id.blue_view);
        red_view = findViewById(R.id.red_view);
        black_view = findViewById(R.id.black_view);
        green_view = findViewById(R.id.green_view);
        myView = (MyView) findViewById(R.id.myview);

        myView.setBackground(R.drawable.img_3);

        seekbar = (SeekBar) findViewById(R.id.seekbar);
        sizeprogress_tv = (TextView) findViewById(R.id.sizeprogress_tv);

        seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                sizeprogress_tv.setText("" + i);
                myView.setTextSize(i);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

        blue_view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myView.setCurrentColor(Color.BLUE);
            }
        });
        black_view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myView.setCurrentColor(Color.BLACK);
            }
        });
        red_view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myView.setCurrentColor(Color.RED);
            }
        });
        green_view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myView.setCurrentColor(Color.GREEN);
            }
        });
    }

    public void back(View view) {
        myView.back();
    }

    public void clear(View view) {
        myView.clear();
    }

    public void save(View view) {
        String path = Configutils.GalleryPath + "test.png";
        boolean b = myView.savePicToSdcard(path);
        if (b) {
            showview_iv.setImageBitmap(BitmapFactory.decodeFile(path));
        }
    }

这样我们就完成了截图中的所有功能!

**小结:
1.在surfaceCreated回调方法中得到SurfaceHolder;
2.重写onTouch,并且在ACTION_DOWN时初始化path和paint,将path移动到点击的位置;在ACTION_MOVE中将path所走的线路利用SurfaceHolder得到的canvas绘制出来,由于在手指移动过程中每移动一个坐标都会绘制一次,都会在集合中添加一个action对象,所以要将移动时的绘制对象打上标记,我这里用的是‘isDelete’,以便手指抬起时将移动过程中的对象删除,只保留抬起手指时最后一次完整路径的action;在ACTION_UP中将最后一次的action对象的标记改为保留,也就是false;
3.在修改画笔颜色和粗细时,重新初始化path和paint;
4.提供画板背景的修改方法,传入一张图片在绘制线路时将背景图画上;
5.回退撤销操作,则将线路历史集合中的最后一个移除并且刷新;
6.保存涂鸦时,则将传入的背景图对象bitmap传给canvas对象,然后将线路历史全部绘制上写入指定的文件中。**

你可能感兴趣的:(图片)