Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 基礎自定義view步驟

基礎自定義view步驟

編輯:關於Android編程

想來說說基礎版自定義View的步驟:

實現自定義View的屬性設置,需要:

在values目錄下建立attrs.xml文件,添加屬性內容

在布局文件中添加新的命名空間xmlns,然後可以使用命名空間給自定義的空間設置屬性

設置完屬性之後,當然還要對其進行處理。在自定義View類中的構造方法中進行處理

首先貼一段attrs的代碼:

   
    
    
    
    
    
        
        
        
        
    
    


接著是命名空間的事,這個我被eclipse坑了好久,要是studio就沒有這個問題了,所以最後說,最後再寫。所以我們來看一看自定

 

義View的代碼,我就不多介紹了,因為我的注釋已經做的真的很詳細了,再不懂真沒辦法了。多的不說,貼代碼:

 

public class MyView extends View
{	//TextView的文字設定
	private String textWord;
	//TextView的顏色設定
	private int textColor;
	//TextView的字體大小設定
	private int textSize;
	/**
	 * Paint即畫筆,畫筆主要保存了顏色,樣式等繪制信息  
	 * 畫筆對象有很多設置方法,大體上可以分為兩類。    
	 * 一類與圖形繪制相關
	 * 一類與文本繪制相關
	 */
	private Paint mPaint;
	/**
	 * Rect類主要用於表示坐標系中的一塊矩形區域,並可以對其做一些簡單操作
	 * 這塊矩形區域,需要用左上右下兩個坐標點表示(left,top,right,bottom)
	 * 你也可以獲取一個Rect實例的Width和Height
	 * 需要注意的一點是:
	 * 例如:Rect r=new Rect(100,50,300,500);
	 * 其實(300,500)這個點是不在矩陣區域內的,但是如果自己調用android自己的函數就沒問題,因為android內部計算方式是統一的
	 */
	private Rect mBound;

	/**
	 * 自定義view時肯定要覆寫構造方法的,目前構造參數在5.0已經達到了四個,平時我們一般使用的其實兩個就夠了。
	 * 第一個參數屬於程序內實例化采用
	 * 在java代碼創建視圖時調用,如果由xml填充的視圖就不會使用這個構造
	 * 第二個參數屬於layout文件實例化,會將xml中的參數通過attributeSet傳入到view中
	 * 在xml創建但沒有指定style的時候調用
	 * 第三個參數屬於style信息,也會從xml中帶入
	 * 在xml創建,並指定了style的時候調用
	 * 這裡的調用的雖然是第三個構造,但是defStyle是系統默認的數值,並沒有由外部傳入任何數據
	 */
	public MyView(Context context, AttributeSet attrs) {
		this(context,attrs,0);
	}

	public MyView(Context context) {
		this(context,null);
	}

