圖片的圓角處理

萬惡的國內(nèi)設(shè)計,都喜歡偏向于ios的扁平化風格,這里涉及到很多原因,國內(nèi)的網(wǎng)路環(huán)境導致大家無法使用國外的優(yōu)秀app;長期的ios一家獨大以及前喬布斯時代的老本至今仍未吃完給ios用戶和蹩腳的應(yīng)用設(shè)計者一發(fā)強心劑,讓他們以為ios的風格就是最好的風格;硬件的審美被同樣帶到了軟件中。
在此不排除扁平化風格存在一定的情景優(yōu)勢,但作為Material Design的擁護者,希望大家可以去如下地址下載個把國外的應(yīng)用把玩一番,打開國門,看看外面的世界,外面的風格。
2017 Google Play Awards:https://play.google.com/store/info/topic?id=merch_topic_30028d2_playwards2017_nomineesTP

以上純屬牢騷,扁平化狂熱者請繞道。。。
今天看到一個ios跟主管表明自己對于擬物風格的向往和對扁平風格的厭惡。。


言歸正傳,由于ios帶來的圓角風格,導致產(chǎn)品對著我指手畫腳,想讓我把前人未完成的圓角圖片坑給添上,做出如下的效果:

請拿走陰影,走你的扁平化

如果下面的兩個圓角你不會處理,請先移步Android背景圓角的實現(xiàn)

很明顯這是一個拼接的控件,你可以使用dialog,可以使用activity去實現(xiàn),這不是重點,重點是上方的圓角圖片怎樣去實現(xiàn)。

實現(xiàn)方式無非兩種,一種是自定義view實現(xiàn),一種是直接通過動態(tài)實現(xiàn)。但是兩者的宗旨是一樣的

自定義view實現(xiàn)

既然是圖片,那么很顯然,最先想到的肯定是ImageView,這里我們就去自定義一個ImageView

  • 創(chuàng)建自定義類
public class RoundedImageView extends ImageView {  
  public RoundedImageView(Context context) {  
      super(context);  
  }  

  public RoundedImageView(Context context, AttributeSet attrs) {  
      super(context, attrs);  
  }  

  public RoundedImageView(Context context, AttributeSet attrs, int defStyleAttr) {  
      super(context, attrs, defStyleAttr);  
  }  
}  

我記得我在面試寶典的文章中指明了自定義view的一些注意點,如上是必須實現(xiàn)的三種構(gòu)造方法,當然在新api中會出現(xiàn)第四種,此處寫三種即可。

  • 先去搞個模具
    想想一下,你要做一個臉盆,可能你先想到的不是去直接打造,而是想著先去制造一個模具,然后我們只需要往里面澆灌就可以得到我們的臉盆。
    而這個圓角恰巧也可能通過這種方式來實現(xiàn),我們就先來看下如何去搞一個圓角的模具。

在此原諒我認為你是一個完全不具備java環(huán)境下繪畫基礎(chǔ)的或者說只具備一丟丟的開發(fā)者。

在Android的graphics包下有許多幾何繪圖相關(guān)的類,我們平時見得比較多的諸如Canvas,Paint都在這個類下面,平時我們會使用canvas參數(shù)傳遞給畫筆Paint,做一些字體的繪制等等,我們通常設(shè)置它的粗細,大小,鋸齒等等之后交給canvas對象來進行繪畫。

在paint的源碼搜索了一番發(fā)現(xiàn)并沒有出現(xiàn)round關(guān)鍵字的方法,這個類基本宣告無法實現(xiàn)圓角的東西。抱著幾何包中肯定存在實現(xiàn)圓角的類和函數(shù)的執(zhí)著,決定繼續(xù)在graphic包中搜尋。

   好了注意了,接下去要進入到詭異的自圓其說了。。。

很慶幸的在graphic包下發(fā)現(xiàn)了Path類,顧名思義就是路線類,感覺很有可能能跟圓角的東西掛上鉤,讓我們?nèi)ピ创a里面看一下。。。
然后你會發(fā)現(xiàn)它有包含round的方法名addRoundRect。。(不知各位看官覺得這一段接的硬么)

