編輯:關於Android編程
最近項目中有個需求,就是進行圖片的裁剪。
裁剪分為兩種方式:1.矩形框裁剪 2.手勢裁剪
在手勢裁剪的過程中遇到一個問題,就是圖片裁剪之後,背景不是透明的,下面給出我的解決方案。
@SuppressLint("DrawAllocation")
public class CropPictureView extends ImageView {
private float density;
private Paint mPaint;
private Path mCirclePath;
private Paint mImagePaint;
private Path mFreehandPath;
private Paint mPaintBitmap;
private final Matrix mCircleatrix = new Matrix();
// 放大鏡的半徑
private static int RADIUS = 160;
// 放大倍數
private static final int FACTOR = 1;
// 裁剪保留的bitmap
private List bitmaps = new ArrayList();
/**
* 一次剪切手勢動作
*/
private boolean isTouchArea = false;
private int mViewWidth = 0;
private int mViewHeight = 0;
private float scale = 1.0f;
private float diff = 0.0f;
private Bitmap sourceBitmap = null;
public enum ViewType {
RECTTYPE, PATHTYPE
}
private ViewType mViewType = ViewType.RECTTYPE;
public void setmViewType(ViewType mViewType) {
this.mViewType = mViewType;
}
public CropPictureView(Context context) {
super(context);
init();
}
public CropPictureView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CropPictureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
density = getContext().getResources().getDisplayMetrics().density;
RADIUS = dipToPx(160);
diff = diff;
setFocusable(true);
DRAW_STATUS = 0;
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(diff);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.RED);
mImagePaint = new Paint();
mImagePaint.setAntiAlias(true);
mImagePaint.setStrokeWidth(diff);
mImagePaint.setStyle(Paint.Style.STROKE);
mImagePaint.setColor(Color.BLACK);
mCirclePath = new Path();
mCirclePath.addRect(diff, diff, RADIUS + diff, RADIUS + diff, Direction.CW);
// mCirclePath.addCircle(RADIUS, RADIUS, RADIUS, Direction.CW);
mCircleatrix.setScale(FACTOR, FACTOR);
mFrameRect = new RectF();
mFreehandPath = new Path();
mPaintBitmap = new Paint();
mPaintBitmap.setFilterBitmap(true);
}
private Bitmap bm;
// 重寫該方法,在這裡繪圖
@SuppressLint({ "DrawAllocation", "NewApi" })
@Override
protected void onDraw(Canvas mcanvas) {
// super.onDraw(canvas);
if (getDrawable() == null) {
return;
}
// 生成畫布圖像
bm = Bitmap.createBitmap(mViewWidth, mViewHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bm);// 使用空白圖片生成canvas
canvas.save();
canvas.translate(mImageRect.left, mImageRect.top);
canvas.drawBitmap(getBitmap(), mCircleatrix, mPaintBitmap);
canvas.restore();
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG));
canvas.drawRect(mImageRect, mImagePaint);
if (DRAW_STATUS == 2) {
if (!isTouchArea) {
return;
}
if (mViewType == ViewType.RECTTYPE) {
drawRect(canvas);
move(canvas);
drawRect(canvas);
} else {
canvas.drawPath(mFreehandPath, mPaint);
move(canvas);
canvas.drawPath(mFreehandPath, mPaint);
}
} else if (DRAW_STATUS == 3) {
if (mViewType == ViewType.PATHTYPE) {
mFreehandPath.close();
mFrameRect = new RectF();
mFreehandPath.computeBounds(mFrameRect, true);
}
// 如果畫的矩形太小就不進行剪切
if (Math.hypot(mFrameRect.width(), mFrameRect.height()) < 50) {
isTouchArea = false;
} else {
if (mViewType == ViewType.PATHTYPE) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.DST_IN);
canvas.clipPath(mFreehandPath);
canvas.translate(mImageRect.left, mImageRect.top);
PaintFlagsDrawFilter dfd = new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG, Paint.FILTER_BITMAP_FLAG);
canvas.setDrawFilter(dfd);
canvas.drawBitmap(getBitmap(), mCircleatrix, null);
}
up();
return;
}
} else if (DRAW_STATUS == 0) {
} else if (DRAW_STATUS == 1) {
}
mcanvas.drawBitmap(bm, 0, 0, mPaintBitmap);
}
private void move(Canvas canvas) {
// 剪切
canvas.clipPath(mCirclePath);
// 畫放大後的圖
canvas.translate(RADIUS / 2 - endX * FACTOR + mImageRect.left, RADIUS / 2 - endY * FACTOR + mImageRect.top);
canvas.drawBitmap(getBitmap(), mCircleatrix, null);
canvas.translate(-mImageRect.left, -mImageRect.top);
canvas.drawRect(mImageRect, mImagePaint);
}
/**
* dip 轉換成px
*
* @param dip
* @return
*/
private int dipToPx(float dip) {
return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
}
/**
* 一次裁剪動作完成
*/
public void up() {
DRAW_STATUS = 0;
Bitmap tempBitmap = getCropImage();
bitmaps.add(tempBitmap);
setImageBitmap(tempBitmap);
}
float startX = 0;
float startY = 0;
float endX = 0;
float endY = 0;
public static int DRAW_STATUS = 0;// 0,初始狀態、1點擊狀態、2移動狀態、3抬起狀態
private RectF mFrameRect = new RectF();
private RectF mImageRect = new RectF();
/**
* 初始化邊框
*/
public void initRect() {
startX = 0;
startY = 0;
endX = 0;
endY = 0;
mFrameRect = new RectF();
if (mFreehandPath != null) {
mFreehandPath.reset();
} else {
mFreehandPath = new Path();
}
}
/**
* 畫矩形邊框
*
* @param canvas
*/
public void drawRect(Canvas canvas) {
if (endX > startX) {
mFrameRect.left = (int) startX;
mFrameRect.right = (int) endX;
} else {
mFrameRect.left = (int) endX;
mFrameRect.right = (int) startX;
}
if (endY > startY) {
mFrameRect.top = (int) startY;
mFrameRect.bottom = (int) endY;
} else {
mFrameRect.top = (int) endY;
mFrameRect.bottom = (int) startY;
}
mFrameRect.setIntersect(mFrameRect, mImageRect);
canvas.drawRect(mFrameRect, mPaint);
}
private Bitmap getCovertBitmap() {
Bitmap tmpBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Config.RGB_565);
Canvas canvas = new Canvas(tmpBitmap);
Drawable tempDrawable = getDrawable();
tempDrawable.setBounds(new Rect(Math.round(mImageRect.left), Math.round(mImageRect.top), Math.round(mImageRect.right), Math.round(mImageRect.bottom)));
tempDrawable.draw(canvas);
return tmpBitmap;
}
/**
*
* @param bitmap
* @param w
* @param h
* @return
*/
public Bitmap resizeBitmap(Bitmap bitmap, int w, int h) {
if (bitmap != null) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int newWidth = w;
int newHeight = h;
float scaleWight = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWight, scaleHeight);
return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
} else {
return null;
}
}
private Bitmap getBitmap() {
Bitmap bm = null;
Drawable d = getDrawable();
if (d != null && d instanceof BitmapDrawable)
bm = ((BitmapDrawable) d).getBitmap();
return bm;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
int antion = event.getAction();
if (antion == MotionEvent.ACTION_CANCEL) {
return true;
}
float touchX = event.getX();
float touchY = event.getY();
// 點擊時
if (antion == MotionEvent.ACTION_DOWN) {
initRect();
startX = touchX;
startY = touchY;
endX = touchX;
endY = touchY;
mFreehandPath.moveTo(touchX, touchY);
DRAW_STATUS = 1;
if (mImageRect.contains(startX, startY)) {
isTouchArea = true;
} else {
isTouchArea = false;
}
invalidate();
return true;
}
// 拖動時
if (antion == MotionEvent.ACTION_MOVE) {
if (mViewType == ViewType.PATHTYPE) {
if (mImageRect.contains(touchX, touchY)) {
touchMove(event);
// mFreehandPath.lineTo(touchX, touchY);
}
}
endX = touchX;
endY = touchY;
DRAW_STATUS = 2;
invalidate();
return true;
}
// 抬起時
if (antion == MotionEvent.ACTION_UP) {
endX = touchX;
endY = touchY;
DRAW_STATUS = 3;
invalidate();
return true;
}
return super.onTouchEvent(event);
}
// 手指在屏幕上滑動時調用
private void touchMove(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final float previousX = endX;
final float previousY = endY;
final float dx = Math.abs(x - previousX);
final float dy = Math.abs(y - previousY);
// 兩點之間的距離大於等於3時,生成貝塞爾繪制曲線
if (dx >= 3 || dy >= 3) {
// 設置貝塞爾曲線的操作點為起點和終點的一半
float cX = (x + previousX) / 2;
float cY = (y + previousY) / 2;
// 二次貝塞爾,實現平滑曲線;previousX, previousY為操作點,cX, cY為終點
mFreehandPath.quadTo(previousX, previousY, cX, cY);
// 第二次執行時,第一次結束調用的坐標值將作為第二次調用的初始坐標值
}
}
// 進行圖片的裁剪,所謂的裁剪就是根據Drawable的新的坐標在畫布上創建一張新的圖片
private Bitmap getCropImage() {
if (mFrameRect.width() <= 0 || mFrameRect.height() <= 0) {
isTouchArea = false;
return null;
}
// this has a error: java.lang.IllegalArgumentException: x + width must
// be <=bitmap.width()
int x = Math.round(mFrameRect.left);
int y = Math.round(mFrameRect.top);
int w = Math.round(mFrameRect.width());
int h = Math.round(mFrameRect.height());
if (x + w > bm.getWidth()) {
w = bm.getWidth() - x;
}
if (y + h > bm.getHeight()) {
h = bm.getHeight() - y;
}
return Bitmap.createBitmap(bm, x, y, w, h, null, true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int viewWidth = MeasureSpec.getSize(widthMeasureSpec);
final int viewHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(viewWidth, viewHeight);
mViewWidth = viewWidth - getPaddingLeft() - getPaddingRight();
mViewHeight = viewHeight - getPaddingTop() - getPaddingBottom();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (getDrawable() != null)
setupLayout(mViewWidth, mViewHeight);
}
/**
* 重新設置view的圖片
*/
public void setupLayout(int viewW, int viewH) {
if (viewW == 0 || viewH == 0)
return;
float imgWidth = getDrawable().getIntrinsicWidth();
float imgHight = getDrawable().getIntrinsicHeight();
float viewRatio = (float) viewW / (float) viewH;
float imgRatio = imgWidth / imgHight;
if (imgRatio >= viewRatio) {
scale = (float) viewW / imgWidth;
} else if (imgRatio < viewRatio) {
scale = (float) viewH / imgHight;
}
float w = imgWidth * scale;
float h = imgHight * scale;
float left = (viewW - w) / 2;
float top = (viewH - h) / 2;
float right = left + w;
float bottom = top + h;
mImageRect = new RectF(left, top, right, bottom);
mCircleatrix.setScale(FACTOR * scale, FACTOR * scale);
}
public Bitmap getCropBitmap() {
if (bitmaps != null && bitmaps.size() > 0) {
return bitmaps.get(bitmaps.size() - 1);
} else {
return getBitmap();
}
}
public void retroversion() {
LogUtil.e("撤銷tupain = " + bitmaps.size());
if (bitmaps != null && bitmaps.size() > 1) {
setImageBitmap(bitmaps.get(bitmaps.size() - 2));
invalidate();
bitmaps.remove(bitmaps.size() - 1);
}
if (bitmaps.size() == 0) {
bitmaps.add(sourceBitmap);
}
}
/**
* Set source image bitmap
*
* @param bitmap
* src image bitmap
*/
@Override
public void setImageBitmap(Bitmap bitmap) {
super.setImageBitmap(bitmap); // calles setImageDrawable internally
if (sourceBitmap == null) {
sourceBitmap = bitmap;
}
if (sourceBitmap != null && bitmaps.size() == 0) {
bitmaps.add(sourceBitmap);
}
}
/**
* Set source image resource id
*
* @param resId
* source image resource id
*/
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
updateLayout();
}
/**
* Set image drawable.
*
* @param drawable
* source image drawable
*/
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
updateLayout();
}
/**
* Set image uri
*
* @param uri
* source image local uri
*/
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
updateLayout();
}
private void updateLayout() {
Drawable d = getDrawable();
if (d != null) {
setupLayout(mViewWidth, mViewHeight);
}
}
private Bitmap getTransBitmap(Bitmap bm) {
int[] pix = new int[bm.getWidth() * bm.getHeight()];
for (int y = 0; y < bm.getHeight(); y++)
for (int x = 0; x < bm.getWidth(); x++) {
int index = y * bm.getWidth() + x;
int r = ((pix[index] >> 16) & 0xff) | 0xff;
int g = ((pix[index] >> 8) & 0xff) | 0xff;
int b = (pix[index] & 0xff) | 0xff;
pix[index] = 0xff000000 | (r << 16) | (g << 8) | b;
}
bm.setPixels(pix, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
return Bitmap.createBitmap(bm);
}
}
(代碼比較亂)
上面分為兩種裁剪方式:RECTTYPE–矩形, PATHTYPE–手勢路徑。
解決背景不是透明的方式:
if (mViewType == ViewType.PATHTYPE) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.DST_IN);
canvas.clipPath(mFreehandPath);
canvas.translate(mImageRect.left, mImageRect.top);
PaintFlagsDrawFilter dfd = new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG, Paint.FILTER_BITMAP_FLAG);
canvas.setDrawFilter(dfd);
canvas.drawBitmap(getBitmap(), mCircleatrix, null);
}
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.DST_IN);
黑色粗體位置就是設置裁剪的背景顏色和層疊方式的,PorterDuff.Mode的具體方式如下:
從上面我們可以看到PorterDuff.Mode為枚舉類,一共有16個枚舉值:
1.PorterDuff.Mode.CLEAR
所繪制不會提交到畫布上。
2.PorterDuff.Mode.SRC
顯示上層繪制圖片
3.PorterDuff.Mode.DST
顯示下層繪制圖片
4.PorterDuff.Mode.SRC_OVER
正常繪制顯示,上下層繪制疊蓋。
5.PorterDuff.Mode.DST_OVER
上下層都顯示。下層居上顯示。
6.PorterDuff.Mode.SRC_IN
取兩層繪制交集。顯示上層。
7.PorterDuff.Mode.DST_IN
取兩層繪制交集。顯示下層。
8.PorterDuff.Mode.SRC_OUT
取上層繪制非交集部分。
9.PorterDuff.Mode.DST_OUT
取下層繪制非交集部分。
10.PorterDuff.Mode.SRC_ATOP
取下層非交集部分與上層交集部分
11.PorterDuff.Mode.DST_ATOP
取上層非交集部分與下層交集部分
12.PorterDuff.Mode.XOR
異或:去除兩圖層交集部分
13.PorterDuff.Mode.DARKEN
取兩圖層全部區域,交集部分顏色加深
14.PorterDuff.Mode.LIGHTEN
取兩圖層全部,點亮交集部分顏色
15.PorterDuff.Mode.MULTIPLY
取兩圖層交集部分疊加後顏色
16.PorterDuff.Mode.SCREEN
取兩圖層全部區域,交集部分變為透明色
另外還有一個就是:圖片保存到本地背景是黑色的,這裡要將保存格式改為png:
* bm.compress(Bitmap.CompressFormat.PNG, 90, baos);*
public static InputStream bitmapToStream(Bitmap bm) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 90, baos);
InputStream is = new ByteArrayInputStream(baos.toByteArray());
return is;
}
一起來開發Android的天氣軟件(二)
謝謝大家對該系列博文的支持與關注,我們現在趁熱打鐵正式開始我們的Android天氣軟件的開發吧!沒有閱讀過之前關於該軟件的功能需求的同學可以先看一下 一起來開發Andro
Android利用ObjectAnimator實現ArcMenu
本文介紹利用ObjectAnimator簡單地實現ArcMenu,直接使用本文的ArcMenu類即可快捷地實現菜單功能。 最終使用效果:先看下最終的使用效果:
Android MediaStore檢索視頻並播放
該文章是為了檢索手機上sd卡中的視頻,然後將檢索出來的相應視頻的縮略圖,名稱等視頻信息顯示在ListView上。點擊每個item後播放相應的視頻。 源代碼: 布局文件
Android中View的布局及繪圖機制
為了研究Android中View的布局及繪圖機制,我創建了一個非常簡單的App,該App只有一個Activity,該Activity對應的layout如下所示: 該