細(xì)數(shù)圖片上傳功能用到的知識(shí)點(diǎn)(裁剪篇)

綜述

圖片選取&拍照篇鏈接 細(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ū)域

PorterDuffXfermode 各種模式

       //矩形區(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)actionMotionEvent.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)(圖片壓縮篇)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末半醉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子劝术,更是在濱河造成了極大的恐慌缩多,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件养晋,死亡現(xiàn)場(chǎng)離奇詭異衬吆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)绳泉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門逊抡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人零酪,你說(shuō)我怎么就攤上這事冒嫡。” “怎么了四苇?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵孝凌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我月腋,道長(zhǎng)蟀架,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任罗售,我火速辦了婚禮辜窑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寨躁。我一直安慰自己穆碎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布职恳。 她就那樣靜靜地躺著所禀,像睡著了一般方面。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上色徘,一...
    開(kāi)封第一講書(shū)人閱讀 52,682評(píng)論 1 312
  • 那天恭金,我揣著相機(jī)與錄音,去河邊找鬼褂策。 笑死横腿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的斤寂。 我是一名探鬼主播耿焊,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼遍搞!你這毒婦竟也來(lái)了罗侯?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤溪猿,失蹤者是張志新(化名)和其女友劉穎钩杰,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體诊县,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡讲弄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了依痊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垂睬。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖抗悍,靈堂內(nèi)的尸體忽然破棺而出驹饺,到底是詐尸還是另有隱情,我是刑警寧澤缴渊,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布赏壹,位于F島的核電站,受9級(jí)特大地震影響衔沼,放射性物質(zhì)發(fā)生泄漏蝌借。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一指蚁、第九天 我趴在偏房一處隱蔽的房頂上張望菩佑。 院中可真熱鬧,春花似錦凝化、人聲如沸稍坯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瞧哟。三九已至混巧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間勤揩,已是汗流浹背咧党。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留陨亡,地道東北人傍衡。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像负蠕,于是被迫代替她去往敵國(guó)和親聪舒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容