android 自定义控件实现截图框功能

     毕业3个月了吧, 自己的第一篇博客,记录自己学到的知识,我话比较少,要是写得不好请见谅。

    在之前接触图片美化的知识,有加边框,贴纸,滤镜,截图等功能,这里介绍图片的截图,这个功能挺简单的,主要是截图框效果的实现。看下效果图:

 


   实现思路重写控件的onDraw函数 ,在画布上进行绘制想要的效果,这里需要绘制的是两个部分一个是截图框外的的黑色半透明部分,一个是截图框部分。这里的截图框可以用一个Rect类来处理,首先目标是获取截图框外的部分,很简单,通过画布的clipRect方法,获取画布与rect的交集的补集,截图框部分同理,具体怎么获取看下面代码;然后就是处理rect的数值变换,再通过重绘达到移动 ,放大,缩小的效果,下面是代码:

        

package com.example.arze.cutview;

import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.PathEffect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.widget.ImageView;

/**
 * Created by arze on 2015/11/1.
 */
public class CutImageView extends ImageView {
    /**
     * 图片的大小
     */
    private int mBitmapWidth;
    private int mBitmapHeight;

    /**
     * 控件的大小
     *
     * @param context
     */
    private int mWidth;
    private int mHeight;

    /**
     * 图片在控件上的放大倍数
     */
    private double mScaleX;
    private double mScaleY;
    /**
     * 用来处理截图框的变换
     */
    private RectF mFloatRect;
    /**
     * 截图框的初始数值
     */
    private RectF mDefaultRect;
    /**
     * 截图框外部的默认颜色
     */
    private final int mDefaultColor = Color.parseColor("#a0000000");
    /**
     * 点击和移动的点
     */
    private float mTouchX;
    private float mTouchY;
    private float mMoveX;
    private float mMoveY;
    /**
     * 一些常量
     */
    private static final int TOPLEFT = 0x0001;
    private static final int TOPRIGHT = 0x0002;
    private static final int BOTTOMLEFT = 0x0003;
    private static final int BOTTOMRIGHT = 0x0004;

    private static final int BOTHSIDE = 0x0005;
    //  private static final int ONESIDE = 0x0006;
    private static final int MOVE = 0x0006;

    /**
     * 判断截图框的改变方式是移动还是缩放
     */
    private int mChangeType;
    /**
     * 判断点击的四个角
     */
    private int mTouch;
    /**
     * xml属性
     */
    private int mFloatColor;


    public CutImageView(Context context) {
        super(context);
        init(context, null, 0);
    }

