android圓形圖片,圓形背景文字的CircleTextImageView開源組件
轉【http://blog.csdn.net/ys408973279/article/details/50350313】
在項目中我們經常遇到需要顯示圓形頭像的需求,一般我們都使用hdodenhof/CircleImageView的這個開源控件實現(簡潔,沒多余的東西)。
而在的項目中我經常遇到這樣的一個需求:如果用戶上傳了頭像就顯示圓形頭像,如果沒有上傳頭像就在圓形背景上顯示文字。或者是直接在圓形頭像上添加文字。因此我就在CircleImageView基礎上實現了一個CircleTextImageView的組件。
CircleTextImageView是一個什么樣的組件呢,直接上圖吧
1.只顯示頭像
2.圓形背景文字
3.頭像+文字
分別的使用方法:
1.只顯示頭像
<com.thinkcool.circletextimageview.CircleTextImageView xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/profile_image" android:layout_width="96dp" android:layout_height="96dp" android:src="@drawable/hugh" app:citv_border_width="2dp" app:citv_border_color="#FF000000"/>
2.圓形背景文字
<com.thinkcool.circletextimageview.CircleTextImageView android:layout_width="wrap_content" android:layout_height="wrap_content" app:citv_border_color="@android:color/white" app:citv_border_width="2dp" android:layout_gravity="center" app:citv_text_text="照片" app:citv_text_size="32sp" app:citv_text_padding="35dp" app:citv_text_color="@android:color/white" app:citv_fill_color="#50555D" />
3.頭像+文字
<com.thinkcool.circletextimageview.CircleTextImageView android:layout_width="96dp" android:layout_height="96dp" app:citv_border_color="@android:color/darker_gray" app:citv_border_width="2dp" android:layout_gravity="center" app:citv_text_text="晴朗" app:citv_text_size="32sp" app:citv_text_padding="35dp" android:src="@drawable/skye" app:citv_text_color="#ff99cc00" app:citv_fill_color="#50555D" />
最后在build.gradle中添加組件即可:
compile 'com.github.thinkcool:circletextimageview:1.0.20151218'
(喜歡的,歡迎加星,fork)
github地址:https://github.com/CoolThink/CircleTextImageView
知其然知其所以然,最后說一下實現吧:
簡單的說下自定義控件的流程
1.到attr.xml中declare-styleable申明自定義控件屬性
2.在構造函數中獲取相應屬性,并實現get,set函數
3.onDraw中完成圖像的繪制
4.onMeasure中處理wrap_content時控件的大小
由于該控件是基于hdodenhof/CircleImageView實現的,我們就關注下不同的地方和需要注意的就可以了:
一.繪制文字
if (!TextUtils.isEmpty(mTextString)) { Paint.FontMetricsInt fm = mTextPaint.getFontMetricsInt(); canvas.drawText(mTextString, getWidth() / 2 - mTextPaint.measureText(mTextString) / 2, getHeight() / 2 - fm.descent + (fm.bottom - fm.top) / 2, mTextPaint); }
在drawText的時候:第三個參數需要傳入的是文字的baseline(基準線:用過英文本子的都知道我們需要對齊并不是底部).
關于baseline怎么求解:
先了解下FontMetricsInt的各個成員:
1.top:文字的頂部坐標
2.bottom:文字的底部坐標
3.descent:文字頂部(bottom)到baseline的距離
要求解baseline之前我們先明確下我們的目標是為了讓字體在y軸上居中:由于baseline是一個對于我們來說是一個抽象的位置,那我們就以bottom為參照來進行計算。
1.字體的高度=bottom-top,為了讓字體居中,所以將整個字體高度居中即可,因此有:bottom=getHeight()/2+(bottom-top)/2
2.而根據descent的意義我們知道:baseline=bottom-descent;
3.所以baseline=getHeight()/2+(bottom-top)/2-descent
二.處理wrap_content的情況的適配
為什么要處理wrap_content的情況,因為控件是繼承自imageView的,因此imageView的wrap_content是指的:控件的寬高等于src中圖像的寬高。而我們由于添加了新功能因此要適應以下兩個場景:
1.當我們使用第二種場景(圓形背景文字)由于不需要指定src,會發現指定為wrap_content的時候控件的寬高為0.
2.當我們使用第三種場景(頭像+文字)的時候,使用wrap_content時如果我們的字體較多或者字符較長時發現文字顯示不下或者大小不合適,因為此時控件的寬高為src圖片的寬高,而圖片較小是自然顯示不下了。
因此我們需要進行重新測量重寫onMeasure函數:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMeasureSpecMode=MeasureSpec.getMode(widthMeasureSpec); int widthMeasureSpecSize=MeasureSpec.getSize(widthMeasureSpec); int heightMeasureSpecMode=MeasureSpec.getMode(heightMeasureSpec); int heightMeasureSpecSize=MeasureSpec.getSize(heightMeasureSpec); if(!TextUtils.isEmpty(mTextString)) { int textMeasuredSize= (int) (mTextPaint.measureText(mTextString)); textMeasuredSize+=2*mTextPadding; if(widthMeasureSpecMode==MeasureSpec.AT_MOST&&heightMeasureSpecMode==MeasureSpec.AT_MOST) { if(textMeasuredSize>getMeasuredWidth()||textMeasuredSize>getMeasuredHeight()) { setMeasuredDimension(textMeasuredSize,textMeasuredSize); } } } }
在onMeasure函數里:我們先調用父類的的onMeasure進行測量,對于父類的測量:除了MeasureSpecMode為AT_MOST時其他的都遵循父類的測量。
當為AT_MOST模式的時候:
我們需要對比已經測量好的MeasureSpecSize和我們傳入的文字的大小進行對比,當我們文字的大小更大時,我們就應該以文字的大小為控件的大小,當然還要加上textpadding的大小,才為控件的最終大小。
來自: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1218/3787.html