android自定義圓形頭像

這幾天看了項(xiàng)目框架里面的圓形頭像梧田,發(fā)現(xiàn)其實(shí)這個(gè)東西并不是很難的東西萌业,學(xué)會(huì)了原理,無(wú)論圓形頭像乡范,五角星頭像都可以實(shí)現(xiàn)配名。
目前我上傳的Demo里用了兩種實(shí)現(xiàn)方式,那么我們分別來(lái)講講這兩種實(shí)現(xiàn)方式:

BitmapShader

Shader其實(shí)是遮罩的意思晋辆,能幫助我們?cè)诒韺訉?duì)圖像進(jìn)行簡(jiǎn)單處理渠脉,而無(wú)需那些深層的opengl,關(guān)于Shader瓶佳,如需深入了解請(qǐng)參考sunqunsunqun的CSDN博客

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

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

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

首先為了簡(jiǎn)便孵滞,我們繼承ImageView,省略一些測(cè)量函數(shù)的重寫威鹿,其次因?yàn)閳A形就會(huì)帶有半徑的參數(shù)剃斧,圓角就會(huì)帶角的半徑參數(shù),因此我們要自定義兩個(gè)屬性忽你,一個(gè)是圖形類型幼东,這里只實(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>

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

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();
}

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

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

接下來(lái)就是重點(diǎn)的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);}

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

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

原理其實(shí)差不多悲龟,Xfermode解決了怎樣將兩張圖片畫到同一個(gè)區(qū)域的問(wèn)題.其實(shí)上一個(gè)方法已經(jīng)很好的解決了問(wèn)題,那么為什么還要講這種呢冰寻,因?yàn)閄fermode是個(gè)很強(qiáng)大的東西须教,可以實(shí)現(xiàn)很多效果。如果單獨(dú)使用xfermode我們就要對(duì)圖片源進(jìn)行縮放拉伸斩芭,普通的方法就是我生成一張新的圖片轻腺,這樣其實(shí)是比較費(fèi)內(nèi)存的,這邊就詳細(xì)講一下Xfermode

Xfermode圖片繪制情況

黃色為下層划乖,藍(lán)色為上層圖片
那么我們的過(guò)程就簡(jiǎn)單了贬养,為了顯示下層我們首先將源圖繪制上去,當(dāng)然是經(jīng)過(guò)拉伸縮放的迁筛,然后再把形狀貼上去煤蚌,取到重疊的部分耕挨,顯示下層圖片也就是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)形最近也上傳了,結(jié)合了SweepGradient尉桩,如果單是圖形變換的話筒占,推薦使用BitmapShader就夠了,Xfermode的話用于實(shí)現(xiàn)混合的蜘犁,其他代碼就不貼了翰苫,送上我的github實(shí)現(xiàn)最后總結(jié),Matrix是個(gè)好東西啊这橙,實(shí)現(xiàn)3D圖形的時(shí)候也要用奏窑,可惜大學(xué)矩陣學(xué)的差,只知道怎么用屈扎,不太懂原理啊埃唯,最后感謝csdn的Hongyang大神,文章參考Android Xfermode 實(shí)戰(zhàn) 實(shí)現(xiàn)圓形、圓角圖片

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鹰晨,一起剝皮案震驚了整個(gè)濱河市墨叛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌模蜡,老刑警劉巖漠趁,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異忍疾,居然都是意外死亡闯传,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門甥绿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人则披,你說(shuō)我怎么就攤上這事妹窖。” “怎么了收叶?”我有些...
    開(kāi)封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)共苛。 經(jīng)常有香客問(wèn)我判没,道長(zhǎng),這世上最難降的妖魔是什么隅茎? 我笑而不...
    開(kāi)封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任澄峰,我火速辦了婚禮,結(jié)果婚禮上辟犀,老公的妹妹穿的比我還像新娘俏竞。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布魂毁。 她就那樣靜靜地躺著玻佩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪席楚。 梳的紋絲不亂的頭發(fā)上咬崔,一...
    開(kāi)封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音烦秩,去河邊找鬼垮斯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛只祠,可吹牛的內(nèi)容都是我干的兜蠕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼抛寝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼熊杨!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起墩剖,我...
    開(kāi)封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤猴凹,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后岭皂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體郊霎,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年爷绘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了书劝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡土至,死狀恐怖购对,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情陶因,我是刑警寧澤骡苞,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站楷扬,受9級(jí)特大地震影響解幽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜烘苹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一躲株、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镣衡,春花似錦霜定、人聲如沸档悠。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辖所。三九已至,卻和暖如春曾雕,著一層夾襖步出監(jiān)牢的瞬間奴烙,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工剖张, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留切诀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓搔弄,卻偏偏與公主長(zhǎng)得像幅虑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子顾犹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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