项目需求讨论:截图—涂鸦—分享

大家好,又到了新的一期项目需求分析。台下的观众举起手,让我看到你们。

同时我已经上传该项目:截屏及仿支付宝涂鸦功能

欢迎各位点个star哦。(⊙o⊙)


开始秋名山飘移之路

这个也是具体项目中遇到的项目需求:需要在一个特定的界面中(都是图表和各种数据,可能需求分享给别人,告诉别人这个数据怎么怎么,这个图表怎么怎么) 所以给我们开发的需求就是:

  1. 对该界面进行截屏
  2. 截屏后跳到涂鸦界面,可能某个数据圈出来,让别人一看就知道。(涂鸦,橡皮擦,撤销,清空操作)
  3. 分享给QQ,微信等等其他第三方软件。

功能上来说,其实没啥难度。各种第三方的,别人写好的涂鸦封装包也有很多。但是要么是不能随意定制化,要么就是写的也不是特别好,而且我更加提倡的是技术本身不是特别好的各位同僚们,还是宁可下载下来观看,然后自己试着去写一下,写一个自己的工具。可能写的没别人好,可能有点重复造轮子的意思,但是对于自己的提升还是很大的。当然大家可以提出意见。我写的也不见得很好。。(大牛请忽略这段话。哈哈)

具体步骤

1.界面截屏功能

因为当前我做的是截屏后把获得的Bitmap保存为本地图片,然后跳转到涂鸦界面,然后打开这个本地图片进行涂鸦处理。其他人可以根据自己的需求对这个截屏获取的Bitmap进行处理。

/**
 * 截屏并将图片保存到相应路径下
 * @param activity 当前需要截屏的activity
 * @param path 图片保存路径
 */
public static void SaveScreenShot(Activity activity,String path) {
    View view = activity.getWindow().getDecorView();
    view.setDrawingCacheEnabled(true);
    view.buildDrawingCache();
    Bitmap bitmap = view.getDrawingCache();

    FileOutputStream outputStream = null;
    try {
        outputStream = new FileOutputStream(path);
        bitmap.compress(Bitmap.CompressFormat.PNG,100,outputStream);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }finally {
        if(outputStream != null){
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        view.setDrawingCacheEnabled(false);
    }
}复制代码

核心的代码就是:

View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
view.setDrawingCacheEnabled(false);复制代码

2.涂鸦功能

核心的概念就是:自定义View(画板),然后在上面先画上我们截屏的图片,然后再在上面画上一层图片,我们的涂鸦其实也就是在上面一层图片画来画去。一脸懵逼?? 一定要逼我换个通俗易懂的???

比如画板是一个桌子,我们先在上面放了一个海报,然后我们再拿一个透明的图片(这里比如我们拿的是一块透明玻璃)盖在海报上面,然后我们拿个粉笔在玻璃上面画来画去,这时候来我们看来,是不是就等于海报被涂了各种颜色。比如我们要清空涂鸦。是不是只要把玻璃上面的涂鸦给擦光就行了。然后下面的海报就又变成了原来的样子。

所以我们这个涂鸦也要一步步来处理,因为在实际开发中会遇到各种问题,所以要一步步来。


如何把截图的图片,按照我们自定义的View(画板)的大小,等比例放进去。

可能有人要问了。为啥要等比例放进去,我们就直接把图片塞进去我们的画板不就OK了吗。 因为我们这次是全屏截图!!!而我们的画板肯定是比全屏的截图面积要小,因为下面有画笔、橡皮擦等其他功能菜单区域。 看效果(我故意把我的涂鸦画板的高度设置小很多):

所要截图的界面

截图不做调整直接放入

截图做了调整放入

这下大家懂了吧

OK,我们接下去就知道了。如果我们要在自定义的View(画板)上先放上去截图的图片,就要先把图片按比例处理好再放入。所以也就是说这个图片需要根据我们的画板的面积来做相应调整。

也有可能有人要问,如果我的需求不是截屏涂鸦,是打开内置的某个图片,然后画板反而比我的图片大怎么办 。没关系。这里教你们一招。不用在意图片跟画板哪个大哪个小。

1.获取图片的宽和高:bitmapWidth,bitmapHeight 2.获取画板的宽和高:boardWidth,boardHeight 3.如果bitmapWidth > boardWidth或者 bitmapHeight > boardHeight,则把图片缩小比例,来适应画板 4.否则就设置画板控件的宽和高等于图片的宽和高。既让boardWidth = bitmapWidth,boardHeight = bitmapHeight。我们反过来让画板来适应图片即可。

/**
 *
 * 图片过大则根据容器把原始图片改变大小。从而适应容器。
 * 否则改变画板大小适应图片
 *
 * @param bitmap
 * @param boardView
 * @return
 */
public static Bitmap resizeBitmap(Bitmap bitmap, View boardView) {

    int bitmapHeight = bitmap.getHeight();
    int bitmapWidth = bitmap.getWidth();
    int boardHeight = boardView.getHeight();
    int boardWidth = boardView.getWidth();

    float scale = 1f;
    if(bitmapHeight > boardHeight || bitmapWidth > boardWidth){
        scale = bitmapHeight > bitmapWidth
                ? boardHeight / (bitmapHeight * 1f)
                : boardWidth / (bitmapWidth * 1f);
    }

    Matrix matrix = new Matrix();
    matrix.postScale(scale, scale);
    Bitmap resizeBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, matrix, false);

    ViewGroup.LayoutParams params = boardView.getLayoutParams();
    params.height = resizeBitmap.getHeight();
    params.width = resizeBitmap.getWidth();
    boardView .setLayoutParams(params);

    return resizeBitmap;
}复制代码

