安卓动画基础--画板

安卓动画基础–画板

最近今天学习了下安卓的绘图类基础 Canvas类 Path类 Paint类这三种基础绘图类,本来是想学动画的,但是这个绘画类的基础就是这三个,动画只是加入了循环播放和旋转什么的
Canvas类 :就是画布,简单的来说就是创建一个画布,拿出一张纸,可以自定义这张在这张纸上任意地方画东西。
Paint类:就是画笔,画笔在Canvas上画东西,可以自定义画笔的大小,颜色,粗细等
path:就是路径,代表任意多的直线连接成图形
做出来的效果如下:

主要的功能有:
1.绘图
2.修改画笔的粗细
3.修改画笔的颜色
4.撤回最近画的那笔
5.恢复撤回的那笔
6.橡皮擦功能

实现的逻辑顺序:
1.自定义一个画布类继承View类
2.利用双缓冲原理实现画板的手绘功能:当看起来是用户在屏幕上画曲线时候,其实是先监听那个人按下去的起始的坐标,然后每次拖动时候,距离太小,然后将很多小直线连接起来就好了,但是为了记住上次那个小直线,就需要双缓冲技术,讲用户绘制的东西先存在一个Bithmap上面,等这个画好之后,直接绘制到自定义画板上。
3.在删除路径和恢复路径的实现也蛮简单的,记录下每次的小线段的path和paint,然后放在LIst里面,对这两个list进行操作
4.修改颜色这个,就是把各种颜色放在一个数组里面,每次点击时候,替换一下,画笔的大小同理。
5.实现布局页面,安排各种按钮的位置
4.美化界面

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.hasee.canvas.MainActivity"
    android:background="@mipmap/back">
    <LinearLayout
        android:id="@+id/Lin"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:orientation="horizontal"
        android:weightSum="3">
        <Button
            android:layout_weight="1"
            android:id="@+id/but_last"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="撤销"
            android:background="@drawable/shapecircle" />
        <Button
            android:id="@+id/but_redo"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="@drawable/shapecircle"
            android:text="重画"/>
        <Button
            android:id="@+id/but_recove"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="@drawable/shapecircle"
            android:text="恢复"/>

    </LinearLayout>
    <FrameLayout
        android:layout_below="@id/Lin"
        android:id="@+id/br"
        android:layout_width="match_parent"
        android:layout_height="400dp">
    </FrameLayout>
    <SeekBar
        android:id="@+id/seekbar"
        android:layout_below="@id/br"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="30"
        android:progress="5"/>
    <LinearLayout
        android:layout_below="@id/seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:weightSum="3">

        <Button
            android:id="@+id/paint_style"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@drawable/shapecircle"
            android:text="画笔的格式" />
        <Button
            android:id="@+id/paint_color"
            android:layout_weight="1"
            android:text="画笔的颜色"
            android:layout_width="0dp"
            android:background="@drawable/shapecircle"
            android:layout_height="match_parent" />

        <Button
            android:id="@+id/paint_size"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@drawable/shapecircle"
            android:text="画笔的大小" />


    </LinearLayout>


</RelativeLayout>

自定义按钮格式shapecircle.xml

在res/drawable/

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <!-- Corner的属性是设置圆角的半径的-->
    <corners
        android:bottomLeftRadius="20dp"
        android:bottomRightRadius="25dp"
        android:radius="20dp"
        android:topLeftRadius="10dp"
        android:topRightRadius="15dp" />
    <!-- Button里面的文字与Button边界的间隔-->
    <padding
        android:bottom="10dp"
        android:left="10dp"
        android:right="10dp"
        android:top="10dp" />
    <solid android:color="#FFCC99" />
</shape>

自定义画板类

package com.example.hasee.canvas;

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.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Created by hasee on 2018/4/11.
 */

public class TuyaView extends View {
    private Context context;
    private Bitmap mBithmap; //缓存的图片
    private Canvas mCanvas;  //画布
    private Path mPath;    //路径
    private Paint mPaint;  //真实的画笔
    private Paint mBithmapPaint; //画布的画笔
    private float mX,mY;   //临时的坐标
    private static final float TOUCH_TOLERANCE = 4;
    private static List<DrawPath> savePath;  //保存path的路径的集合
    private static List<DrawPath> deletePath; //保存删除的path的路径
    private DrawPath dp;   //路径的的对象
    private int screenWidth, screenHeight; //屏幕的
    private int currentColor = Color.RED;
    private int currentSize = 5;
    private int currentStyle = 1;
    private int[] paintColor;//颜色集合

