編輯:關於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 Listbitmaps = 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天氣軟件的開發吧!沒有閱讀過之前關於該軟件的功能需求的同學可以先看一下 一起來開發Andro
本文介紹利用ObjectAnimator簡單地實現ArcMenu,直接使用本文的ArcMenu類即可快捷地實現菜單功能。 最終使用效果:先看下最終的使用效果:
該文章是為了檢索手機上sd卡中的視頻,然後將檢索出來的相應視頻的縮略圖,名稱等視頻信息顯示在ListView上。點擊每個item後播放相應的視頻。 源代碼: 布局文件
為了研究Android中View的布局及繪圖機制,我創建了一個非常簡單的App,該App只有一個Activity,該Activity對應的layout如下所示: 該