添加第二层Canvas。用于涂鸦

我们上面拿到了截图的Bitmap,我们画上去后,还要画一层用于涂鸦的Bitmap,也就是我前面举例的那块透明玻璃。 也很简单。我们只要再新建一个Bitmap,大小等同于画板的大小,然后用Canvas包裹,让这个Bitmap能够具有画画的功能, 然后再把这个新的Bitmap让我们的画板画出来就可以了。 我们在自定义View 中写入一个方法:

/**
  * 设置背景图片及监理新的用来涂鸦的Bitmap
  * 
  * @param bitmap 传入的截图界面图片
  */
public void setBackgroud(Bitmap bitmap) {
    this.backgroudBitmap = bitmap;
    this.bitmap = Bitmap.createBitmap(backgroudBitmap.getWidth(), backgroudBitmap.getHeight(), Bitmap.Config.ARGB_8888);
    paintCanvas = new Canvas(this.bitmap);
}复制代码

这样我们把第一步处理好的截图图片传进来的时候。同时建一个和这个截图图片一样大小的图片,并且用Canvas包裹。这样等会我们就可以用这个paintCanvas在新的图片上面进行画画了。

我们在onDraw方法中也只要画这二个Bitmap就可以了。

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    if (backgroudBitmap != null && !backgroudBitmap.isRecycled()) {
        canvas.drawBitmap(backgroudBitmap, 0, 0, null);
    }

    if (bitmap != null && !bitmap.isRecycled()) {
        canvas.drawBitmap(bitmap, 0, 0, null);
    }
}复制代码

真正的涂鸦实现

真正的涂鸦功能,其实只是对我们的后面新建的Bitmap进行各种操作,也就是用上面的paintCanvas来进行画线等操作。

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            startX = event.getX();
            startY = event.getY();

            path = new Path();
            path.moveTo(startX, startY);

            break;

        case MotionEvent.ACTION_MOVE:
            endX = event.getX();
            endY = event.getY();
            path.quadTo(startX, startY, endX, endY);
            paintCanvas.drawPath(path, isEraser ? eraserPaint : paint);
            startX = endX;
            startY = endY;
            postInvalidate();
            break;

        case MotionEvent.ACTION_UP:
            drawPathList.add(new DrawPathEntry(path, isEraser ? eraserPaint.getColor() : paint.getColor(), isEraser));
            break;

        default:
            break;
    }
    return true;
}复制代码

切换涂鸦颜色功能

其实用不同颜色画笔来画,就是单纯的切换Paint的颜色值即可。

/**
 * 设置画笔颜色及橡皮擦
 *
 * @param type
 */
public void setPaintType(int type) {

    isEraser = false;
    switch (type) {
        case AnnotationConfig.PaintType.Paint_Red:
            paint.setColor(ContextCompat.getColor(context, R.color.red_radio));
            break;
        case AnnotationConfig.PaintType.Paint_Orange:
            paint.setColor(ContextCompat.getColor(context, R.color.orange_radio));
            break;
        case AnnotationConfig.PaintType.Paint_Yellow:
            paint.setColor(ContextCompat.getColor(context, R.color.yellow_radio));
            break;
        case AnnotationConfig.PaintType.Paint_Green:
            paint.setColor(ContextCompat.getColor(context, R.color.green_radio));
            break;
        case AnnotationConfig.PaintType.Paint_Blue:
            paint.setColor(ContextCompat.getColor(context, R.color.blue_radio));
            break;
        case AnnotationConfig.PaintType.Paint_Purple:
            paint.setColor(ContextCompat.getColor(context, R.color.purple_radio));
            break;
        case AnnotationConfig.PaintType.Paint_Eraser:
            isEraser = true;
            break;
        default:
            break;
    }
}复制代码

