毕业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>