編輯:關於Android編程
要獲取屏幕寬高,我們可以先從android的界面構成了解
android的界面主要由三部分構成:1、狀態欄 2、標題欄 3、內容區域
\
狀態欄主要用來顯示一些系統圖標,應用的通知圖標和系統時間。
android中標題欄主要用來顯示當前位置,3.0過後添加了ActionBar,擁有了導航和OptionMenu的功能,5.0又新添加了ToolBar控件,和ActionBar類似,但自定義的空間更充足
android中的內容區域,實際上是一塊系統默認的名為android.id.content的單幀布局,setContentView()方法相當於添加View到這個單幀布局中,並覆蓋。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DisplayMetrics dm = new DisplayMetrics();
// 獲取屏幕信息
getWindowManager().getDefaultDisplay().getMetrics(dm);
int screenWidth = dm.widthPixels;
int screenHeigh = dm.heightPixels;
Log.v("獲取屏幕寬度", "寬度:" + screenWidth + ",高度:" + screenHeigh);
}

控件View有getHeight()和getwidth()方法可以獲取寬高,但是如果直接在onCreate()方法中獲取控件寬高,獲取到的值是0,至於原因的是因為onCreate()方法中只是提供了數據初始化此時還沒有正式繪制圖形。而繪制圖形在OnDraw中進行,此時計算又顯得太晚。容易想到的辦法是:希望能在程序剛剛測量好某個指定控件後,拿到它的寬度和高度立刻進行計算或數據初始化。這就需要有一個方法來監聽到這個事件的發生,幸好Android提供了這樣的機制,利用View類中的getViewTreeObserver方法,可以獲取到指定View的觀察者,在繪制控件前的一剎那進行回調,這樣速度上又不耽誤,得到的數據由是准確的。
public class MainActivity extends AppCompatActivity {
TextView firstTxt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
firstTxt = (TextView) findViewById(R.id.hello_word_txt);
ViewTreeObserver viewTreeObserver = firstTxt.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
int height = firstTxt.getHeight();
int width = firstTxt.getWidth();
Log.v("獲取TextView寬高", "寬度:" + width + ",高度:" + height);
return true;
}
});
}
}

