自定義view實現圓角圖片


前兩天想實現一個圓角圖片的效果,通過網絡搜索后找到一些答案。這里自己再記錄一下,加深一下自己的認識和知識理解。

 

實現圓角圖片的思路是自定義一個ImageView,然后通過Ondraw()重繪的功能,將drawable和一個圓形進行重疊繪制,這樣就可以達到圓角的效果了。

 

下面開始具體實現圓角圖片的過程。

第一步:寫自定義屬性文件

首先我們需要定義一個屬性。在values目錄下面新建一個xml文件,這個文件用來自定義一些屬性,這樣我們就可以寫出自己的控件了。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RoundImageAttrs">
        <attr name="BorderRadius" format="dimension"/>
        <attr name="RoundType">
        	<enum name="circle" value="0"/>
        	<enum name="round" value="1"/>
        </attr>
    </declare-styleable>
</resources>

我來簡單解釋一下,declare-styleable這個標簽就是用來自定義屬性的,attr標簽用來定義具體的屬性,format可以指定很多種格式,具體有哪些屬性,這里不做過多介紹了,請看博客中的介紹http://blog.csdn.net/mayingcai1987/article/details/6216655。在本例子中dimension表示尺寸的意思,這個應該在平常的開發中也用到。

同時還有一個enum的枚舉類型,我們之前在用系統控件的時候,比如 android:orientation="vertical"就是比較典型的枚舉類型,只不過在本例中我們自己實現了這個枚舉類型。

定義好這個之后,我們就可以開始寫我們的自定義view的代碼了。最后注意的一點,這里的這個xml的名字可以隨便命名(不過最好命名的比較有意義),android系統會自動找到的。

 

第二步,自定義View

  這一步是本文中最重要的一步,也是實現自定義view的核心。那么我們從目的出發來探討實現圓角image的方式。那么我們的目的是將圖片變圓,在這里一般人可能有想到兩種方式將圖片變圓  1.把本來圖片修改為圓形,其他地方都是透明2.只是讓顯示的時候,動態的裁剪到一些部分,然后讓圖片變圓。

  這兩種方法的優劣我想大家一眼就能看明白。直接修改圖片,帶來的后果是,我如果換了一種方式了,不再是圓角,而是星型,那么我們的圖片已經毀掉了,沒辦法在重用了。而第二種方式就可以,無論我們換什么樣的表現方式,我們是需要換一種裁剪的算法,就可以實現不同的形狀的圖片了,那么很顯然,第二種方法是以不變應萬變的。

  那么現在,我們選定了第二種方式,那怎么實現呢?有人說我們自己寫一個類,直接繼承自view,然后所有的繪圖和大小的計算我們都自己來搞,這樣行嗎?當然可行,但是我們可能是在重復造輪子,因為我們已經有了一個ImageView,而且它里面給我們做了很多關於圖片的操作,我們何不繼承自ImageView,然后做少量的工作,就可以實現圓角效果呢。方案確定,好,那我們就開始實現自己的類。

  

public class RoundImageView extends ImageView {

  我們定義一個RoundImageView 繼承自ImageView

 

private int mBorderRadius;
private int mType;

