利用Android中BitmapShader制作自帶邊框的圓形頭像
效果如下:
BitmapShader 的簡單介紹
關(guān)于 Shader
是什么,Shader
的種類有哪幾種以及如何使用不屬于本文范疇,對這方面不是很了解的同學(xué),建議先去學(xué)習(xí)一下 Shader
的基本使用。
BitmapShader
主要的作用就是 通過Paint對象,對 畫布進(jìn)行指定的Bitmap
填充,實現(xiàn)一系列效果,可以有以下三種模式進(jìn)行選擇
1.CLAMP
- 拉伸,這里拉伸的是圖片的最后一個元素,不斷地重復(fù),這個效果,在圖片比較小,而所要畫的面積比較大的時候會比較明顯。
2.REPEAT
- 重復(fù),橫向縱向不斷地重復(fù),不同于上一模式,這種模式在圖片比較小不能滿足要求是,會在橫向縱向不斷重復(fù)繪制圖形。
3.MIRROR
- 翻轉(zhuǎn),這種模式和 REPEAT
是類似的,只不過這里的重復(fù)是翻轉(zhuǎn)著重復(fù),和折紙的效果差不多。
而我們將要使用的是 CLAMP
模式,因為只要我們對圖形的大小進(jìn)行控制,就可以避免圖像進(jìn)行拉伸。
具體實現(xiàn)介紹
為了自定義 圖像,邊框?qū)挾群皖伾?,我們首先?res/values 目錄下,新建一個 attrs.xml文件,里面要書寫的內(nèi)容如下所示
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyCustomView"> <attr name="mborder_color" format="color"></attr> <attr name="mborder_width" format="dimension"></attr> <attr name="msrc" format="reference"></attr> </declare-styleable> </resources>
當(dāng)然,在這里還可以添加一些其他的特性。既然定義了我們想要使用的特性,那么我們就要在自定義View
里面 解析這些屬性并且加以利用,解析過程如下所示
TypedArray type = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView); mBorderColor = type.getColor(R.styleable.MyCustomView_mborder_color,0); mDrawable = type.getDrawable(R.styleable.MyCustomView_msrc); //將獲得的 Drawable 轉(zhuǎn)換成 Bitmap BitmapDrawable bitmapDrawable = (BitmapDrawable) mDrawable; mBitmap = bitmapDrawable.getBitmap(); mBorderWidth = type.getDimensionPixelSize(R.styleable.MyCustomView_mborder_width, 2);
值得注意的是 mSrc
屬性的解析,由于獲得是 Drawable
對象,所以我們需要將其轉(zhuǎn)換為 Bitmap
對象。
下面就利用我們獲得的 Bitmap
對象進(jìn)行圓形頭像的繪制,對 BitmapShader
和 Paint
的初始化如下所示
mSrcBitmap = Bitmap.createScaledBitmap(mBitmap, mWidth, mHeight, false); mShader = new BitmapShader(mSrcBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mPaint = new Paint(); mPaint.setShader(mShader); mRadius = (mWidth - mBorderWidth * 2 - 4) / 2; mCircleX = (mWidth) / 2; mCircleY = (mHeight) / 2;
mSrcBitmap是對獲得的圖像進(jìn)行適當(dāng)?shù)目s小或者放大,以適應(yīng)我們對圖形的要求,而這里的 mWidth
和 mHeight
又是什么呢?實際上就是我們在 定義視圖在 layout_width
和 layout_height
中傳遞進(jìn)來的值,不過在這里我對他們進(jìn)行了處理,也就是選取最小值操作,這樣的話就可以避免由于寬大于高或者高大于寬而造成圖像填充不滿指定區(qū)域的現(xiàn)象。值得注意的是,自定義視圖,需要對 wrap_content
進(jìn)行特殊處理,否則系統(tǒng)對該屬性的視圖不予以顯示。至于如何進(jìn)行處理,可以看看本例的源碼,很簡單,相信很多人一看就知道或者說早就了然于胸。
還有一點需要強(qiáng)調(diào)的是這里的 mRadius
,也就是將要繪制的圓的半徑,為什么要減去邊框的寬度 乘 2 呢? 要知道,我們的圓是根據(jù) 視圖指定的寬度或者高度來畫的,如果我們所畫 的圓恰好是指定視圖的內(nèi)切圓,那么邊框放在哪里呢?它肯定要被畫在視圖的外面,那樣我們就看不到完整的邊框了。所以,這么減去的意義在于為邊框騰出空間。
經(jīng)過以上操作,我們就已經(jīng)將圓形頭像繪制出來了,下面來繪制邊框,其實非常簡單,我只不過是又定義了一個 Paint
對象,并且利用它畫了一個圓而已,畫筆的初始化操作如下所示
mBorderPaint = new Paint(); mBorderPaint.setStyle(Paint.Style.STROKE); mBorderPaint.setStrokeWidth(mBorderWidth); mBorderPaint.setColor(mBorderColor); mBorderPaint.setStrokeCap(Paint.Cap.ROUND);
好了,下面就可以在onDraw()
函數(shù)中,進(jìn)行繪制了,如下所示
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawCircle(mCircleX, mCircleY, mRadius, mPaint); canvas.drawCircle(mCircleX, mCircleY, mRadius, mBorderPaint); }
這樣,整個效果就算實現(xiàn)完畢了。下面來看看如何使用
<com.example.hwaphon.patheffecttest.MyView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:layout_marginRight="8dp" app:mborder_color="@android:color/holo_green_light" app:mborder_width="4dp" app:msrc="@drawable/myview_test"/>
注意,一定要在容器中加上這么一句
xmlns:app=http://schemas.android.com/apk/res-auto
具體實現(xiàn)的核心代碼
package com.example.hwaphon.patheffecttest; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; /** * Created by Hwaphon on 2016/5/12. */ public class MyView extends View { private Bitmap mBitmap; private Drawable mDrawable; private Bitmap mSrcBitmap; private BitmapShader mShader; private Paint mPaint; private int mWidth, mHeight; private int mRadius; private int mCircleX, mCircleY; private int mBorderColor; private Paint mBorderPaint; private int mBorderWidth; public MyView(Context context) { this(context, null); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray type = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView); mBorderColor = type.getColor(R.styleable.MyCustomView_mborder_color,0); mDrawable = type.getDrawable(R.styleable.MyCustomView_msrc); //將獲得的 Drawable 轉(zhuǎn)換成 Bitmap BitmapDrawable bitmapDrawable = (BitmapDrawable) mDrawable; mBitmap = bitmapDrawable.getBitmap(); mBorderWidth = type.getDimensionPixelSize(R.styleable.MyCustomView_mborder_width, 2); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth = measureWidth(widthMeasureSpec); mHeight = measureHeight(heightMeasureSpec); int temp = mWidth > mHeight ? mHeight : mWidth; mWidth = mHeight = temp; initView(); setMeasuredDimension(mWidth, mHeight); } private int measureHeight(int heightMeasureSpec) { int size = MeasureSpec.getSize(heightMeasureSpec); int sizeMode = MeasureSpec.getMode(heightMeasureSpec); int result = 0; if (sizeMode == MeasureSpec.EXACTLY) { result = size; } else { result = 200; if (sizeMode == MeasureSpec.AT_MOST) { result = Math.min(result, size); } } return result; } private int measureWidth(int widthMeasureSpec) { int size = MeasureSpec.getSize(widthMeasureSpec); int sizeMode = MeasureSpec.getMode(widthMeasureSpec); int result = 0; if (sizeMode == MeasureSpec.EXACTLY) { result = size; } else { result = 200; if (sizeMode == MeasureSpec.AT_MOST) { result = Math.min(result, size); } } return result; } private void initView() { mSrcBitmap = Bitmap.createScaledBitmap(mBitmap, mWidth, mHeight, false); mShader = new BitmapShader(mSrcBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mPaint = new Paint(); mPaint.setShader(mShader); mRadius = (mWidth - mBorderWidth * 2) / 2; mCircleX = (mWidth) / 2; mCircleY = (mHeight) / 2; mBorderPaint = new Paint(); mBorderPaint.setStyle(Paint.Style.STROKE); mBorderPaint.setStrokeWidth(mBorderWidth); mBorderPaint.setColor(mBorderColor); mBorderPaint.setStrokeJoin(Paint.Join.ROUND); mBorderPaint.setStrokeCap(Paint.Cap.ROUND); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawCircle(mCircleX, mCircleY, mRadius, mPaint); canvas.drawCircle(mCircleX, mCircleY, mRadius, mBorderPaint); } }
總結(jié)
以上就是Android利用BitmapShader制作自帶邊框圓形頭像的全部內(nèi)容,希望這篇文章對大家開發(fā)Android的時候能有所幫助,如果有疑問大家可以留言交流。
- 整理CocosCreator常用知識點
- 全面講解CocosCreator熱更新
- CocosCreator經(jīng)典入門項目之flappybird
- CocosCreator通用框架設(shè)計之網(wǎng)絡(luò)
- 如何用CocosCreator實現(xiàn)射擊小游戲
- cocos2dx-3.10 C++實現(xiàn)滾動數(shù)字
- Cocos2dx實現(xiàn)數(shù)字跳動效果
- cocos2dx實現(xiàn)刮獎效果
- cocos2dx實現(xiàn)橡皮擦效果以及判斷是否擦除完畢
- Cocos2d實現(xiàn)刮刮卡效果
- Android自定義view實現(xiàn)圓形、圓角和橢圓圖片(BitmapShader圖形渲染)
- CocosCreator如何實現(xiàn)劃過的位置顯示紋理
相關(guān)文章
Android開發(fā)Jetpack組件DataBinding用例詳解
這篇文章主要為大家介紹了Android開發(fā)Jetpack組件DataBinding的使案用例詳解說明,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02Android開發(fā)中方向傳感器定義與用法詳解【附指南針實現(xiàn)方法】
這篇文章主要介紹了Android開發(fā)中方向傳感器定義與用法,結(jié)合實例形式分析了Android方向傳感器的功能、定義與使用方法,并附帶基于方向傳感器實現(xiàn)指南針功能的方法,需要的朋友可以參考下2017-11-11Android利用ViewPager實現(xiàn)可滑動放大縮小畫廊效果
這篇文章主要介紹了Android利用ViewPager實現(xiàn)可滑動放大縮小畫廊效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-08-08Android Recyclerview實現(xiàn)上拉加載更多功能
在項目中使用列表的下拉刷新和上拉加載更多是很常見的功能。下文給大家?guī)砹薃ndroid Recyclerview上拉加載更多功能,需要的朋友參考下吧2017-05-05Android studio實現(xiàn)簡易計算器App功能
這篇文章主要為大家詳細(xì)介紹了Android studio實現(xiàn)簡易計算器App功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05