亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android圖片裁剪功能實(shí)現(xiàn)代碼

 更新時(shí)間:2021年09月18日 10:34:46   作者:湖廣午王  
這篇文章主要為大家詳細(xì)介紹了Android圖片裁剪功能實(shí)現(xiàn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

在Android應(yīng)用中,圖片裁剪也是一個(gè)經(jīng)常用到的功能。Android系統(tǒng)中可以用隱式意圖調(diào)用系統(tǒng)應(yīng)用進(jìn)行裁剪,但是這樣做在不同的手機(jī)可能表現(xiàn)出不同的效果,甚至在某些奇葩手機(jī)上還會(huì)出其他更奇怪的問題,所以調(diào)用系統(tǒng)功能進(jìn)行圖片裁剪在很多時(shí)候?qū)ξ覀儊碚f并不是一個(gè)好的選擇。這時(shí)候就需要我們自己去實(shí)現(xiàn)這種裁剪功能了。

功能分析

要完成圖片裁剪的功能,我們需要先知道圖片裁剪的功能有哪些。圖片裁剪之前,我們需要有一個(gè)框指示我們需要裁剪的樣式合大小。圖片顯示出來后大小和位置可能并不是我們所期望的,所以我們還需要對(duì)圖片進(jìn)行移動(dòng)、縮放等操作。確定好位置和大小后,我們需要真正的對(duì)圖片進(jìn)行裁剪,并將裁剪的圖片存起來以供使用。也就是說需要實(shí)現(xiàn)圖片裁剪的功能細(xì)分后如下:

1、顯示指示框
2、圖片移動(dòng)和縮放
3、圖片裁剪并保存

最終效果展示如下:

功能實(shí)現(xiàn)

顯示指示框

要實(shí)現(xiàn)顯示一個(gè)如上圖一樣的指示框有很多方法,這里實(shí)現(xiàn)的方式是用自定義的Drawable作為View的背景,然后將這個(gè)View覆蓋在原圖片上作為指示框。為了在一定程度上滿足更多的要求,我們讓指示框可設(shè)置為矩形也可設(shè)置為圓形,陰影區(qū)域的顏色也可設(shè)置。
要繪制出作為指示的圖層,我們可以將它拆分成兩半,變成兩個(gè)封閉的Path進(jìn)行繪制,也可以先繪制出半透明的覆蓋層,然后在中間裁剪一個(gè)洞。顯然,要考慮到這個(gè)洞的形狀大小并不是固定的,裁剪的方式比拆分成兩個(gè)封閉的Path要簡單多了。
Canvas的canvas.clipPath(Path, Region.Op);方法,可以對(duì)Canvas進(jìn)行裁剪,可以很容易得到這樣的指示框。然而Canvas的clipPath裁剪出來的曲線圖形會(huì)有鋸齒,我多番嘗試都沒能去掉鋸齒,所以不得不放棄這個(gè)方法。繼而利用paint的paint.setXfermode(new PorterDuffXfermode(mode))方法來實(shí)現(xiàn)這個(gè)效果。
Android 4.4的Path增加了裁剪功能,我們可以直接用Path的path.op(Path, Path.Op)方法將Path裁剪成我們需要的形狀再進(jìn)行繪制,這種方式效率更高。

指示框Drawable的核心代碼如下:

@Override
public void draw(@NonNull Canvas canvas) {
 int cWidth=canvas.getWidth();
 int cHeight=canvas.getHeight();
 if(rect==null){
 rect=new Rect(cWidth/2-width/2,cHeight/2-height/2,cWidth/2+width/2,cHeight/2+height/2);
 }
 canvas.drawColor(Color.TRANSPARENT);
 Path path=new Path();
 path.addRect(0,0,cWidth,cHeight, Path.Direction.CW);
 cropPath=new Path();
 if(shape==SHAPE_RECT){
 cropPath.addRect(rect.left,rect.top,rect.right,rect.bottom, Path.Direction.CW);
 }else if(shape==SHAPE_CIRCLE){
 cropPath.addCircle(rect.centerX(),rect.centerY(),rect.width()/2, Path.Direction.CW);
 }
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
 //可以抗鋸齒
 path.op(cropPath, Path.Op.DIFFERENCE); 
 canvas.drawPath(path,paint);
 }else{
 //此方法可以去掉鋸齒
 //在這里saveLayer然后restoreToCount的操作不能少,否則不會(huì)得到想要的效果
 int layerId = canvas.saveLayer(0, 0, cWidth, cHeight, null, Canvas.ALL_SAVE_FLAG);
 canvas.drawPath(path,paint);
 //已經(jīng)繪制的可以看做為目標(biāo)圖
 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
 canvas.drawPath(cropPath,paint);
 paint.setXfermode(null);
 canvas.restoreToCount(layerId);
 //裁剪的方式會(huì)有鋸齒,沒找到方法去掉鋸齒
 //canvas.clipPath(opPath, Region.Op.DIFFERENCE);
 //canvas.drawRect(0,0,cWidth,cHeight,paint);
 }
}