  然后兩個成員變量,分別對應於自定義屬性文件中的BorderRadius和RoundType。

 

接下來我們重寫構造函數

public RoundImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mPaint = new Paint();  
	       mPaint.setAntiAlias(true);
		TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.RoundImageAttrs);
		
		mBorderRadius = a.getDimensionPixelSize(R.styleable.RoundImageAttrs_BorderRadius,
				(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
		mType = a.getInt(R.styleable.RoundImageAttrs_RoundType,0);
		a.recycle();
		Log.i("Log","mBorderRadius:"+mBorderRadius+"type:"+mType);
		
	}

 首先是mPaint的初始化,這是一個畫刷,用來畫圖形的,后面會說。

  接下來是最為關鍵的代碼, context.obtainStyledAttributes(attrs,R.styleable.RoundImageAttrs),這句代碼用來獲取控件上的自定義屬性。

  然后下面兩句

mBorderRadius = a.getDimensionPixelSize(R.styleable.RoundImageAttrs_BorderRadius,
				(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
mType = a.getInt(R.styleable.RoundImageAttrs_RoundType,0);

  分別獲取這兩個屬性的值,第一個比較復雜,涉及到默認值單位的轉換,(這里10的代表默認值)

  第二個就是獲取RoundImageAttrs_RoundType,獲取完畢后,記得一定要調用a.recycle();對資源進行釋放。以便后面的其他代碼可以訪問這些屬性資源。(理解的不透徹,但記住釋放就ok)

 

  到現在為止我們完成了萬里長征第一步,獲取到了我們自定義控件的屬性了。

  接下來就是我們的重頭戲,重繪圖片。下面我們重寫了OnDraw函數

  

@Override
	protected void onDraw(Canvas canvas) {
		//super.onDraw(canvas);
		Bitmap bitmap = mWeakBitmap == null?null:mWeakBitmap.get();
		if(bitmap == null || bitmap.isRecycled())
		{			
			Drawable drable = getDrawable();
			int width = drable.getIntrinsicWidth();
			int height = drable.getIntrinsicHeight();
			if(drable!=null)
			{
				bitmap = Bitmap.createBitmap(getWidth(),getHeight(),Config.ARGB_8888);
				Canvas dcanvas = new Canvas(bitmap);
				drable.draw(dcanvas);
				if(mMashBitmap == null || mMashBitmap.isRecycled())
				{
					mMashBitmap = getShapeBitmap();
				}
				mPaint.reset();
				mPaint.setFilterBitmap(false);
				mPaint.setXfermode(mXfermode);
				dcanvas.drawBitmap(mMashBitmap, 0,0, mPaint);
				mPaint.setXfermode(null);
				canvas.drawBitmap(bitmap, 0,0, null);
				mWeakBitmap = new WeakReference<Bitmap>(bitmap);
				
			}	
		}
		else
		{
			mPaint.setXfermode(null);
			canvas.drawBitmap(bitmap, 0,0, null);
			return;
		}
	}

  上面的代碼可能第一次看比較迷惑,各種paint還有canvas,drawable,各種區分不清。下面我結合代碼都說說。

  Bitmap bitmap = mWeakBitmap == null?null:mWeakBitmap.get();這句話是從一個弱引用中取得Bitmap圖像,我們在成功創建圓形圖像后,會保存起來,以供后面刷新使用。
  
  接下來我們判斷bitmap,如果為空說明還沒有創建過。接下來我們通過getDrawable();獲取當前ImageView的drawable,里面包含了原本的圖像。
  
  下面我們創建了一個臨時的Bitmap對象,這個對象將保存經過我們處理之后的圖像
  bitmap = Bitmap.createBitmap(getWidth(),getHeight(),Config.ARGB_8888);
然后我們創建一個Canvas dcanvas = new Canvas(bitmap); drable.draw(dcanvas); 通過drawable的draw方法將原來ImageView的圖像繪制到dcanvas上(其實也是畫到bitmap上)。

  接下來我們獲取圖形(這里是圓形,后面大家可以自己定義形狀)mMashBitmap = getShapeBitmap();(這個函數我們后面介紹),然后我們設置了paint的屬性
private Xfermode mXfermode = new PorterDuffXfermode(Mode.DST_IN);
mPaint.setXfermode(mXfermode);
  這個mXfermode代表的意思是,當用paint畫圖時,新繪制的圖像與原圖像的關系。給大家一張圖,就很容易理解各種繪制方式了。
  

,在本例子中用的就是DST_IN,想必這張圖一看就明白。paint配置完后,我們就開始將新的形狀繪制到原來的圖像上
dcanvas.drawBitmap(mMashBitmap, 0,0, mPaint); 此時,bitmap中保存的就是疊加之后的圖片了,也就是我們最終需要的圓角圖片了。最后我們將這個bitmap繪制到OnDraw函數給我們傳遞進來的
canvas上,所有工作就基本做完了。canvas.drawBitmap(bitmap, 0,0, null);
最后將繪制好的圖片保存起來。mWeakBitmap = new WeakReference<Bitmap>(bitmap);

下一次執行ondraw 的時候,我們就直接用保存好的bitmap進行繪制了,也就是我們代碼中else的部分。

最艱難的部分說完了,哈哈,如果不理解還是得多看幾遍。接下來的工作就輕松了很多,對了,我們還沒有實現之前那個繪制形狀的函數呢。我們來繪制把。很容易的。

  

private Bitmap getShapeBitmap()
	{
		Bitmap bit = Bitmap.createBitmap(getWidth(),getHeight(),Config.ARGB_8888);
		Canvas can = new Canvas(bit);
		Paint pa = new Paint(Paint.ANTI_ALIAS_FLAG);
		pa.setColor(Color.BLACK);
		
		if(mType == 0)
		{
			can.drawCircle(getWidth()/2, getHeight()/2, mBorderRadius, pa);
		}
		return bit;
		
	}

  看看上面的代碼,是不是很熟悉,我們之前已經接觸過基本的畫圖方法了。想必,不用解釋了,一眼都能看明白。這里我只實現了畫圓的,大家可以各自發揮想想,畫出各種各樣的形狀,哈哈,是不是很容易,我們自己實現了圓角圖像,同時對於android自定義view的繪制也有了大致了解。

  對了,這里面還有一個問題,如果用戶想動態修改圖片怎么辦,我們在內存里面保存了一個舊的圖片,該怎么更新呢。其實好辦,我們只需要做下面的操作就行

 public void invalidate()  
	    {  
	        mWeakBitmap = null;  
	        if (mMashBitmap != null)  
	        {  
	        	mMashBitmap.recycle();  
	        	mMashBitmap = null;  
	        }  
	        super.invalidate();  
	    } 

  這個函數是當view視圖重繪的時候執行的,於是乎,當我們更換圖片后,讓view重繪,就可以將之前保留的舊的圖片信息清空啦。

 

  真不容易啊,終於寫完了,竟然寫了三個小時,看來以后自己還是得多多些博客了,不過通過寫文章, 更加加深了認識,值得。

 

  最后如果轉載,別忘了注明源地址噢,謝謝各位看官。

  轉載請注明出處http://www.cnblogs.com/gaoteng/p/4222207.html     www.gaotenglife.com

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com