我們看一下,總共有四個方法

  public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,
          Direction dir) {
     ...
  }
 public void addRoundRect(RectF rect, float[] radii, Direction dir) {
      ...
  }
public void addRoundRect(float left, float top, float right, float bottom, float[] radii,
          Direction dir) {
    ...
  }
public void addRoundRect(RectF rect, float rx, float ry, Direction dir) {
      ...
  }
   我打算不花費長篇大論,去談?wù)撜嬲那懈钸壿嫛?   兩個層面,疊加,然后切割得到圓角啊什么的,過于乏味,面向初學者的話很難堅持讀完。

上面四種方法,直接上白話解釋,

  • 第一種,貌似參數(shù)最全,分別傳入需要畫圓角的圖片view的大小,此處lefttop傳入0即可,這邊的四個參數(shù)表示的僅僅是區(qū)域大小,而非被切割I(lǐng)mageView的區(qū)域。
    float rx, float ry分別表示,在x軸和y軸上的圓角切割度數(shù), 因為實際上圓角切割我們涉及到兩個方向,一個是左右方向,x方向,一個是上下方向,y方向。這里我們傳入兩個8.0f就行了。
    最后的Direction表示曲線的閉合方向,這里由于我們只是畫圓角曲線,并不涉及到曲線上的文字,那么隨便傳哪個都一樣,直接傳入Direction.CW即可
  • 第二種,直接傳入包含l,t,r,b的rect對象,和radii數(shù)組,以及Direction對象,如何去理解這個叫radii的float數(shù)組,我們看到在具體的方法中,有這么一段
```

if (radii.length < 8) {
throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
}

  再看看,方法注釋
 * @param radii Array of 8 values, 4 pairs of [X,Y] radii
   很顯然,只能傳size為8的float數(shù)組,分別是4對[X,Y]的角度切割度數(shù),那么很容易理解了,這個數(shù)組可以然我們對Rect的四個角且出不同的角度。
   * 第三種和第四種同上,自己YY吧。

  好了,我們開始畫曲線

final RectF rectF = new RectF(0, 0, bitmapWidth, bitmapHeight);
int radius = getResources().getDimensionPixelSize(R.dimen.radius);
Path path = new Path();
path.addRoundRect(rectF, radius, radius, Path.Direction.CW);

 先new一個Path對象出來,并且傳入所需要切割的范圍大小,以及切割圓角角度,以及方向參數(shù)。

* 曲線畫完了,怎么切割
      我們已經(jīng)把模具準備好了,那么接下去我們就開始要準備澆灌了,我們還需要兩樣東西,機床和原料。
   原料很明顯就是ImageView的Bitmap本身,機床是什么呢?

   實際上我們在繪制中最終使用的繪制操作都是來自于Canvas類,這回也不例外,機床就是`Canvas`類。
   * 先把機床拿出來
Bitmap result = Bitmap.createBitmap(imageviewWidth, viewHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
    因為機床創(chuàng)建需要傳入Bitmap對象,因此此處我們就直接偽造一個Bitmap,大小為ImageView的大小,配置這種無所謂,傳入32位即可。
   
    * 把原料整理好
     ```
     Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
獲得了直角矩形的原料。
原料.png
* 將模具再整理一下
 我們之前已經(jīng)得到了模具Path,現(xiàn)在我們需要將它安放到機床上去(在這里吧上面的代碼又鐵了一遍,省的你再往上翻了)