    private class DrawPath{
        public Path path;  //路径
        public Paint paint; //画笔
    }

    public TuyaView(Context context, int x,int y) {
        super(context);
        this.context = context;
        screenWidth = x;
        screenHeight = y;

        paintColor = new int[]{
                Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW, Color.BLACK, Color.GRAY, Color.CYAN
        };
        setLayerType(LAYER_TYPE_HARDWARE,null);//设置格式
        initCanvas();
        savePath = new ArrayList<DrawPath>();
        deletePath = new ArrayList<DrawPath>();
    }

    /**
     * 初始化画布和缓冲区图片
     */
    private void initCanvas(){
        setPaintStyle();
        mBithmap = Bitmap.createBitmap(screenWidth,screenHeight,Bitmap.Config.ARGB_8888);//缓冲区
        mBithmap.eraseColor(Color.argb(0,0,0,0));  //颜色
        mCanvas = new Canvas(mBithmap);  //把所有的画布的东西放在图片中
        mCanvas.drawColor(Color.TRANSPARENT);
    }
    /**
     * 初始话画笔的样式
     */
    private void setPaintStyle(){
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setDither(true);
        if (currentStyle==1){
            mPaint.setStrokeWidth(currentSize);
            mPaint.setColor(currentColor);
        }else {
            mPaint.setAlpha(0);
            mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
            mPaint.setColor(Color.TRANSPARENT);
            mPaint.setStrokeWidth(100);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mBithmap, 0, 0, mBithmapPaint);//将之前的显示出来
        if (mPath != null){
            //实时的显示
            canvas.drawPath(mPath,mPaint);
        }
    }

    /**
     * 获取初始
     * @param x
     * @param y
     */
    private void touch_srart(float x,float y){
        mPath.moveTo(x,y);
        mX = x;
        mY = y;
    }
    private void touch_move(float x,float y){
        float dx = Math.abs(x-mX);
        float dy = Math.abs(y-mY);
        if(dx>=TOUCH_TOLERANCE||dy>=TOUCH_TOLERANCE){
            mPath.quadTo(mX,mY,(x+mX)/2,(y+mY)/2);
            mX = x;
            mY = y;
        }
    }
    private void touch_up(){
        mPath.lineTo(mX,mY);
        mCanvas.drawPath(mPath,mPaint);//将完整的路径保存下了
        savePath.add(dp);
        mPath = null;  //清空
    }
    /**
     * 撤销
     * 将保存的最后一个Path路径移除
     * 重新画在画布上
     */
    public void undo(){
        if (savePath!=null&&savePath.size()>0){
            DrawPath drawPath = savePath.get(savePath.size()-1);
            deletePath.add(drawPath);
            savePath.remove(savePath.size()-1);
            redrawOnBitmap();
        }
    }

    /**
     * 重新写
     * 将其清空然后重新画
     */

    public void redo(){
        if (savePath!=null&&savePath.size()>0){
            savePath.clear();  //把路径全清空
            redrawOnBitmap();//重新画
        }
    }

    /**
     * 重新画
     */

    private void redrawOnBitmap(){
        initCanvas();
        Iterator<DrawPath> iter = savePath.iterator();
        while (iter.hasNext()) {
            DrawPath drawPath = iter.next();
            mCanvas.drawPath(drawPath.path, drawPath.paint);
        }
        invalidate();// 刷新
    }

    /**
     * 恢复
     * 就是将删除的那个路径重新加载到savaPath里面
     */

