android获得一个view的高度,Android ViewTreeObserver使用总结及获得View高度的几种方法...
ViewTreeObserver 注冊一個觀察者來監聽視圖樹,當視圖樹的布局、視圖樹的焦點、視圖樹將要繪制、視圖樹滾動等發生改變時,ViewTreeObserver都會收到通知,ViewTreeObserver不能被實例化,可以調用View.getViewTreeObserver()來獲得。
ViewTreeObserver繼承關系:
public final class ViewTreeObserverextendsObject
java.lang.Object
?android.view.ViewTreeObserver
ViewTreeObserver直接繼承自Object.
ViewTreeObserver提供了View的多種監聽,每一種監聽都有一個內部類接口與之對應,內部類接口全部保存在CopyOnWriteArrayList中,通過ViewTreeObserver.addXXXListener()來添加這些監聽,源碼如下:
public final class ViewTreeObserver {
// Recursive listeners use CopyOnWriteArrayList
private CopyOnWriteArrayList mOnWindowFocusListeners;
private CopyOnWriteArrayList mOnWindowAttachListeners;
private CopyOnWriteArrayList mOnGlobalFocusListeners;
private CopyOnWriteArrayList mOnTouchModeChangeListeners;
private CopyOnWriteArrayList mOnEnterAnimationCompleteListeners;
// Non-recursive listeners use CopyOnWriteArray
// Any listener invoked from ViewRootImpl.performTraversals() should not be recursive
private CopyOnWriteArray mOnGlobalLayoutListeners;
private CopyOnWriteArray mOnComputeInternalInsetsListeners;
private CopyOnWriteArray mOnScrollChangedListeners;
private CopyOnWriteArray mOnPreDrawListeners;
private CopyOnWriteArray mOnWindowShownListeners;
// These listeners cannot be mutated during dispatch
private ArrayList mOnDrawListeners;
}
以OnGlobalLayoutListener為例,首先是定義接口:
public interface OnGlobalLayoutListener {
/**
* Callback method to be invoked when the global layout state or the visibility of views
* within the view tree changes
*/
public void onGlobalLayout();
}
將OnGlobalLayoutListener 添加到CopyOnWriteArray數組中:
/**
* Register a callback to be invoked when the global layout state or the visibility of views
* within the view tree changes
*
* @param listener The callback to add
*
* @throws IllegalStateException If {@link #isAlive()} returns false
*/
public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
checkIsAlive();
if (mOnGlobalLayoutListeners == null) {
mOnGlobalLayoutListeners = new CopyOnWriteArray();
}
mOnGlobalLayoutListeners.add(listener);
}
移除OnGlobalLayoutListener,當視圖樹布局發生變化時不會再收到通知了:
/**
* Remove a previously installed global layout callback
*
* @param victim The callback to remove
*
* @throws IllegalStateException If {@link #isAlive()} returns false
*
* @deprecated Use #removeOnGlobalLayoutListener instead
*
* @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
*/
@Deprecated
public void removeGlobalOnLayoutListener(OnGlobalLayoutListener victim) {
removeOnGlobalLayoutListener(victim);
}
/**
* Remove a previously installed global layout callback
*
* @param victim The callback to remove
*
* @throws IllegalStateException If {@link #isAlive()} returns false
*
* @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
*/
public void removeOnGlobalLayoutListener(OnGlobalLayoutListener victim) {
checkIsAlive();
if (mOnGlobalLayoutListeners == null) {
return;
}
mOnGlobalLayoutListeners.remove(victim);
}
其他常用方法:
dispatchOnGlobalLayout():視圖樹發生改變時通知觀察者,如果想在View Layout 或 View hierarchy 還未依附到Window時,或者在View處于GONE狀態時強制布局,這個方法也可以手動調用。
/**
* Notifies registered listeners that a global layout happened. This can be called
* manually if you are forcing a layout on a View or a hierarchy of Views that are
* not attached to a Window or in the GONE state.
*/
public final void dispatchOnGlobalLayout() {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
final CopyOnWriteArray listeners = mOnGlobalLayoutListeners;
if (listeners != null && listeners.size() > 0) {
CopyOnWriteArray.Access access = listeners.start();
try {
int count = access.size();
for (int i = 0; i < count; i++) {
access.get(i).onGlobalLayout();
}
} finally {
listeners.end();
}
}
}
dispatchOnPreDraw():通知觀察者繪制即將開始,如果其中的某個觀察者返回 true,那么繪制將會取消,并且重新安排繪制,如果想在View Layout 或 View hierarchy 還未依附到Window時,或者在View處于GONE狀態時強制繪制,可以手動調用這個方法。
/**
* Notifies registered listeners that the drawing pass is about to start. If a
* listener returns true, then the drawing pass is canceled and rescheduled. This can
* be called manually if you are forcing the drawing on a View or a hierarchy of Views
* that are not attached to a Window or in the GONE state.
*
* @return True if the current draw should be canceled and resceduled, false otherwise.
*/
@SuppressWarnings("unchecked")
public final boolean dispatchOnPreDraw() {
boolean cancelDraw = false;
final CopyOnWriteArray listeners = mOnPreDrawListeners;
if (listeners != null && listeners.size() > 0) {
CopyOnWriteArray.Access access = listeners.start();
try {
int count = access.size();
for (int i = 0; i < count; i++) {
cancelDraw |= !(access.get(i).onPreDraw());
}
} finally {
listeners.end();
}
}
return cancelDraw;
}
ViewTreeObserver常用內部類:
內部類接口
備注
ViewTreeObserver.OnPreDrawListener
當視圖樹將要被繪制時,會調用的接口
ViewTreeObserver.OnGlobalLayoutListener
當視圖樹的布局發生改變或者View在視圖樹的可見狀態發生改變時會調用的接口
ViewTreeObserver.OnGlobalFocusChangeListener
當一個視圖樹的焦點狀態改變時,會調用的接口
ViewTreeObserver.OnScrollChangedListener
當視圖樹的一些組件發生滾動時會調用的接口
ViewTreeObserver.OnTouchModeChangeListener
當視圖樹的觸摸模式發生改變時,會調用的接口
獲得View高度的幾種方式:
我們應該都遇到過在onCreate()方法里面調用view.getWidth()和view.getHeight()獲取到的view的寬高都是0的情況,這是因為在onCreate()里還沒有執行測量,需要在onResume()之后才能得到正確的高度,那么可不可以在onCreate()里就得到寬高呢?答:可以!常用的有下面幾種方式:
1、通過設置View的MeasureSpec.UNSPECIFIED來測量:
int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(w, h);
//獲得寬高
int viewWidth=view.getMeasuredWidth();
int viewHeight=view.getMeasuredHeight();
設置我們的SpecMode為UNSPECIFIED,然后去調用onMeasure測量寬高,就可以得到寬高。
2、通過ViewTreeObserver .addOnGlobalLayoutListener來獲得寬高,當獲得正確的寬高后,請移除這個觀察者,否則回調會多次執行:
//獲得ViewTreeObserver
ViewTreeObserver observer=view.getViewTreeObserver();
//注冊觀察者,監聽變化
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//判斷ViewTreeObserver 是否alive,如果存活的話移除這個觀察者
if(observer.isAlive()){
observer.removeGlobalOnLayoutListener(this);
//獲得寬高
int viewWidth=view.getMeasuredWidth();
int viewHeight=view.getMeasuredHeight();
}
}
});
3、通過ViewTreeObserver .addOnPreDrawListener來獲得寬高,在執行onDraw之前已經執行了onLayout()和onMeasure(),可以得到寬高了,當獲得正確的寬高后,請移除這個觀察者,否則回調會多次執行
//獲得ViewTreeObserver
ViewTreeObserver observer=view.getViewTreeObserver();
//注冊觀察者,監聽變化
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if(observer.isAlive()){
observer.removeOnDrawListener(this);
}
//獲得寬高
int viewWidth=view.getMeasuredWidth();
int viewHeight=view.getMeasuredHeight();
return true;
}
});
總結
以上是生活随笔為你收集整理的android获得一个view的高度,Android ViewTreeObserver使用总结及获得View高度的几种方法...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux as86,记linux_ce
- 下一篇: android京东首页轮播代码,web移