橡皮擦功能

其实橡皮擦功能,你可以理解就是在画一个透明的线,这个线盖在了你的其他的画好的线的上面。我们的目标就是他们二个交集的地方,让原本的线消失。 下表就是二个线交集的时候,不同Mode下呈现的情况。我们这里就可以用Clear模式。交集的地方,让底下的颜色消失就可以。

Xfermode

设置橡皮擦的Paint的Xfermode模式为Mode.Clear。

Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
eraserPaint.setXfermode(xfermode);复制代码

然后让这个earserPaint按照上面涂鸦的写法,让它画线就可以。


撤销功能

撤销其实也很简单,就是我们在画的时候,把每次画的Path和所画这个Path的画笔颜色保存下来,放在一个List集合里面,然后每次点撤销,就把List集合里面最后一个的Path给去掉,然后把二层Bitmap(也就是那个透明玻璃)清空,再把List里面的所有的Path按照其对应的画笔颜色画出来就可以了。

/**
 * 撤销操作
 */
public void cancelPath() {
    if (drawPathList != null && drawPathList.size() <= 0) {
        return;
    }
    drawPathList.remove(drawPathList.size() - 1);
    paintCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    for (DrawPathEntry entry : drawPathList) {
        paint.setColor(entry.getPaintColor());
        paintCanvas.drawPath(entry.getPath(), entry.isEraser() ? eraserPaint : paint);
    }
    postInvalidate();
}复制代码

清空涂鸦

清空涂鸦就比上面撤销功能更简单了。把二层Bitmap(也就是那个透明玻璃)清空,然后把我们的上面存Path的List给清空,这样撤销也就不会出现原来的涂鸦。

/**
 * 清空涂鸦
 */
public void clearScrawlBoard() {
    paintCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    drawPathList.clear();
    postInvalidate();
}复制代码

返回最终的涂鸦好的图片

新建一个Canvas。把最终的截图图片的bitmap和最终已经涂鸦好的二层Bitmap(透明玻璃)给画上去,然后获取最终的合成的Bitmap即可。

/**
 * @return 返回最终的涂鸦好的图片
 */