    public void recover(){
        if (deletePath.size()>0){
            DrawPath dp = deletePath.get(deletePath.size()-1);
            savePath.add(dp);
            //重新画一下
            mCanvas.drawPath(dp.path,dp.paint);
            deletePath.remove(deletePath.size()-1);
            invalidate();

        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY(); //获取目前的坐标

        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mPath = new Path();
                dp = new DrawPath();
                dp.paint = mPaint;
                dp.path = mPath;
                touch_srart(x,y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x,y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
        }
        return true;
    }

    /**
     * 画笔的样式
     * @param which
     */
    public void selectPaintStyle(int which){
        if (which == 0){
            currentStyle =1;
            setPaintStyle();
        }
        if (which == 1){
            currentStyle = 2;
            setPaintStyle();
        }
    }

    /**
     * 画笔的大小
     * @param which
     */
    public void selectPaintSize(int which){
        currentSize = which;
        setPaintStyle();
    }

    /**
     * 画笔的颜色
     * @param which
     */
    public void selectPaintColor(int which){
        currentColor = paintColor[which];
        setPaintStyle();
    }

}

MainActivity类

package com.example.hasee.canvas;

import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.Display;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.SeekBar;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TuyaView tuyaView;  //自定义画版
    private FrameLayout frameLayout;
    private Button btn_undo;
    private Button btn_redo;
    private Button btn_recover;
    private Button btn_paintcolor;
    private Button btn_paintsize;
    private Button btn_paintstyle;
    private SeekBar sb_size;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
        initListener();
        sb_size.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                tuyaView.selectPaintSize(progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });
    }
    private void initView() {
        frameLayout = (FrameLayout) findViewById(R.id.br);
        btn_undo = (Button) findViewById(R.id.but_last);
        btn_redo = (Button) findViewById(R.id.but_redo);
        btn_recover = (Button) findViewById(R.id.but_recove);
        btn_paintcolor = (Button) findViewById(R.id.paint_color);
        btn_paintsize = (Button) findViewById(R.id.paint_size);
        btn_paintstyle = (Button) findViewById(R.id.paint_style);
        sb_size = (SeekBar) findViewById(R.id.seekbar);
    }
    private void initListener(){
        btn_paintcolor.setOnClickListener(this);
        btn_paintsize.setOnClickListener(this);
        btn_paintstyle.setOnClickListener(this);
        btn_recover.setOnClickListener(this);
        btn_redo.setOnClickListener(this);
        btn_undo.setOnClickListener(this);
        sb_size = (SeekBar)findViewById(R.id.seekbar);
    }
    private void initData() {
        //虽然此时获取的是屏幕宽高,但是我们可以通过控制framlayout来实现控制涂鸦板大小
        Display defaultDisplay = getWindowManager().getDefaultDisplay();
        int screenWidth = defaultDisplay.getWidth();
        int screenHeight = defaultDisplay.getHeight();
        tuyaView = new TuyaView(this,screenWidth,screenHeight);
        frameLayout.addView(tuyaView);
        tuyaView.requestFocus();
        tuyaView.selectPaintSize(sb_size.getProgress());
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.but_last:  //撤销
                tuyaView.undo();
                break;
            case R.id.but_redo:  //重做
                tuyaView.redo();
                break;
            case R.id.but_recove: //恢复
                tuyaView.recover();
                break;
            case R.id.paint_size:
                sb_size.setVisibility(View.VISIBLE);
                break;
            case R.id.paint_color:
                sb_size.setVisibility(View.GONE);
                showPaintColorDialog(v);
                break;
            case R.id.paint_style:
                sb_size.setVisibility(View.GONE);
                showMoreDialog(v);
                break;

        }
    }
    private int select_paint_color_index = 0;
    private int select_paint_style_index = 0;

    public void showPaintColorDialog(View parent){
        final String[] paintColor = new String[]{
                "红色","蓝色", "橙色", "黄色", "黑色", "灰色", "青色"
        };
        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
        alertDialogBuilder.setTitle("选择画笔颜色:");
        alertDialogBuilder.setSingleChoiceItems(paintColor, 0, new DialogInterface.OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {
               select_paint_color_index = which;
               tuyaView.selectPaintColor(which);
               dialog.dismiss();
           }
       });
        alertDialogBuilder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        alertDialogBuilder.create().show();
    }

    //弹出选择画笔或橡皮擦的对话框
    public void showMoreDialog(View parent){
        final String[] paintstyle = new String[]{"画笔","橡皮擦"};
        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
        alertDialogBuilder.setTitle("选择画笔或橡皮擦:");
        alertDialogBuilder.setSingleChoiceItems(paintstyle, 0, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                select_paint_style_index = which;
                tuyaView.selectPaintStyle(which);
                dialog.dismiss();
            }
        });
        alertDialogBuilder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        alertDialogBuilder.create().show();
    }

}

总结

这个就是按网上一个大佬博客写的东西,熟悉熟悉下绘图的基本类的用法,写个简单的小东西,,,然后继续写项目了,,,产品经理太可怕了哇咔咔

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值