編輯:關於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了。
都是基礎知識,大家不要嫌棄。
(Android 基礎(十一)) Snackbar
介紹Snackbars provide lightweight feedback about an operation. They show a brief messag
Android N 之 重要的開發者功能
FrameMetricsListener APIAndroid N 仍處於活動的開發狀態,但現在您可以將其作為 N Developer Preview 的一部分進行試用。
Android-支付寶快捷支付
支付寶的快捷支付Android版業務流程比較麻煩,出現的意外情況比較多.在此,簡單說下開發流程以及出現錯誤的解決方案; 1.注冊支付業務.這裡不在贅述.建立數據安全傳輸所
Android提供的系統服務之--Vibrator(振動器)
Android提供的系統服務之--Vibrator(振動器)