android自定義圓形頭像

這幾天看了項目框架里面的圓形頭像,發(fā)現(xiàn)其實這個東西并不是很難的東西,學會了原理,無論圓形頭像,五角星頭像都可以實現(xiàn)。
目前我上傳的Demo里用了兩種實現(xiàn)方式,那么我們分別來講講這兩種實現(xiàn)方式:

BitmapShader

Shader其實是遮罩的意思,能幫助我們在表層對圖像進行簡單處理,而無需那些深層的opengl,關于Shader,如需深入了解請參考sunqunsunqun的CSDN博客

基礎知識準備Shader的實現(xiàn):
  • BitmapShader 圖片填充某一區(qū)域(三種模式,拉伸,重疊,鏡像),下面詳講
  • ComposeShader 這個是用來混合其他Shader的
  • LinearGradient 可實現(xiàn)某區(qū)域的線性漸變效果
  • RadialShaderGradient 某一區(qū)域?qū)崿F(xiàn)環(huán)形漸變
  • SweepGradient 是指在某一中心以x軸正方向逆時針旋轉(zhuǎn)一周而形成的掃描效果的渲染形式(不深入研究)
    接下來詳細講講BitmapShader:
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)

這是BitmapShader的構造函數(shù),bitmap參數(shù)就是我們要處理的圖片,TileMode有三個

  • CLAMP 拉伸
  • REPEAT 重復
  • MIRROR 鏡像重復
    一般情況下如果繪制區(qū)域大于我們的bitmap時這些參數(shù)會起效,反之則無效果
    CLAMP參數(shù)起效的情況下,是按照邊緣像素進行拉伸的,這一點如果使用過.9圖的應該比較清楚
    所以我們實現(xiàn)圓形頭像的關鍵就是對源圖的合理縮放與拉伸,因為drawXXX已經(jīng)決定了圖形的形狀
BitmapShader實現(xiàn)圓形圖像的過程

首先為了簡便,我們繼承ImageView,省略一些測量函數(shù)的重寫,其次因為圓形就會帶有半徑的參數(shù),圓角就會帶角的半徑參數(shù),因此我們要自定義兩個屬性,一個是圖形類型,這里只實現(xiàn)圓形與圓角矩形

<attr name="cornerRadius" format="dimension"/><attr name="type">    
<enum name="circle" value="0"/>    
<enum name="round" value="1"/></attr>
<declare-styleable name="RoundImageView">   
 <attr name="cornerRadius"/>r    
<attr name="type"/></declare-styleable>

接下來在構造函數(shù)內(nèi)獲得這兩個屬性的值

private void init(Context context, AttributeSet attrs) {    
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView);   
 type = ta.getInt(R.styleable.RoundImageView_type, TYPE_CIRCLE);    
roundRadius = ta.getDimensionPixelSize(R.styleable.RoundImageView_cornerRadius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));    ta.recycle();
}

這個不多說了,自定義View內(nèi)常用方法
然后,如果是圓的話,我們就要截取當前繪制區(qū)域,既然是截取所以只能去寬度,高度中小的作為直徑

if (type == TYPE_CIRCLE) {    
viewWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());    
circleRadius = viewWidth / 2;  
setMeasuredDimension(viewWidth, viewWidth);}

接下來就是重點的shader處理了

private void setUpShader() {    
Drawable drawable = getDrawable();   
 if (drawable == null)       
         return;    
Bitmap bitmap = drawable2Bitmap(drawable);  
 mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
float scale = 1.0f; //bitmap:canvas ratio    
Log.i("------", "bitmap original width:" + bitmap.getWidth() + "height" + bitmap.getHeight());    
if (type == TYPE_CIRCLE) {        
int minWidth = Math.min(bitmap.getWidth(), bitmap.getHeight());        
scale = viewWidth * 1.0f / minWidth;    
} else {        
scale = Math.max(getWidth() * 1.0f / bitmap.getWidth(), getHeight() * 1.0f / bitmap.getHeight());   
 } 
mMatrix.setScale(scale, scale);    //顯示圖片中心    
if (bitmap.getWidth() * 1.0 / bitmap.getHeight() < 1) {  
mMatrix.postTranslate(0, -getHeight() / 2);    
} else {       
mMatrix.postTranslate(-getWidth() / 2, 0);    }    
mBitmapShader.setLocalMatrix(mMatrix);   
 Log.i("------", "bitmap new width:" + bitmap.getWidth() + "height" + bitmap.getHeight());   
 mPaint.setShader(mBitmapShader);}

此處的關鍵就在于獲取拉伸縮放比,為了顯示完全肯定要取消的比例,最后可能由于圖片的比例與繪圖區(qū)域不一致,導致我的圖片主要內(nèi)容不再正中,所以默認將圖片移到中間
最后,就是畫出形狀

if (type == TYPE_CIRCLE) {    
canvas.drawCircle(circleRadius, circleRadius, circleRadius, mPaint);
} else {    
canvas.drawRoundRect(roundRec, roundRadius, roundRadius, mPaint);
}
關于Xfermode實現(xiàn)圓形,圓角

原理其實差不多,Xfermode解決了怎樣將兩張圖片畫到同一個區(qū)域的問題.其實上一個方法已經(jīng)很好的解決了問題,那么為什么還要講這種呢,因為Xfermode是個很強大的東西,可以實現(xiàn)很多效果。如果單獨使用xfermode我們就要對圖片源進行縮放拉伸,普通的方法就是我生成一張新的圖片,這樣其實是比較費內(nèi)存的,這邊就詳細講一下Xfermode

Xfermode圖片繪制情況

黃色為下層,藍色為上層圖片
那么我們的過程就簡單了,為了顯示下層我們首先將源圖繪制上去,當然是經(jīng)過拉伸縮放的,然后再把形狀貼上去,取到重疊的部分,顯示下層圖片也就是DstIn模式

@Overrideprotected void onDraw(Canvas canvas) {  
  Drawable drawable = getDrawable();    
if (drawable == null)        
return;   
 circleRadius = Math.min(getWidth() / 2, getHeight() / 2);    
float scale=1.0f;    
int actWidth=drawable.getIntrinsicWidth();   
 int actHeight=drawable.getIntrinsicHeight();    
Bitmap bmp=Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);    
Canvas temp=new Canvas(bmp);    
scale= Math.min(actWidth/getWidth(),actHeight/getHeight()); 
   drawable.setBounds(0,0, (int) (getHeight()*scale), (int) (getWidth()*scale));    
drawable.draw(temp);   
 Bitmap maskBmp = getMaskBitMap();    
mPaint.reset();    
mPaint.setFilterBitmap(false);    
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));    
temp.drawBitmap(maskBmp, 0, 0, mPaint);    
mPaint.setXfermode(null);    
canvas.drawBitmap(bmp,0,0,mPaint);}
Bitmap getMaskBitMap() {    
Bitmap result = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);    
Canvas canvas = new Canvas(result);    
Paint paint = new Paint();    
paint.setAntiAlias(true);    
canvas.drawCircle(circleRadius, circleRadius, circleRadius, paint);    
return result;}

環(huán)形最近也上傳了,結合了SweepGradient,如果單是圖形變換的話,推薦使用BitmapShader就夠了,Xfermode的話用于實現(xiàn)混合的,其他代碼就不貼了,送上我的github實現(xiàn)最后總結,Matrix是個好東西啊,實現(xiàn)3D圖形的時候也要用,可惜大學矩陣學的差,只知道怎么用,不太懂原理啊,最后感謝csdn的Hongyang大神,文章參考Android Xfermode 實戰(zhàn) 實現(xiàn)圓形、圓角圖片

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

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