圖片的圓角處理

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

以上純屬牢騷漓摩,扁平化狂熱者請繞道裙士。。幌甘。
今天看到一個ios跟主管表明自己對于擬物風(fēng)格的向往和對扁平風(fēng)格的厭惡潮售。痊项。


言歸正傳,由于ios帶來的圓角風(fēng)格酥诽,導(dǎo)致產(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)造方法,當(dāng)然在新api中會出現(xiàn)第四種虾宇,此處寫三種即可搓彻。

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

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

在Android的graphics包下有許多幾何繪圖相關(guān)的類靶端,我們平時見得比較多的諸如Canvas,Paint都在這個類下面,平時我們會使用canvas參數(shù)傳遞給畫筆Paint凛膏,做一些字體的繪制等等杨名,我們通常設(shè)置它的粗細(xì),大小猖毫,鋸齒等等之后交給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ù)撜嬲那懈钸壿嫛?   兩個層面蓖柔,疊加,然后切割得到圓角啊什么的风纠,過于乏味,面向初學(xué)者的話很難堅持讀完牢贸。

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

  • 第一種潜索,貌似參數(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)把模具準(zhǔn)備好了臀蛛,那么接下去我們就開始要準(zhǔn)備澆灌了亲桦,我們還需要兩樣?xùn)|西,機床和原料浊仆。
   原料很明顯就是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)

![切割細(xì)節(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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市腻扇,隨后出現(xiàn)的幾起案子债热,更是在濱河造成了極大的恐慌,老刑警劉巖幼苛,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窒篱,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機墙杯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門配并,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人高镐,你說我怎么就攤上這事溉旋。” “怎么了嫉髓?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵观腊,是天一觀的道長。 經(jīng)常有香客問我算行,道長梧油,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任纱意,我火速辦了婚禮婶溯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘偷霉。我一直安慰自己迄委,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布类少。 她就那樣靜靜地躺著叙身,像睡著了一般。 火紅的嫁衣襯著肌膚如雪硫狞。 梳的紋絲不亂的頭發(fā)上信轿,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音残吩,去河邊找鬼财忽。 笑死,一個胖子當(dāng)著我的面吹牛泣侮,可吹牛的內(nèi)容都是我干的即彪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼活尊,長吁一口氣:“原來是場噩夢啊……” “哼隶校!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蛹锰,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤深胳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后铜犬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舞终,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡轻庆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了权埠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榨了。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖攘蔽,靈堂內(nèi)的尸體忽然破棺而出龙屉,到底是詐尸還是另有隱情,我是刑警寧澤满俗,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布转捕,位于F島的核電站,受9級特大地震影響唆垃,放射性物質(zhì)發(fā)生泄漏五芝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一辕万、第九天 我趴在偏房一處隱蔽的房頂上張望枢步。 院中可真熱鬧,春花似錦渐尿、人聲如沸醉途。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽隘擎。三九已至,卻和暖如春凉夯,著一層夾襖步出監(jiān)牢的瞬間货葬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工劲够, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留震桶,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓征绎,卻偏偏與公主長得像蹲姐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子炒瘸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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