    public CutImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public CutImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CutView);
        if (null != typedArray) {
            mFloatColor = typedArray.getColor(R.styleable.CutView_floatColor, mDefaultColor);
            typedArray.recycle();
        }
    }

    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        mBitmapWidth = bm.getWidth();
        mBitmapHeight = bm.getHeight();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 计算控件的大小就不用说了
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
            mWidth = MeasureSpec.getSize(widthMeasureSpec);
            mHeight = MeasureSpec.getSize(heightMeasureSpec);
        } else {
            setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
            mWidth = getMeasuredWidth();
            mHeight = getMeasuredHeight();
        }
        if (0 != mBitmapWidth && 0 != mWidth) {
            setScale();
        }
    }

    /**
     * 计算bitmap在控件上的放大倍数目前只写了FIT_CENTER,
     * 其它的可以自己写,不知有没有其它直接的方法获取
     */
    private void setScale() {
        if (ScaleType.FIT_CENTER == getScaleType()) {
            double widthScale = (double) mWidth / (double) mBitmapWidth;
            double heightScale = (double) mHeight / (double) mBitmapHeight;
            if (mBitmapWidth <= mWidth && mBitmapHeight <= mHeight) {
                mScaleX = mScaleY = (widthScale > heightScale ? heightScale : widthScale);
            } else if (mBitmapWidth <= mWidth && mBitmapHeight > mHeight) {
                mScaleX = mScaleY = heightScale;
            } else if (mBitmapWidth > mWidth && mBitmapHeight <= mHeight) {
                mScaleX = mScaleY = widthScale;
            } else if (mBitmapWidth > mWidth && mBitmapHeight > mHeight) {
                mScaleX = mScaleY = (widthScale > heightScale ? heightScale : widthScale);
            }
            setFloatRect();
        }
    }

    /**
     * 计算截图框的大小
     */
    private void setFloatRect() {
        float width = mBitmapWidth * (float) mScaleX;
        float height = mBitmapHeight * (float) mScaleY;
        float left = (mWidth - width) / 2;
        float top = (mHeight - height) / 2;
        float right = left + width;
        float bottom = top + height;
        mFloatRect = new RectF(left, top, right, bottom);
        mDefaultRect = new RectF(left, top, right, bottom);

    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (null != mFloatRect) {
            Paint paint = new Paint();
            paint.setColor(mFloatColor);
            // 绘制截图框外部部分
            canvas.save();
            //Region.Op.XOR 是异并集,这里是获取截图框外部部分
            canvas.clipRect(mFloatRect, Region.Op.XOR);
            canvas.drawColor(mFloatColor);
            canvas.restore();
            canvas.save();
            //Region.Op.INTERSECT 是交集
            canvas.clipRect(mFloatRect, Region.Op.INTERSECT);
            // 绘制截图框部分
            drawFloatRect(canvas);
            canvas.restore();
        }
    }

    /**
     * 绘制截图框部分
     *
     * @param canvas
     */

    private void drawFloatRect(Canvas canvas) {
        Paint paint = new Paint();
        paint.setColor(Color.parseColor("#ffffffff"));
        paint.setStrokeWidth(15);
        // 外边框
        float[] points = {mFloatRect.left, mFloatRect.top, mFloatRect.right, mFloatRect.top,
                mFloatRect.right, mFloatRect.top, mFloatRect.right, mFloatRect.bottom,
                mFloatRect.right, mFloatRect.bottom, mFloatRect.left, mFloatRect.bottom,
                mFloatRect.left, mFloatRect.bottom, mFloatRect.left, mFloatRect.top};
        canvas.drawLines(points, paint);
        PathEffect effects = new DashPathEffect(new float[]{5, 5}, 1);
        paint.setPathEffect(effects);
        paint.setStrokeWidth(1);
        float vx = (mFloatRect.right - mFloatRect.left) / 3;
        float vy = (mFloatRect.bottom - mFloatRect.top) / 3;
        // 四条虚线,要画出虚线需要执行setLayerType(LAYER_TYPE_SOFTWARE, null);方法
        float[] dashPoints = {mFloatRect.left + vx, mFloatRect.top, mFloatRect.left + vx, mFloatRect.bottom,
                mFloatRect.left + 2 * vx, mFloatRect.top, mFloatRect.left + 2 * vx, mFloatRect.bottom,
                mFloatRect.left, mFloatRect.top + vy, mFloatRect.right, mFloatRect.top + vy,
                mFloatRect.left, mFloatRect.top + 2 * vy, mFloatRect.right, mFloatRect.top + 2 * vy,};
        canvas.drawLines(dashPoints, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mTouchX = event.getX();
                mTouchY = event.getY();
                jugmentChangeType();
                break;
            case MotionEvent.ACTION_UP:
                mChangeType = 0;
                mTouch = 0;
                break;
            case MotionEvent.ACTION_MOVE:
                mMoveX = event.getX();
                mMoveY = event.getY();
                setFloatRectChange();
                mTouchX = mMoveX;
                mTouchY = mMoveY;
                break;
        }
        return true;
    }

    /**
     * 判断截图框的改变方式,若是缩放需要判断点击的是哪个角
     */
    private void jugmentChangeType() {
        RectF rectF1 = new RectF(mFloatRect.left + 50, mFloatRect.top, mFloatRect.right - 50, mFloatRect.bottom);
        RectF rectF2 = new RectF(mFloatRect.left, mFloatRect.top + 50, mFloatRect.right, mFloatRect.bottom - 50);
        if (rectF1.contains(mTouchX, mTouchY) || rectF2.contains(mTouchX, mTouchY)) {
            mChangeType = MOVE;
        } else {
            mChangeType = BOTHSIDE;
            RectF rectFLeftTop = new RectF(mFloatRect.left - 50, mFloatRect.top - 50, mFloatRect.left + 50, mFloatRect.top + 50);
            RectF rectFRightTop = new RectF(mFloatRect.right - 50, mFloatRect.top - 50, mFloatRect.right + 50, mFloatRect.top + 50);
            RectF rectFLeftBottom = new RectF(mFloatRect.left - 50, mFloatRect.bottom - 50, mFloatRect.left + 50, mFloatRect.bottom + 50);
            RectF rectFRightBottom = new RectF(mFloatRect.right - 50, mFloatRect.bottom - 50, mFloatRect.right + 50, mFloatRect.bottom + 50);
            if (rectFLeftTop.contains(mTouchX, mTouchY)) {
                mTouch = TOPLEFT;
            } else if (rectFRightTop.contains(mTouchX, mTouchY)) {
                mTouch = TOPRIGHT;
            } else if (rectFLeftBottom.contains(mTouchX, mTouchY)) {
                mTouch = BOTTOMLEFT;
            } else if (rectFRightBottom.contains(mTouchX, mTouchY)) {
                mTouch = BOTTOMRIGHT;
            }
        }
    }

    /**
     * 截图框的数值变换
     */
    private void setFloatRectChange() {
        if (mChangeType == MOVE) {
            float x = mMoveX - mTouchX;
            float y = mMoveY - mTouchY;
            if (mDefaultRect.left <= mFloatRect.left + x && mDefaultRect.right >= mFloatRect.right + x) {
                mFloatRect.left += x;
                mFloatRect.right += x;
            } else if (mDefaultRect.left > mFloatRect.left + x && mDefaultRect.right >= mFloatRect.right + x) {
                float offx = mDefaultRect.left - mFloatRect.left;
                mFloatRect.left = mDefaultRect.left;
                mFloatRect.right += offx;
            } else if (mDefaultRect.left <= mFloatRect.left + x && mDefaultRect.right < mFloatRect.right + x) {
                float offx = mDefaultRect.right - mFloatRect.right;
                mFloatRect.right = mDefaultRect.right;
                mFloatRect.left += offx;
            }
            if (mDefaultRect.top <= mFloatRect.top + y && mDefaultRect.bottom >= mFloatRect.bottom + y) {
                mFloatRect.bottom += y;
                mFloatRect.top += y;
            } else if (mDefaultRect.top > mFloatRect.top + y && mDefaultRect.bottom >= mFloatRect.bottom + y) {
                float offy = mDefaultRect.top - mFloatRect.top;
                mFloatRect.top = mDefaultRect.top;
                mFloatRect.bottom += offy;
            } else if (mDefaultRect.top <= mFloatRect.top + y && mDefaultRect.bottom < mFloatRect.bottom + y) {
                float offy = mDefaultRect.bottom - mFloatRect.bottom;
                mFloatRect.bottom = mDefaultRect.bottom;
                mFloatRect.top += offy;
            }
        } else if (mChangeType == BOTHSIDE) {
            float x = mMoveX - mTouchX;
            float y = mMoveY - mTouchY;
            if (mTouch == TOPLEFT) {
                if (mDefaultRect.left <= mFloatRect.left + x && mFloatRect.right >= mFloatRect.left + x)
                    mFloatRect.left += x;
                if (mDefaultRect.top <= mFloatRect.top + y && mFloatRect.bottom >= mFloatRect.top + y)
                    mFloatRect.top += y;

            } else if (mTouch == TOPRIGHT) {
                if (mDefaultRect.right >= mFloatRect.right + x && mFloatRect.left <= mFloatRect.right + x)
                    mFloatRect.right += x;
                if (mDefaultRect.top <= mFloatRect.top + y && mFloatRect.bottom >= mFloatRect.top + y)
                    mFloatRect.top += y;

            } else if (mTouch == BOTTOMLEFT) {
                if (mDefaultRect.left <= mFloatRect.left + x && mFloatRect.right >= mFloatRect.left + x)
                    mFloatRect.left += x;
                if (mDefaultRect.bottom >= mFloatRect.bottom + y && mFloatRect.top <= mFloatRect.bottom + y)
                    mFloatRect.bottom += y;

            } else if (mTouch == BOTTOMRIGHT) {
                if (mDefaultRect.right >= mFloatRect.right + x && mFloatRect.left <= mFloatRect.right + x)
                    mFloatRect.right += x;
                if (mDefaultRect.bottom >= mFloatRect.bottom + y && mFloatRect.top <= mFloatRect.bottom + y)
                    mFloatRect.bottom += y;
            }
        }
        invalidate();
    }

    /**
     * drawable转化为bitmap
     *
     * @param drawable
     * @return
     */
    public Bitmap creatBitmapFromDrawable(Drawable drawable) {
        if (null == drawable)
            return null;
        if (drawable instanceof BitmapDrawable)
            return ((BitmapDrawable) drawable).getBitmap();
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicHeight(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }

    /**
     * 获取截图的bitmap 这个方法是在图片的原图上处理的,也可以通过截图的方式,不过截图的方式
     * 获取的图片大小是有偏差的。
     *
     * @return
     */
    public Bitmap getCutBitmap() {
        Drawable drawable = getDrawable();
        if (null == drawable)
            return null;
        Bitmap bitmap = creatBitmapFromDrawable(drawable);
        // 计算截图框在原图片所在的位置大小,通过比例可以算出
        float left = (mFloatRect.left - mDefaultRect.left) / (float) mScaleX;
        float top = (mFloatRect.top - mDefaultRect.top) / (float) mScaleY;
        float width = mFloatRect.width() / (float) mScaleX;
        float height = mFloatRect.height() / (float) mScaleY;
        Bitmap dstBitmap = Bitmap.createBitmap(bitmap, (int) left, (int) top, (int) width, (int) height);
        return dstBitmap;
    }
}

       上面是代码 ,在使用的时候直接使用就行了 ,要注意的是在传入bitmap的时候要考虑它的大小,比如一个3M的图片转化成bitmap就几十M了 ,所以在转化bitmap时要采用先算宽高,通过缩小获取bitmap,这样可以避免OOM.

       补上自定义属性,只有一个,额外的可以自己加。

        

<resources>
    <declare-styleable name="CutView">
        <attr name="floatColor" format="color"/>
    </declare-styleable>
</resources>
       

      

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值