final RectF rectF = new RectF(0, 0, bitmapWidth, bitmapHeight);
int radius = getResources().getDimensionPixelSize(R.dimen.radius);
Path path = new Path();  
path.addRoundRect(rectF, radius, radius, Path.Direction.CW);
canvas.clipPath(path);
這樣一來,我們就在機床上擺上了圓角的模型。
圓角模具.png
* 開動機床,開始切割
```        

Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
canvas.drawBitmap(bitmap, rect, rectF, paint);

    這里還需要傳入畫筆,因為切割鋸齒以及bitmap填充需要使用到paint來實現(xiàn)。
    這樣一來,result就是我們所需要的自帶圓角的bitmap了
    
![切割.png](http://upload-images.jianshu.io/upload_images/1305996-b3b3f2c4a76be84b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![切割細節(jié).png](http://upload-images.jianshu.io/upload_images/1305996-52535f1c30273acc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

   > 如果你看到現(xiàn)在還是一頭霧水,既然你真心誠意的問了,那我就大發(fā)慈悲的再來幫你梳理一遍(火箭隊?)
   * 首先搞一個空的bitmap,大小傳入你需要的尺寸,質(zhì)量32位即可
   * 將這個空的bitmap放到機床canvas上
   * 獲取真實圖片的矩形rect
   * 獲取圓角模具的矩形rectF
   * 創(chuàng)建畫筆Paint,控制鋸齒和圖形填充(理解成機床上的潤滑劑比較不錯)
   * 機床按動開關(guān),通過drawBitmap方法將bitmap繪制成我們需要圓角bitmap

![切圓角流程.png](http://upload-images.jianshu.io/upload_images/1305996-ea6c2721d7f7c0a7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
   
   那么我們直接在自定義ImageView的onDraw方法中做一下手腳,

@Override
protected void onDraw(Canvas canvas) {
Path path = new Path();
int w = this.getWidth();
int h = this.getHeight();
/向路徑中添加圓角矩形。radii數(shù)組定義圓角矩形的四個圓角的x,y半徑。radii長度必須為8/
path.addRoundRect(new RectF(0,0,w,h), 8.0f, 8.0f, Path.Direction.CW);
canvas.clipPath(path);
super.onDraw(canvas);
}




 ## 代碼中動態(tài)實現(xiàn)
   陰差陽錯,上面反而已經(jīng)把如何在代碼中動態(tài)實現(xiàn)講出來了,我們可以直接用一個方法來概括,

/**設(shè)置圖片圓角
* @param bitmap 原圖
* @param radius 圓角角度
* @param viewWidth 需要展示所在view的寬度
* @param viewHeight
* @return
*/
public Bitmap getRoundRectBitmap(Bitmap bitmap, int radius, int viewWidth, int viewHeight) {
Bitmap result = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);

    final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
    final RectF rectF = new RectF(0, 0, viewWidth, viewHeight);

    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setFilterBitmap(true);

    float[] rids = {10.0f,10.0f,10.0f,10.0f,0.0f,0.0f,0.0f,0.0f,};

    Path path = new Path();
    path.addRoundRect(rectF, rids, Path.Direction.CW);
    canvas.clipPath(path);

    canvas.drawBitmap(bitmap, rect, rectF, paint);
    return result;
}
   這里使用到了float數(shù)組,只將圖片的左上和右上修改為圓角。僅供參考。

------
## 注意點以及拓展
   * 如果ImageView的本身尺寸不是固定的,比如你寫了wrap_content,而后又動態(tài)的改變了它的尺寸,記得要同步到切割方法的Rect中去,否則會出現(xiàn)各種切得的bitmap不是你要的問題。
   * 由于如上方法可以對指定的角進行切割圓角,所以最終選擇如上如上方法在此闡述。其實如果單單實現(xiàn)圓角,方法有很多,比如如下方法

paint.setXfermode(null);
canvas.drawRoundRect(rectF, radius, radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

     在此不做過多展開,若有疑問可直接私信或留言交流。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,338評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,296評論 2 376

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

  • 系列文章之 Android中自定義View(一)系列文章之 Android中自定義View(二)系列文章之 And...
    YoungerDev閱讀 4,440評論 3 11
  • 版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載 前言 Canvas 本意是畫布的意思,然而將它理解為繪制工具一...
    cc榮宣閱讀 41,605評論 1 47
  • 作者:扔物線 鏈接:https://juejin.im/post/5962a3746fb9a06ba2687226...
    不怕曬閱讀 480評論 0 0
  • 導航 Android Paint之顏色過濾器 Paint之shader(圖像渲染) Paint之PathEffec...
    侯蛋蛋_閱讀 4,639評論 0 5
  • 媽媽,我向你道歉“對不起” 我不應(yīng)該看書的時候在玩,有一天,你頭疼,讓我自己去看書,幾個小時過去,我只...
    63f3bc70e361閱讀 371評論 0 5