public Bitmap getSrawBoardBitmap() {
    Bitmap resultBitmap = Bitmap.createBitmap(backgroudBitmap.getWidth(), backgroudBitmap.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(resultBitmap);
    canvas.drawBitmap(backgroudBitmap, 0, 0, null);
    canvas.drawBitmap(bitmap, 0, 0, null);
    canvas.save();
    return resultBitmap;
}复制代码

3.图片分享功能

我们通过上面的getSrawBoardBitmap()方法获得了最后的合成的图片。然后分享到微信,QQ等第三方软件。我这边用的是友盟分享

友盟分享Android SDK

它只是帮你做了个封装,而各个第三方的分享都要去其相应的平台去申请Key。 比如你要分享到微信和QQ,就要分别取申请微信和QQ的key。然后再集成到友盟分享中去,才能使用。

UMImage image = new UMImage(DrawBaseActivity.this, bit);
new ShareAction(DrawBaseActivity.this).withText("hello").withMedia(image)
    .setDisplayList(SHARE_MEDIA.QQ,SHARE_MEDIA.WEIXIN,SHARE_MEDIA.ALIPAY)
    .setCallback(new UMShareListener() {
        @Override
        public void onStart(SHARE_MEDIA share_media) {

        }

        @Override
        public void onResult(SHARE_MEDIA share_media) {
            Toast.makeText(DrawBaseActivity.this, share_media + " 分享成功啦", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onError(SHARE_MEDIA share_media, Throwable throwable) {
            Toast.makeText(DrawBaseActivity.this,share_media + " 分享失败啦", Toast.LENGTH_SHORT).show();
            if(throwable!=null){
                Log.d("throw","throw:"+throwable.getMessage());
            }
        }

        @Override
        public void onCancel(SHARE_MEDIA share_media) {
            Toast.makeText(DrawBaseActivity.this,share_media + " 分享取消了", Toast.LENGTH_SHORT).show();
            }
    }).open();复制代码

最终

最后的最后,贴上相应的相关自定义View和涂鸦的所属的Activity的代码。大家也可以去我上面贴的Github地址中取下载demo。更方便查看:截屏及仿支付宝涂鸦功能

ScrawlActivity.java
package com.example.scrawldemo;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.IdRes;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.RadioGroup;
import android.widget.Toast;

import com.example.scrawldemo.config.AnnotationConfig;
import com.example.scrawldemo.util.BitmapUtil;
import com.example.scrawldemo.widget.ScrawlBoardView;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class ScrawlActivity extends AppCompatActivity {

    @BindView(R.id.color_group)
    RadioGroup colorGroup;
    @BindView(R.id.board_view)
    ScrawlBoardView boardView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        String path = getIntent().getStringExtra("path");
        final Bitmap bitmap = BitmapFactory.decodeFile(path);


        boardView.post(new Runnable() {
            @Override
            public void run() {
                final Bitmap resizeBitmap = BitmapUtil.resizeBitmap(bitmap,boardView);
                //final Bitmap resizeBitmap = bitmap;
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        boardView.setBackgroud(resizeBitmap);
                    }
                });
            }
        });

        colorGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
                switch (checkedId) {
                    case R.id.red_radio:
                        boardView.setPaintType(AnnotationConfig.PaintType.Paint_Red);
                        break;
                    case R.id.orange_radio:
                        boardView.setPaintType(AnnotationConfig.PaintType.Paint_Orange);
                        break;
                    case R.id.yellow_radio:
                        boardView.setPaintType(AnnotationConfig.PaintType.Paint_Yellow);
                        break;
                    case R.id.green_radio:
                        boardView.setPaintType(AnnotationConfig.PaintType.Paint_Green);
                        break;
                    case R.id.blue_radio:
                        boardView.setPaintType(AnnotationConfig.PaintType.Paint_Blue);
                        break;
                    case R.id.purple_radio:
                        boardView.setPaintType(AnnotationConfig.PaintType.Paint_Purple);
                        break;
                    case R.id.eraser_radio:
                        boardView.setPaintType(AnnotationConfig.PaintType.Paint_Eraser);
                        break;
                    default:
                        break;
                }
            }
        });
    }

    @OnClick({R.id.cancel, R.id.rubbish,R.id.finish_btn,R.id.send_btn})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.cancel:
                boardView.cancelPath();
                break;
            case R.id.rubbish:
                boardView.clearScrawlBoard();
                break;
            case R.id.finish_btn:
                finish();
                break;
            case R.id.send_btn:
                Bitmap bitmap = boardView.getSrawBoardBitmap();

                //该处的bitmap是涂鸦好的图片。
                //该例子中是把涂鸦好的图片保存到本地
                try {
                    FileOutputStream outputStream = new FileOutputStream(ScrawlActivity.this.getExternalFilesDir(Environment.DIRECTORY_PICTURES)+"/ChintScreenShot.png");
                    bitmap.compress(Bitmap.CompressFormat.PNG,100,outputStream);
                    Toast.makeText(this, "保存到本地成功", Toast.LENGTH_SHORT).show();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }

                //我分享用的是友盟分享。也就是把上面获取到的bitmap传入到相应的友盟分享中的方法即可
                /*
                UMImage image = new UMImage(ScrawlActivity.this, bitmap);
                new ShareAction(DrawBaseActivity.this).withText("hello").withMedia(image)
                        .setDisplayList(SHARE_MEDIA.QQ,SHARE_MEDIA.WEIXIN,SHARE_MEDIA.ALIPAY)
                        .setCallback(new UMShareListener() {
                            @Override
                            public void onStart(SHARE_MEDIA share_media) {

                            }

                            @Override
                            public void onResult(SHARE_MEDIA share_media) {
                                Toast.makeText(DrawBaseActivity.this, share_media + " 分享成功啦", Toast.LENGTH_SHORT).show();
                            }

                            @Override
                            public void onError(SHARE_MEDIA share_media, Throwable throwable) {
                                Toast.makeText(DrawBaseActivity.this,share_media + " 分享失败啦", Toast.LENGTH_SHORT).show();
                                if(throwable!=null){
                                    Log.d("throw","throw:"+throwable.getMessage());
                                }
                            }

                            @Override
                            public void onCancel(SHARE_MEDIA share_media) {
                                Toast.makeText(DrawBaseActivity.this,share_media + " 分享取消了", Toast.LENGTH_SHORT).show();
                            }
                        }).open();
                  */



                break;

            default:
                break;
        }
    }
}复制代码
ScrawlBoardView.java
package com.example.scrawldemo.widget;

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.graphics.Xfermode;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.example.scrawldemo.R;
import com.example.scrawldemo.bean.DrawPathEntry;
import com.example.scrawldemo.config.AnnotationConfig;

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