圖片移動(dòng)和縮放

圖片的移動(dòng)和縮放功能,在預(yù)覽看大圖的時(shí)候也會(huì)用到,在進(jìn)行圖片裁剪時(shí),我們需要對(duì)圖片的移動(dòng)和縮放范圍進(jìn)行限定,禁止圖片操作完成后出現(xiàn)超出指示框(根據(jù)需求也有在操作過程中就不允許超出指示框的情況)。ImageView有一個(gè)ImageView.setImageMatrix(Matrix)方法,可以直接設(shè)置圖片的變換矩陣。所以我們也可以利用這個(gè)方法,結(jié)合ImageView的OnTouchListener監(jiān)聽,來做圖片的移動(dòng)和縮放處理。
移動(dòng)縮放核心代碼如下:

@Override
public boolean onTouch(View v, MotionEvent event) {

 if(v!=null&&((ImageView) v).getDrawable()!=null){
 ImageView view = (ImageView) v;
 Rect rect=view.getDrawable().getBounds();
 //事件處理
 switch (event.getAction() & MotionEvent.ACTION_MASK) {
 //一個(gè)手指按下時(shí),標(biāo)記為移動(dòng)模式
 case MotionEvent.ACTION_DOWN:
 matrix.set(view.getImageMatrix());
 savedMatrix.set(matrix);
 start.set(event.getX(), event.getY());
 mode = DRAG;
 break;
 //第二個(gè)手指按下時(shí),標(biāo)記為縮放模式
 case MotionEvent.ACTION_POINTER_DOWN:
 oldDist = distance(event);
 if (oldDist > 10f) {
  savedMatrix.set(matrix);
  midPoint(mid, event);
  mode = ZOOM;
 }
 break;
 case MotionEvent.ACTION_UP:
 case MotionEvent.ACTION_POINTER_UP:
 checkMatrix(rect);
 mode = NONE;
 break;
 //手指移動(dòng)時(shí),根據(jù)當(dāng)前是移動(dòng)模式還是縮放模式做相應(yīng)處理
 case MotionEvent.ACTION_MOVE:
 if (mode == DRAG) {
  matrix.set(savedMatrix);
  matrix.postTranslate(event.getX() - start.x, event.getY()
  - start.y);
 } else if (mode == ZOOM) {
  float newDist = distance(event);
  if (newDist > 10f) {
  matrix.set(savedMatrix);
  float scale = newDist / oldDist;
  matrix.postScale(scale, scale, mid.x, mid.y);
  }
 }
 break;
 }
 view.setImageMatrix(matrix);
 }
 return true;
}

當(dāng)手指抬起時(shí),我們需要對(duì)圖片當(dāng)前狀態(tài)進(jìn)行判斷,避免在限定范圍中存在無圖片區(qū)域:

private void checkMatrix(Rect rect){
 if(limit==null&&cropPath!=null){
 limit=cropPath.limit();
 }
 if(limit!=null){
 if(mode==ZOOM){
 matrix.getValues(values);
 if(rect.width()*values[0]<limit.width()){ //當(dāng)前寬度小于最小寬度
 float scale = limit.width()/(float)rect.width()/values[0];
 matrix.postScale(scale, scale, mid.x, mid.y);
 }
 matrix.getValues(values);
 if(rect.height()*values[4]<limit.height()){ //當(dāng)前高度小于最小高度
 float scale = limit.height()/(float)rect.height()/values[4];
 matrix.postScale(scale, scale, mid.x, mid.y);
 }
 }
 matrix.getValues(values);
 if(values[2]>=limit.left){
 matrix.postTranslate(limit.left-values[2],0);
 }
 matrix.getValues(values);
 if(values[2]+rect.width()*values[0]<limit.right){
 matrix.postTranslate(limit.right-rect.width()*values[0]-values[2],0);
 }
 matrix.getValues(values);
 if(values[5]>limit.top){
 matrix.postTranslate(0,limit.top-values[5]);
 }
 matrix.getValues(values);
 if(values[5]+rect.height()*values[4]<limit.bottom){
 matrix.postTranslate(0,limit.bottom-rect.height()*values[4]-values[5]);
 }
 }
}