	public MyView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		/**
		 * TypedArray是一個數組,用來記錄attributeSet的值,也是android提供給我們的一個工具類,
		 * 所以這個類是可以用,也可以不用的,不用的話可以用一個數組存放attributeSet中的參數屬性值,不過寫起來很麻煩
		 * TypedArray存放恢復obtainStyledAttributes(AttributeSet, int[], int, int)
		 * 或 obtainAttributes(AttributeSet, int[])值的一個數組容器,
		 * 當操作完成以後,一定要調用recycle()方法。
		 * 用於檢索的索引值在這個結構對應的位置給obtainStyledAttributes屬性。
		 * 使用這個類的時候,先要在valuse文件夾下創建:atts.xml文件
		 */
		TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyView, defStyle, 0);
		//返回我們所使用屬性的個數
		int n = a.getIndexCount();

		for (int i = 0; i < n; i++)
		{	//返回屬性id,自定義屬性初始化和設置默認值
			int attr = a.getIndex(i);
			switch (attr)
			{
			case R.styleable.MyView_myTextWord:
				textWord = a.getString(attr);
				break;
			case R.styleable.MyView_myTextColor:
				// 默認顏色設置為黑色
				textColor = a.getColor(attr, Color.BLACK);
				break;
			case R.styleable.MyView_myTextSize:
				// 默認設置為16sp,TypeValue也可以把sp轉化為px
				textSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
						TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
				break;

			}

		}
		a.recycle();

		/**
		 * 獲得繪制文本的寬和高
		 */
		mPaint = new Paint();
		//繪本字體大小
		mPaint.setTextSize(textSize);
		// mPaint.setColor(mTitleTextColor);繪本字體顏色
		//view 的繪圖范圍
		mBound = new Rect();

		/**
		 * 這裡的代碼需要倒著看,倒著寫就好理解一些
		 * 首先我們需要得到文本Text的視圖大小(不是TextView視圖大小)
		 * 需要傳入的參數和實例有畫筆mPaint,有mBound矩陣實例,有TextWord文字
		 * 並且需要規定好文本字體的大小,應為他會影響視圖范圍,所以上面代碼中
		 * 設畫筆顏色的代碼可以注銷,但設置文本字體的代碼不能少。
		 */
		mPaint.getTextBounds(textWord, 0, textWord.length(), mBound);

		//這裡是添加點擊事件,動態的更改文本內容,程序員都知道,不用看,主要需呀看得就一句postInvalidate();
		this.setOnClickListener(new OnClickListener()
		{

			@Override
			public void onClick(View v)
			{	//設置文本呢
				textWord = setMyTextWord();
				/**
				 * android提供的更新ui界面的方法有兩個
				 * 一個是Invalidate();
				 * 一個是postInvalidate();
				 * 其中前者是在UI線程自身中使用,而後者在非UI線程中使用。 
				 * 很顯然兩個用法是不同的,我們分開說:
				 * Android提供了Invalidate方法實現界面刷新,但是Invalidate不能直接在線程中調用,
				 * 因為他是違背了單線程模型:Android UI操作並不是線程安全的,並且這些操作必須在UI線程中調用。 
				 * 利用invalidate()刷新界面實例化一個Handler對象,
				 * 並重寫handleMessage方法調用invalidate()實現界面刷新
				 * 而在線程中通過sendMessage發送界面更新消息。
				 * 
				 * 使用postInvalidate()刷新界面使用postInvalidate則比較簡單,
				 * 不需要handler,直接在線程中調用postInvalidate即可。
				 * 這裡我們就直接調用了postInvalidate();這個方法,實現text文字的刷新了。
				 * 
				 * Android中View的繪制過程當Activity獲得焦點時,它將被要求繪制自己的布局,
				 * Android framework將會處理繪制過程,Activity只需提供它的布局的根節點。
				 * 繪制過程從布局的根節點開始,從根節點開始測量和繪制整個layout tree。
				 * 每一個ViewGroup 負責要求它的每一個孩子被繪制,每一個View負責繪制自己。
				 * 因為整個樹是按順序遍歷的,所以父節點會先被繪制,而兄弟節點會按照它們在樹中出現的順序被繪制。
				 */
				postInvalidate();
			}

		});
	}
	//略過,可以不看你
	public String setMyTextWord(){
		Setset=new HashSet();
		Random r=new Random();
		while(set.size()<4){
			int num=r.nextInt(10);
			set.add(num);
		}
		StringBuffer sb=new StringBuffer();
		for(Integer i:set){
			sb.append(i);
		}

		return sb.toString();
	}

	/**
	 * onMeasure方法,獲取視圖view的寬高尺寸
	 * widthMeasureSpec寬的詳細測量值
	 * heightMeasureSpec高的詳細測量值
	 * 表示的是view視圖想要的大小寬高,但不一定就是最終實際的大小寬高
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		/**
		 * MeasureSpec這是一個封裝在View裡面的內部類
		 * 其內部處理的是measurespec詳細測量中,size大小,model模式之間呢的關系
		 * MeasureSpec封裝了父布局傳遞給子布局的布局要求,每個MeasureSpec代表了一組寬度和高度的要求 
		 * MeasureSpec由size和mode組成。 
		 * 三種Mode: 
		 * 1.UNSPECIFIED 
		 * 父不沒有對子施加任何約束,子可以是任意大小(也就是未指定) 
		 * (UNSPECIFIED在源碼中的處理和EXACTLY一樣。當View的寬高值設置為0的時候或者沒有設置寬高時,模式為UNSPECIFIED 
		 * 2.EXACTLY 
		 * 父決定子的確切大小,子被限定在給定的邊界裡,忽略本身想要的大小。 
		 * (當設置width或height為match_parent時,模式為EXACTLY,因為子view會占據剩余容器的空間,所以它大小是確定的) 
		 * 3.AT_MOST 
		 * 子最大可以達到的指定大小 
		 * (當設置為wrap_content時,模式為AT_MOST, 表示子view的大小最多是多少,這樣子view會根據這個上限來設置自己的尺寸) 
		 *  
		 * MeasureSpecs使用了二進制去減少對象的分配。 
		 */
		int width = 0;
		int height = 0;

		/**
		 * 設置寬度
		 */
		int specMode = MeasureSpec.getMode(widthMeasureSpec);
		int specSize = MeasureSpec.getSize(widthMeasureSpec);
		switch (specMode)
		{
		case MeasureSpec.EXACTLY:// 明確指定了
			width = getPaddingLeft() + getPaddingRight() + specSize;
			break;
		case MeasureSpec.AT_MOST:// 一般為WARP_CONTENT
			width = getPaddingLeft() + getPaddingRight() + mBound.width();
			break;
		}

		/**
		 * 設置高度
		 */
		specMode = MeasureSpec.getMode(heightMeasureSpec);
		specSize = MeasureSpec.getSize(heightMeasureSpec);
		switch (specMode)
		{
		case MeasureSpec.EXACTLY:// 明確指定了
			height = getPaddingTop() + getPaddingBottom() + specSize;
			break;
		case MeasureSpec.AT_MOST:// 一般為WARP_CONTENT
			height = getPaddingTop() + getPaddingBottom() + mBound.height();
			break;
		}

		/**
		 * 在onMeasure中只調用了setMeasuredDimension()方法,接受兩個參數,
		 * 這兩個參數是通過getDefaultSize方法得到的
		 * getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
		 * 這裡就是獲取最小寬度作為默認值,然後再根據具體的測量值和選用的模式來得到widthMeasureSpec。
		 * heightMeasureSpec同理。
		 * 之後將widthMeasureSpec,heightMeasureSpec傳入setMeasuredDimension()方法。
		 */
		setMeasuredDimension(width, height);

	}


	/**
	 * 在這裡就對canvas解釋一下
	 * Canvas類(android.graphics.Canvas)。
	 * Canvas類就是表示一塊畫布,你可以在上面畫你想畫的東西。
	 * 當然,你還可以設置畫布的屬性,如畫布的顏色/尺寸等。
	 * canvas提供的方法有很多,這裡就不細說了,自己百度,但是就我被坑慘了的一點來談談。
	 * canvas.drawText方法,這個方法中第二,第三兩個參數
	 * 第二的參數標示的是視圖左邊的距離
	 * 第三個參數標示的是基准線baseline的設置
	 * 它的baseline 指的是這個UI控件中文字Text的baseline 到UI控件頂端的偏移值
	 * 可以理解為text下面那條看不見的線
	 * 如果baseline的設置和左距的設置一樣,很有可能你找不到你的text,只能看見背景圖
	 * 兩種設置方式可供參考吧,一種如下
	 * 另一種很取巧,不過試過,能用,getHeight()/2+mBound.Height()/2+6;
	 */
	@Override
	protected void onDraw(Canvas canvas) {
		//畫筆填充背景顏色
		mPaint.setColor(Color.GRAY);
		//畫布畫背景區域
		canvas.drawRect(0, 0,getMeasuredWidth(),getMeasuredHeight(),mPaint);
		//畫筆填充文字顏色
		mPaint.setColor(textColor);
		//畫布畫文字
		canvas.drawText(textWord, getWidth()/2-mBound.width()/2, (getHeight()-mBound.height())/2+mBound.height()-6, mPaint);
	}
}