/**
 * Project:AndroidDemo
 * Author:dyping
 * Date:2017/4/11 10:36
 */

public class ScrawlBoardView extends View {

    Canvas paintCanvas;
    Paint paint, eraserPaint;
    Bitmap bitmap;
    Bitmap backgroudBitmap;

    float startX, startY, endX, endY;

    Context context;

    boolean isEraser;
    List<DrawPathEntry> drawPathList = new ArrayList<>();
    Path path;

    public ScrawlBoardView(Context context) {
        super(context);
    }

    public ScrawlBoardView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setFilterBitmap(true);
        paint.setColor(ContextCompat.getColor(context, R.color.red_radio));
        paint.setStrokeWidth(10);

        eraserPaint = new Paint();
        eraserPaint.setStyle(Paint.Style.STROKE);
        eraserPaint.setStrokeWidth(20);
        eraserPaint.setColor(Color.TRANSPARENT);
        Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
        eraserPaint.setXfermode(xfermode);

        this.context = context;
    }

    public ScrawlBoardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (backgroudBitmap != null && !backgroudBitmap.isRecycled()) {
            canvas.drawBitmap(backgroudBitmap, 0, 0, null);
        }

        if (bitmap != null && !bitmap.isRecycled()) {
            canvas.drawBitmap(bitmap, 0, 0, null);
        }
    }

    /**
     * 设置背景图片及监理新的用来涂鸦的Bitmap
     *
     * @param bitmap 传入的截图界面图片
     */
    public void setBackgroud(Bitmap bitmap) {
        this.backgroudBitmap = bitmap;
        this.bitmap = Bitmap.createBitmap(backgroudBitmap.getWidth(), backgroudBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        paintCanvas = new Canvas(this.bitmap);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startX = event.getX();
                startY = event.getY();

                path = new Path();
                path.moveTo(startX, startY);

                break;

            case MotionEvent.ACTION_MOVE:
                endX = event.getX();
                endY = event.getY();
                path.quadTo(startX, startY, endX, endY);
                paintCanvas.drawPath(path, isEraser ? eraserPaint : paint);
                startX = endX;
                startY = endY;
                postInvalidate();
                break;

            case MotionEvent.ACTION_UP:
                drawPathList.add(new DrawPathEntry(path, isEraser ? eraserPaint.getColor() : paint.getColor(), isEraser));
                break;

            default:
                break;
        }
        return true;
    }

    /**
     * 设置画笔颜色及橡皮擦
     *
     * @param type
     */
    public void setPaintType(int type) {

        isEraser = false;
        switch (type) {
            case AnnotationConfig.PaintType.Paint_Red:
                paint.setColor(ContextCompat.getColor(context, R.color.red_radio));
                break;
            case AnnotationConfig.PaintType.Paint_Orange:
                paint.setColor(ContextCompat.getColor(context, R.color.orange_radio));
                break;
            case AnnotationConfig.PaintType.Paint_Yellow:
                paint.setColor(ContextCompat.getColor(context, R.color.yellow_radio));
                break;
            case AnnotationConfig.PaintType.Paint_Green:
                paint.setColor(ContextCompat.getColor(context, R.color.green_radio));
                break;
            case AnnotationConfig.PaintType.Paint_Blue:
                paint.setColor(ContextCompat.getColor(context, R.color.blue_radio));
                break;
            case AnnotationConfig.PaintType.Paint_Purple:
                paint.setColor(ContextCompat.getColor(context, R.color.purple_radio));
                break;
            case AnnotationConfig.PaintType.Paint_Eraser:
                isEraser = true;
                break;
            default:
                break;
        }
    }

    /**
     * 撤销操作
     */
    public void cancelPath() {
        if (drawPathList != null && drawPathList.size() <= 0) {
            return;
        }
        drawPathList.remove(drawPathList.size() - 1);
        paintCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        for (DrawPathEntry entry : drawPathList) {
            paint.setColor(entry.getPaintColor());
            paintCanvas.drawPath(entry.getPath(), entry.isEraser() ? eraserPaint : paint);
        }
        postInvalidate();
    }


    /**
     * 清空涂鸦
     */
    public void clearScrawlBoard() {
        paintCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        drawPathList.clear();
        postInvalidate();
    }

    /**
     * @return 返回最终的涂鸦好的图片
     */
    public Bitmap getSrawBoardBitmap() {
        Bitmap resultBitmap = Bitmap.createBitmap(backgroudBitmap.getWidth(), backgroudBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(resultBitmap);
        canvas.drawBitmap(backgroudBitmap, 0, 0, null);
        canvas.drawBitmap(bitmap, 0, 0, null);
        canvas.save();
        return resultBitmap;
    }

}复制代码
BitmapUtil.java
package com.example.scrawldemo.util;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.view.View;
import android.view.ViewGroup;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Project:AndroidDemo
 * Author:dyping
 * Date:2017/4/11 10:40
 */

