綜述
圖片選取&拍照篇鏈接 細(xì)數(shù)圖片上傳功能用到的知識(shí)點(diǎn)-圖片選取&拍照篇
我們先來(lái)看最終效果
其中涉及到的知識(shí)點(diǎn)
- 蒙版的繪制
- 手勢(shì)拖動(dòng),手勢(shì)放大
- 圖片的matrix操作
- 圖片的裁剪
下面我就對(duì)這每個(gè)知識(shí)點(diǎn)進(jìn)行詳細(xì)的說(shuō)明
蒙版的繪制
可以看到界面上覆蓋了一層半透明的蒙版追葡,中間扣出了一個(gè)正方形的區(qū)域四濒,為了使邊界更加明顯我又繪制了一個(gè)白色的正方形的線框坎缭。
實(shí)現(xiàn)方式自定義View 重寫ondraw
方法繪圖作為背景篷扩。利用PorterDuffXfermode
中的XOR
模式扣掉中間的矩形區(qū)域
//矩形區(qū)域的坐標(biāo)可根據(jù)你的需求定制
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
canvas.drawRect(mleft, mtop, mright, mbottom, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
paint.setColor(Color.parseColor("#FFFFFF"));
paint.setStrokeWidth(2f);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(mleft - 2, mtop - 2, mright + 2, mbottom + 2, paint);
手勢(shì)拖動(dòng)與手勢(shì)放大
這里有個(gè)業(yè)務(wù)需求变汪,無(wú)論圖片怎么樣變化都必須保證圖片的邊界在矩形區(qū)域之外仪吧。另外結(jié)合我對(duì)于圖片展示的需求啊送,我決定自定義一個(gè)處理這些業(yè)務(wù)的imageView
死遭。
先說(shuō)拖動(dòng)手勢(shì)的獲取广恢。重寫onTouchEvent
方法。記錄回傳坐標(biāo)的變化呀潭,move事件中獲得x和y的偏移量钉迷。拖動(dòng)所需的數(shù)據(jù)很簡(jiǎn)單就能拿到。而手勢(shì)放大需要我們支持多點(diǎn)觸控的記錄蜗侈。要想獲取的多點(diǎn)觸控的數(shù)據(jù)篷牌,需要根據(jù)event.getAction() & MotionEvent.ACTION_MASK
來(lái)判斷action ,之后獲取當(dāng)action
為MotionEvent.ACTION_POINTER_DOWN
時(shí)的觸摸事件踏幻,該事件即為第二只手指的事件枷颊。
那么如何在move
事件中區(qū)分放大和拖動(dòng)呢?我們這個(gè)時(shí)候就需要一個(gè)狀態(tài)量來(lái)記錄當(dāng)前手勢(shì)狀態(tài)该面。回調(diào)ACTION_DOWN
后為拖動(dòng)狀態(tài)夭苗。回調(diào)ACTION_POINTER_DOWN
之后則為放大狀態(tài)隔缀。
//mode 為狀態(tài)量题造,記錄手勢(shì)狀態(tài)
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//拖動(dòng)
mode = DRAG;
currentMaritx.set(this.getImageMatrix());
startPoint.set(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
//拖動(dòng)
if (mode == DRAG) {
dragDo(event);
}
//放大
else if (mode == ZOOM) {
zoomDo(event);
}
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_POINTER_DOWN:
//判斷第二個(gè)手指與第一個(gè)手指的位置。設(shè)置閾值避免一個(gè)手指兩個(gè)觸摸點(diǎn)的情況猾瘸。
startDis = distance(event);
if (startDis > 10f) {
mode = ZOOM;
midPoint = mid(event);
currentMaritx.set(this.getImageMatrix());
}
break;
}
return true;
dragDo()
函數(shù)能夠拿到手指拖動(dòng)偏移量界赔,處理圖片拖動(dòng)事件丢习。而zoomDo()
可以根據(jù)兩個(gè)點(diǎn)距離變化倍率獲得應(yīng)該放大的倍率
//獲取兩個(gè)手的距離
float endDis = distance(event);
//當(dāng)前距離除以初始距離
float scale = endDis / startDis;
private float distance(MotionEvent event) {
float dx = event.getX(1) - event.getX(0);
float dy = event.getY(1) - event.getY(0);
return (float) Math.sqrt(dx * dx + dy * dy);
}
圖片的matrix操作
圖片的移動(dòng)和放大等操作,我們選擇利用imageView的matrix屬性進(jìn)行改變淮悼。
Matrix matrix=getImageMatrix();
//偏移操作咐低,參數(shù)為想x,y的偏移量
matrix.postTranslate(realdx, realdy);
//縮放操作袜腥,參數(shù)為 x见擦,y的放大量,和縮放中心
matrixteamp.postScale(scale, scale, midPoint.x, midPoint.y);
別忘了羹令,我們有業(yè)務(wù)需求鲤屡,要保證圖片的邊界在矩形區(qū)域之外。
首先我們要對(duì)傳入的圖片進(jìn)行預(yù)處理福侈,因?yàn)閳D片一開(kāi)始的位置就可能出現(xiàn)越界的情況酒来,另外我們需要合理的放大縮小圖片來(lái)讓用戶的裁剪功能更加的快捷。
//圖片位置預(yù)制
private void PreDealPic() {
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
if ((float) (bitmapWidth) / bitmapHeight - ViewMaxWidth/ ViewMaxHeight > 0) {
// 寬圖 寬/高 大于視圖的寬/高
float bili = ViewMaxWidth/ bitmapWidth;
//超寬圖 寬占滿后癌刽,圖片上下邊越界情況
if (bitmapHeight * bili < maxHeight) {
//放大至
float bei = maxWidth / (float) bitmapHeight;
final Matrix matrix = new Matrix();
matrix.postScale(bei , bei );
//將其移動(dòng)到邊界
matrix.postTranslate(left , top);
moveImage.setScaleType(ImageView.ScaleType.MATRIX);
moveImage.setImageMatrix(matrix);
}
} else {
//長(zhǎng)圖 寬/高 小于視圖的寬/高
float bili = ViewMaxHeight/ bitmapHeight;
//超長(zhǎng)圖 高占滿后役首,圖片左右邊越界情況
if (bitmapWidth * bili < maxWidth) {
//放大至
float bei = maxWidth /bitmapWidth;
Matrix matrix = new Matrix();
matrix.postScale(bei , bei );
//移動(dòng)到邊界
matrix.postTranslate(left, top);
moveImage.setScaleType(ImageView.ScaleType.MATRIX);
moveImage.setImageMatrix(matrix);
}
}
我們先來(lái)說(shuō)拖動(dòng),直接將偏移量傳參給偏移函數(shù)即可显拜。讓用戶直接拖動(dòng)是無(wú)法避免越界操作的衡奥。如此我們便需要對(duì)手勢(shì)后圖片的坐標(biāo)進(jìn)行判斷,若本次操作會(huì)導(dǎo)致越界远荠,則不予執(zhí)行矮固。但僅僅這樣是不夠的,如果總是不執(zhí)行越界操作的話譬淳,那么圖片拖動(dòng)到邊界會(huì)留下一個(gè)小區(qū)域拖動(dòng)不過(guò)去(拖過(guò)去就越界了)档址。這樣的用戶體驗(yàn)并不友好。我們應(yīng)該判斷邻梆,如果本次操作會(huì)導(dǎo)致越界守伸,那么我們只需要將圖片移動(dòng)到邊界即可。
要判斷是否越界浦妄,我們需要拿到當(dāng)前圖片四個(gè)角的坐標(biāo)尼摹。正確的獲取方式為
//獲取圖片四個(gè)坐標(biāo)
RectF rectF = new RectF();
//傳遞bitmap本身的寬高
rectF.right = bitmapWidth;
rectF.bottom = bitmapHeight;
matrixteamp.mapRect(rectF);
既然我們拿到了四個(gè)角坐標(biāo)那么就可以們判斷圖片是否越界了
//判斷圖片是否越界
if (maxleft > left && maxtop > top && right > maxright && bottom > maxbottom) {
//執(zhí)行拖動(dòng)操作
}
else
{
//修正拖動(dòng)
transLateDragToRight();
}
//矯正拖動(dòng)位置
private void transLateDragToRight(float left, float right, float bottom, float top, Matrix matrix, RectF currentRect) {
float realdx = dx;
float realdy = dy;
if (maxleft < left) {
realdx = maxleft-currentRect.left;
}
if (maxtop < top) {
realdy = maxtop - currentRect.top;
}
if (right < maxright) {
realdx = maxright-currentRect.right;
}
if (bottom < maxbottom) {
realdy = maxbottom - currentRect.bottom;
}
matrix.postTranslate(realdx, realdy);
}
放大也是同樣的道理,需要進(jìn)行修正剂娄。但注意放大與拖動(dòng)存在不同蠢涝,如果縮小后存在有一個(gè)邊小于限制大小,那么位移修正是沒(méi)有意義的阅懦。我們需要對(duì)其不操作和二,或者再次放大。
所以需要判斷
if (right - left >= maxright - maxleft && bottom - top >= maxbottom - maxtop)
//矯正放大位置
//矯正放大位置
private void transLateToRight(float left, float right, float bottom, float top, Matrix matrix) {
float realdx = dx;
float realdy = dy;
if (maxleft < left) {
realdx = maxleft - left;
}
if (maxtop < top) {
realdy = maxtop - top;
}
if (right < maxright) {
realdx = -right + maxright;
}
if (bottom < maxbottom) {
realdy = maxbottom - bottom;
}
matrix.postTranslate(realdx, realdy);
}
這樣處理過(guò)后即便圖片緊靠邊界耳胎,也能夠進(jìn)行縮小操作惯吕。直到縮小會(huì)導(dǎo)致有一個(gè)邊小于限制惕它。能夠提供最佳的圖片裁剪操作。
圖片的裁剪
最后我們需要處理用戶點(diǎn)擊完成后的裁剪事件,裁剪事件我選擇了獲取當(dāng)前跟view的視圖废登,獲取截圖怠缸,并根據(jù)我的限制坐標(biāo)對(duì)圖片進(jìn)行裁剪。
//我的根布局
relativeLayout.setDrawingCacheEnabled(true);
relativeLayout.buildDrawingCache();
Bitmap bitmap = relativeLayout.getDrawingCache();
// 傳入left top right bottom 來(lái)創(chuàng)建新的圖片钳宪。
Bitmap realfinBitamp = Bitmap.createBitmap(bitmap, (int) (picCultBackground.getMleft()), (int) (picCultBackground.getMtop()), (int) (picCultBackground.getMwidth()), (int) (picCultBackground.getMheight()));
bitmap.recycle();
relativeLayout.setDrawingCacheEnabled(false);
realfinBitamp 就是用戶裁剪得到的圖片扳炬。根據(jù)業(yè)務(wù)需求再對(duì)bitmap 進(jìn)行處理吧吏颖。
到這里我們已經(jīng)看完了裁剪功能的所有重要知識(shí)點(diǎn)和代碼。但還要親自嘗試才能夠明白各種細(xì)節(jié)的緣由恨樟。
細(xì)數(shù)圖片上傳功能用到的知識(shí)點(diǎn)(圖片選取&拍照篇)
細(xì)數(shù)圖片上傳功能用到的知識(shí)點(diǎn)(裁剪篇)
細(xì)數(shù)圖片上傳功能用到的知識(shí)點(diǎn)(圖片壓縮篇)