編輯:關於Android編程
想來說說基礎版自定義View的步驟:
實現自定義View的屬性設置,需要:
在values目錄下建立attrs.xml文件,添加屬性內容
在布局文件中添加新的命名空間xmlns,然後可以使用命名空間給自定義的空間設置屬性
設置完屬性之後,當然還要對其進行處理。在自定義View類中的構造方法中進行處理
首先貼一段attrs的代碼:
義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了。
都是基礎知識,大家不要嫌棄。
介紹Snackbars provide lightweight feedback about an operation. They show a brief messag
FrameMetricsListener APIAndroid N 仍處於活動的開發狀態,但現在您可以將其作為 N Developer Preview 的一部分進行試用。
支付寶的快捷支付Android版業務流程比較麻煩,出現的意外情況比較多.在此,簡單說下開發流程以及出現錯誤的解決方案; 1.注冊支付業務.這裡不在贅述.建立數據安全傳輸所
Android提供的系統服務之--Vibrator(振動器)