public class BitmapUtil {

    /**
     *
     * 图片过大则根据容器把原始图片改变大小。从而适应容器。
     * 否则改变画板大小适应图片
     *
     * @param bitmap
     * @param boardView
     * @return
     */
    public static Bitmap resizeBitmap(Bitmap bitmap, View boardView) {

        int bitmapHeight = bitmap.getHeight();
        int bitmapWidth = bitmap.getWidth();
        int boardHeight = boardView.getHeight();
        int boardWidth = boardView.getWidth();

        float scale = 1f;
        if(bitmapHeight > boardHeight || bitmapWidth > boardWidth){
            scale = bitmapHeight > bitmapWidth
                    ? boardHeight / (bitmapHeight * 1f)
                    : boardWidth / (bitmapWidth * 1f);
        }

        Matrix matrix = new Matrix();
        matrix.postScale(scale, scale);
        Bitmap resizeBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, matrix, false);

        ViewGroup.LayoutParams params = boardView.getLayoutParams();
        params.height = resizeBitmap.getHeight();
        params.width = resizeBitmap.getWidth();
        boardView .setLayoutParams(params);

        return resizeBitmap;
    }


    /**
     * 截屏并将图片保存到相应路径下
     *
     * @param activity 当前需要截屏的activity
     * @param path     图片保存路径
     */
    public static void SaveScreenShot(Activity activity, String path) {
        View view = activity.getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        Bitmap bitmap = view.getDrawingCache();

        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(path);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            view.setDrawingCacheEnabled(false);
        }
    }
}复制代码

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏封碎

Android放大镜的实现 博客分类: Android Android

快三个月了没写博客了,因为工作调动,很多经验、心得都没有时间记录下来。现在时间稍微充裕了点,我会尽量抽时间将之前想写而没写的东西补上。进入正题。     ...

30810
来自专栏Sorrower的专栏

动画必须有(二):悬浮菜单了解一下!

29530
来自专栏开发之途

Android ImageView的Tint属性

21940
来自专栏developerHaoz 的安卓之旅

Android 带你撸一个好玩的 DoodleView(涂鸦)

可以看到这个这个自定义 View 的功能还是很丰富的,无论是设置画笔的形状、颜色、粗细,还是进行重置和保存,该有的 API,基本都已经实现了。有需要的读者直接 ...

12130
来自专栏向治洪

android自定义xmls文件属性

在使用到自定义View的xml布局文件中需要加入xmlns:前缀=http://schemas.android.com/apk/res/你的自定义View所在...

21350
来自专栏移动开发之家

Android 列表视频的全屏、自动小窗口优化实践

上例牌 github>>>>>>>> https://github.com/CarGuo 对,就是这个郭老司机。

33850
来自专栏程序员叨叨叨

【Android】手把手教你上滑解锁的效果

最近,公司开发的APP中要实现类似上滑解锁效果的推荐页,捣腾了两天,基本实现了效果,附效果图如上。接下来和大家聊聊如何实现这样的效果。

60020
来自专栏项勇

笔记11 | 动态设置TextView的字体大小

23260
来自专栏Android干货园

Android源码解析-仿今日头条PagerSlidingTabStrip滑动页面导航效果

版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/48...

52120
来自专栏小灰灰

Android 之游戏开发流程

一、概述 刚开始接触Android平台,之前也没有游戏开发经验,因此对于如何开发一款游戏没有思路,而且也不知道如何对整个项目进行模块划分。在学习连连看的教程时,...

21460

扫码关注云+社区

领取腾讯云代金券