關於方法二,下面有一段我自己的簡單理解
將一個runnable添加到Layout隊列中:View.post()
簡單地說,只要用View.post()一個runnable就可以了。runnable對象中的方法會在View的measure、layout等事件後觸發UI事件隊列會按順序處理事件。在setContentView()被調用後,事件隊列中會包含一個要求重新layout的message,所以任何你post到隊列中的東西都會在Layout發生變化後執行。
firstTxt.post(new Runnable() {
@Override
public void run() {
firstTxt.getHeight(); //height is ready
firstTxt.getWidth();
}
}
上面提到過繪制控件是在ondraw()方法中進行,在ondraw中獲取控件寬高則太晚,而在onMeasure()中測量又太早。那我們嘗試在onLayout()中獲取控件寬高是否可行。 onLayout()和ondraw()都是控件View生命周期中的回調方法。至於View的生命周期,我也還沒接觸過,等後面深入學習了再總結一下。
首先新建一個類繼承自TextView,然後重寫裡面的onLayout()和onMeasure()方法
public class MyTextView extends TextView {
public MyTextView(Context context) {
super(context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
//===========上面為繼承TextView默認的實現方法,我們可以不管
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取MyTextView當前實例的高
int height = this.getHeight();
//獲取MyTextView當前實例的寬
int width = this.getWidth();
//通過Log.v打印輸出顯示
Log.v("onMeasure獲取控件寬高", "高:" + height + ",寬:" + width);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//獲取MyTextView當前實例的高
int height = this.getHeight();
//獲取MyTextView當前實例的寬
int width = this.getWidth();
//通過Log.v打印輸出顯示
Log.v("onLayout獲取控件寬高", "高:" + height + ",寬:" + width);
}
}
再在程序入口的Acitvity中的onCreate()方法中實例化自定義的MyTextView
public class MainActivity extends AppCompatActivity {
MyTextView mMyTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyTextView = (MyTextView) findViewById(R.id.mytextview_txt);
}
}

從上面可以看出MyTextView第一次回調onMeasure()方法時,還沒有獲取到控件的寬高數據。上面提到過"如果直接在onCreate()方法中獲取控件寬高,獲取到的值是0",那我們可以假設一下activity的onCreate()方法執行時在View執行第一次onMeasure()方法之前,我們嘗試通過以下方法看能否驗證這一假設
首先在上面新建的MyTextView中添加兩個我們自定義的方法,一個是setMyTextViewSize(),另外一個是getMyTextViewSize(),代碼如下:
public class MyTextView extends TextView {
int mMyTextViewHeight;
int mMyTextViewWidth;
public MyTextView(Context context) {
super(context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
//===========上面為繼承TextView默認的實現方法,我們可以不管
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取MyTextView當前實例的高
int height = this.getHeight();
//獲取MyTextView當前實例的寬
int width = this.getWidth();
//調用setMyTextViewSize,使其他類可以通過getMyTextViewSize()可以獲取到
//mMyTextViewHeight和mMyTextViewWidth變量的值,即高和寬
setMyTextViewSize(height, width);
//通過Log.v打印輸出顯示
Log.v("onMeasure獲取控件寬高", "高:" + height + ",寬:" + width);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//獲取MyTextView當前實例的高
int height = this.getHeight();
//獲取MyTextView當前實例的寬
int width = this.getWidth();
//與上同理
setMyTextViewSize(height, width);
//通過Log.v打印輸出顯示
Log.v("onLayout獲取控件寬高", "高:" + height + ",寬:" + width);
}
public void setMyTextViewSize(int height, int width) {
mMyTextViewHeight = height;
mMyTextViewWidth = width;
}
public int[] getMyTextViewSize() {
int[] viewSize = new int[]{mMyTextViewHeight, mMyTextViewWidth};
return viewSize;
}
}
再在程序入口的Acitvity的onCreate()方法中上面已經實例化得到的MyTextView的getMyTextViewsize()方法嘗試獲取控件寬高
public class MainActivity extends AppCompatActivity {
MyTextView mMyTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyTextView = (MyTextView) findViewById(R.id.mytextview_txt);
int[] myTextViewSize = mMyTextView.getMyTextViewSize();
Log.v("入口acitivity的onCreate獲取控件寬高", "高:" + myTextViewSize[0] + ",寬:" + myTextViewSize[1]);
}
}
運行打印輸出顯示:

通過上圖可以發現Acitivity的onCreate()方法執行時機是在View的第一次回調onMeasure()之前,View第一次回調onMeasure()方法的時候還沒有獲取到寬高,自然Acitivity的onCreate()方法中也無法獲取到控件寬高。那麼,通過上面的打印信息,我們可以設想如果在onLayout()方法已經獲取到View的寬高時,Activity的onCreate()再獲取寬高就行了。我想到的一種思路是在Acitivity的onCreate()方法循環調用MyTextView的onLayout()方法,但是我們直到在android的主線程也就是UI線程中執行死循環會導致頁面無法顯示,那麼我們應該在onCreate()方法中開辟一條子線程 ,方法循環調用MyTextView的onLayout()方法,直到獲取到寬高。要實現這個思路的話,我們可以用到Handler。
下面是具體的代碼實現:
public class MainActivity extends AppCompatActivity {
MyTextView mMyTextView;
//使用Handler默認的無參構造方法時,Handler執行的線程即聲明Handler的線程,在這裡是在主線程,也就是UI線程中
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.v("Handler獲取控件寬高", "高:" + msg.arg1 + ",寬:" + msg.arg2);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyTextView = (MyTextView) findViewById(R.id.mytextview_txt);
new Thread(new Runnable() {
@Override
public void run() {
int[] viewSize = mMyTextView.getMyTextViewSize();
Log.v("onCreate()獲取控件寬高", "高:" + viewSize[0] + ",寬:" + viewSize[1]);
while (viewSize[0] == 0) {
viewSize = mMyTextView.getMyTextViewSize();
if (viewSize[0] != 0) {
Message msg = Message.obtain();
msg.arg1 = viewSize[0];
msg.arg2 = viewSize[1];
mHandler.sendMessage(msg);
}
}
}
}).start();
}
}

通過上面的方法我們就可以在onCreate()方法中獲取View的寬高了
Android響應式編程(一)RxJava前篇[入門基礎]
1.RxJava概述ReactiveX與RxJava在講到RxJava之前我們首先要了解什麼是ReactiveX,因為RxJava是ReactiveX的一種java實現。
淺談Android應用的內存優化及Handler的內存洩漏問題
一、Android內存基礎物理內存與進程內存物理內存即移動設備上的RAM,當啟動一個Android程序時,會啟動一個Dalvik VM進程,系統會給它分配固定的內存空間(
使用Android提供的大量標准Action,Category調用系統Activity
Android提供了大量的標准Action,Category:例子,查看並獲取聯系人電話用戶點擊按鈕值會顯示系統的聯系人列表,當用戶單擊聯系人之後,程序將會顯示該聯系人的
Android_View_View繪制流程
1. View 樹的繪圖流程當 Activity 接收到焦點的時候,它會被請求繪制布局,該請求由Android framework 處理.繪制是從根節點開始,