裁剪和保存

將圖片縮放移動(dòng)操作到我們預(yù)期的大小和位置后,我們就可以將出現(xiàn)在指示框內(nèi)的區(qū)域裁剪出來了。我們有兩種方式,將這個(gè)區(qū)域裁剪出來,一種是對(duì)原圖進(jìn)行裁剪,另外一種是對(duì)ImageView展示出來的圖片進(jìn)行裁剪。當(dāng)原圖過大或者圖片是網(wǎng)絡(luò)圖片等情況時(shí),對(duì)原圖裁剪并不是我們所期望的,而且相對(duì)直接對(duì)ImageView展示的內(nèi)容進(jìn)行裁剪,對(duì)原圖進(jìn)行裁剪還需要我們?nèi)ビ?jì)算我們所期望的區(qū)域在原圖上的位置。所以我們還是直接對(duì)ImageView展示出來的圖片進(jìn)行裁剪,然后得到裁剪結(jié)果比較方便。當(dāng)然,如果這個(gè)裁剪本來就是希望對(duì)原圖進(jìn)行處理的話,那就只能裁剪原圖了。

View有View.getDrawingCache()的方法,可以得到當(dāng)前View展示的內(nèi)容,它返回一個(gè)Bitmap。需要注意的是,在使用View.getDrawingCache()前,我們需要調(diào)用View.setDrawingCacheEnabled(true)來開啟繪制緩存,否則無法得到當(dāng)前的View所展示的內(nèi)容。使用完畢后,再調(diào)用View.setDrawingCacheEnabled(false)關(guān)閉繪制緩存,否則下次調(diào)用View.getDrawingCache()時(shí),得到的是之前的內(nèi)容。

裁剪的核心代碼:

public Bitmap crop(){
 if(imageView!=null&&cropPath!=null){
 if(limit==null){
 limit=cropPath.limit();
 }
 Paint paint=new Paint();
 paint.setAntiAlias(true);
 imageView.setDrawingCacheEnabled(true);
 Bitmap bmp=Bitmap.createBitmap(limit.width(),limit.height(), Bitmap.Config.ARGB_8888);
 Canvas canvas=new Canvas(bmp);
 canvas.drawColor(Color.TRANSPARENT);
 int lId=canvas.saveLayer(0,0,limit.width(),limit.height(),null,Canvas.ALL_SAVE_FLAG);
 Path path=new Path();
 path.addPath(cropPath.path(),-limit.left,-limit.top);
 canvas.drawPath(path,paint);
 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
 canvas.drawBitmap(imageView.getDrawingCache(),-limit.left,-limit.top,paint);
 paint.setXfermode(null);
 canvas.restoreToCount(lId);
 imageView.setDrawingCacheEnabled(false);
 return bmp;
 }
 return null;
}

裁剪后,將結(jié)果保存到指定目錄:

public String cropAndSave(String path) throws IOException {
 Bitmap bmp=crop();
 if(bmp==null)return null;
 File file=new File(path);
 if(!file.getParentFile().exists()){
 boolean b=file.mkdirs();
 if(!b)return null;
 }
 if(file.exists()){
 boolean c=file.delete();
 if(!c)return null;
 }
 FileOutputStream fos=new FileOutputStream(file);
 bmp.compress(Bitmap.CompressFormat.PNG,100,fos);
 fos.flush();
 fos.close();
 bmp.recycle();
 return file.getAbsolutePath();
}

源碼

博客中代碼片段的完整類,以代碼段的形式放在了CSDN代碼筆記中,有需要的朋友自行建立工程使用相關(guān)類。

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論