最後來談一談命名空間,首先讓我們來看看命名空間是什麼鬼,有什麼作用。

 

xmlns:android="http://schemas.android.com/apk/res/android"

聲明xml命名空間。xmlns意思為“xml namespace”.冒號後面是給這個引用起的別名。
schemas是xml文檔的兩種約束文件其中的一種,規定了xml中有哪些元素(標簽)、元素有哪些屬性及各元素的關系,當然從面向 對象的角度理解schemas文

件可以認為它是被約束的xml文檔的“類”或稱為“模板”。

早期或簡單的xml用的是另一種約束,稱為DTD,這東西大家天天都見到。html/xhtml中都存在(早期的html可能沒有),如"

"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"。

現在大部分xml文檔的約束都換成schema了,原因是schema本身也是xml,二schema擴展性強。

首先說說在studio中添加命名空間,就是添加xmlns:myview="http://schemas.android.com/apk/res-auto"就ok了,myview 就是你的可用命名空間了。

但是在eclipse中,你使用xmlns:myview="http://schemas.android.com/apk/res/包名"你這麼寫是會報警的,警告是unused namespace,並且你在自定義view中調用自己定義的屬性,使用自己命名空間myview 是調不出任何東西的,這個時候不用慌,要相信自己,自己的命名空間,自己的屬性全部用敲的,不要用提示,也提示不出鬼東西,寫完保存,然後你會發現屬性能用,這就ok了。

都是基礎知識,大家不要嫌棄。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved