View. post 컨트롤 폭 높 은 원리 탐색 가 져 오기
16614 단어 Android
안 드 로 이 드 개발 시 Activity 의 onCreate 방법 에서 컨트롤 의 getMeasure Height / getHeight 또는 getMeasure Width / getWidth 방법 으로 얻 은 너비 와 높이 는 모두 0 입 니 다. 저 는 여러분 이 이런 문제 에 직면 했 을 때 먼저 도 모 를 열 고 검색 하면 흔히 볼 수 있 는 두 가지 해결 방안 이 나 올 것 이 라 고 믿 습 니 다.
1. Draw / Layout 이벤트 감청 을 통 해: ViewTreeObserver
1 view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
2 @Override
3 public void onGlobalLayout() {
4 mScrollView.post(new Runnable() {
5 public void run() {
6 view.getHeight(); //height is ready
7 }
8 });
9 }
10 });
우리 가 이 감청 을 등록 할 때 컨트롤 은 onMeasure - > onLayout - > onDraw 의 일련의 방법 으로 렌 더 링 이 끝 난 후에 이 등 록 된 감청 을 되 돌려 줍 니 다. 우 리 는 자 연 스 럽 게 컨트롤 의 너비 와 높이 를 얻 을 수 있 습 니 다.
2. 두 번 째 방법 은 마음 에 들 어 요. View. post () 하나 runnable 로 하면 돼 요.
1 ...
2 view.post(new Runnable() {
3 @Override
4 public void run() {
5 view.getHeight(); //height is ready
6 }
7 });
8 ...
우 리 는 보통 이렇게 컨트롤 의 너비 와 높이 를 얻 을 수 있 는 방법 을 사용 하 는데, 방법 은 매우 좋 지만, 10 만 개의 왜 태 도 를 가지 고 있 는 지 에 따라 나 는 이 방법의 원 리 를 한 번 정리 하기 로 결정 했다.
안 드 로 이 드 의 모든 화면 은 Activity 이 고 모든 Activity 의 맨 위 는 DecorView 이 며 사용자 정의 레이아웃 을 감 싸 줍 니 다. 자, 다음은 View 의 post 방법 이 무엇 을 했 는 지 살 펴 보 겠 습 니 다.
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
이 안 에는 mAttachInfo 가 비어 있 는 지 아 닌 지 를 판단 합 니 다. null 이 아 닐 경우 mAttachInfo 에 저 장 된 Handler 대상 을 직접 꺼 내 서 우리 의 Runnable 작업 을 게시 합 니 다. null 이 라면 getRunQueue () 방법 이 무엇 을 하 는 지 살 펴 보 겠 습 니 다.
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
Handler Action Queue 대상 을 만 들 고 이 대상 을 되 돌려 줍 니 다. 이 대상 을 눌 러 보 겠 습 니 다.
public class HandlerActionQueue {
private HandlerAction[] mActions;
private int mCount;
public void post(Runnable action) {
postDelayed(action, 0);
}
public void postDelayed(Runnable action, long delayMillis) {
final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
synchronized (this) {
if (mActions == null) {
mActions = new HandlerAction[4];
}
mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
mCount++;
}
}
....
이전 코드 에서 볼 수 있 듯 이 mAttachInfo 가 null 이면 HandlerAction Queue 대상 의 post 방법 을 호출 하여 Runnable 작업 을 전달 합 니 다. 이 어 post Delayed 방법 을 호출 합 니 다. 이 방법 은 우리 의 Runnable 작업 과 지연 되 는 시간 을 HandlerAction 대상 에 밀봉 한 다음 에 아래 의 HandlerAction [] 배열 에 추가 합 니 다.눌 러 도 Handler Action 이 간단 한 패키지 클래스 라 는 것 을 알 수 있다.
private static class HandlerAction {
final Runnable action;
final long delay;
public HandlerAction(Runnable action, long delay) {
this.action = action;
this.delay = delay;
}
public boolean matches(Runnable otherAction) {
return otherAction == null && action == null
|| action != null && action.equals(otherAction);
}
}
이제 고 개 를 돌려 생각해 보면 의 심 스 러 울 것 입 니 다. 상황 은 post 방법 으로 돌아 갑 니 다. 우 리 는 mAttach Info 라 는 값 에 따라 직접 post 로 작업 을 보 내 는 지, 아니면 작업 을 대기 열 에 넣 는 지 판단 합 니 다. 그러면 이 값 은 언제 할당 되 었 습 니까?
정 답 은 ViewRootImpl 류 에서 ViewRootImpl 구조 에서 이러한 엔 드 코드 가 AttachInfo 대상 을 만 들 었 습 니 다.
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
그리고 permTraversals () 방법 에서 이 코드 를 호출 합 니 다. 이 코드 는 여러분 이 잘 아 는 permMeasure, permLayout, permDraw 방법 전에 실 행 됩 니 다.
host.dispatchAttachedToWindow(mAttachInfo, 0);
host 는 DecorView 입 니 다. ViewGroup 이기 때문에 ViewGroup 의 dispatchAttached ToWindow 방법 을 살 펴 보 겠 습 니 다.
@Override
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
super.dispatchAttachedToWindow(info, visibility);
mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
child.dispatchAttachedToWindow(info,
combineVisibility(visibility, child.getVisibility()));
}
final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
for (int i = 0; i < transientCount; ++i) {
View view = mTransientViews.get(i);
view.dispatchAttachedToWindow(info,
combineVisibility(visibility, view.getVisibility()));
}
}
주요 이 방법 에서 자신 과 모든 child 부모 클래스, 즉 View 의 dispatchAttached ToWindow 방법 을 호출 하 는 것 을 볼 수 있 습 니 다. View 의 dispatchAttached ToWindow 방법 이 도대체 무슨 일 을 했 는 지 살 펴 보 겠 습 니 다.
/**
* @param info the {@link android.view.View.AttachInfo} to associated with
* this view
*/
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
}
mWindowAttachCount++;
// We will need to evaluate the drawable state at least once.
mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
if (mFloatingTreeObserver != null) {
info.mTreeObserver.merge(mFloatingTreeObserver);
mFloatingTreeObserver = null;
}
registerPendingFrameMetricsObservers();
if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0) {
mAttachInfo.mScrollContainers.add(this);
mPrivateFlags |= PFLAG_SCROLL_CONTAINER_ADDED;
}
// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
performCollectViewAttributes(mAttachInfo, visibility);
onAttachedToWindow();
ListenerInfo li = mListenerInfo;
final CopyOnWriteArrayList listeners =
li != null ? li.mOnAttachStateChangeListeners : null;
if (listeners != null && listeners.size() > 0) {
// 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.
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewAttachedToWindow(this);
}
}
int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(vis);
if (isShown()) {
// Calling onVisibilityChanged directly here since the subtree will also
// receive dispatchAttachedToWindow and this same call
onVisibilityAggregated(vis == VISIBLE);
}
}
// Send onVisibilityChanged directly instead of dispatchVisibilityChanged.
// As all views in the subtree will already receive dispatchAttachedToWindow
// traversing the subtree again here is not desired.
onVisibilityChanged(this, visibility);
if ((mPrivateFlags&PFLAG_DRAWABLE_STATE_DIRTY) != 0) {
// If nobody has evaluated the drawable state yet, then do it now.
refreshDrawableState();
}
needGlobalAttributesUpdate(false);
}
이 코드 는 비교적 길 지만, 우 리 는 한눈 에 mAttachInfo 가 바로 여기에 할당 되 어 있 는 것 을 볼 수 있 습 니 다. 할당 되 어 호출 된 곳 은 바로 위 ViewRootImpl 의 permTraversals () 방법 입 니 다!그리고 이 방법 을 이어서 보도 록 하 겠 습 니 다.
mRunQueue.executeActions(info.mHandler);
이 코드 를 호출 하여 앞에서 언급 한 Handler Action 류 의 execute Actions 방법 을 실 행 했 습 니 다. 이 방법 이 무엇 을 했 는 지 살 펴 보 겠 습 니 다.
public void executeActions(Handler handler) {
synchronized (this) {
final HandlerAction[] actions = mActions;
for (int i = 0, count = mCount; i < count; i++) {
final HandlerAction handlerAction = actions[i];
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
mActions = null;
mCount = 0;
}
}
ok, 모든 생각 이 뚜렷 해 졌 습 니 다. 이 방법 은 우리 가 전달 한 mAttach Info 의 Handler 로 우리 View. post 에 저 장 된 모든 Runnable 작업 을 옮 겨 다 닐 것 입 니 다.
이로써 모든 절 차 를 다 분석 한 것 같 지만 세심 한 동창 회 가 앞의 분석 에 구멍 이 있다 는 것 을 발견 하면 어디 일 까?ViewRootImpl 의 performTraversals () 방법 을 실행 할 때,
host.dispatchAttachedToWindow(mAttachInfo, 0);
이 방법 을 사용 하면 분명히 측정, 레이아웃, 세 가지 방법 을 그리 기 전에 실 행 됩 니 다. 즉, 이 방법 을 사용 하면 우리 가 mAttach Info 에서 Handler 를 전달 하여 View. post 중의 Runnable 을 실행 한 다음 에 측정, 레이아웃, 세 가지 방법 을 사용 할 수 있 습 니 다. 그러면 이론 적 으로 너비 와 높 은 값 을 얻 지 못 합 니 다. 이때 측정 을 실행 하지 않 았 습 니 다.그런데 왜 결 과 를 얻 을 수 있 을까요??
괜 찮 습 니 다. 저도 처음에 그렇게 생각 했 고 오 랜 시간 동안 곤 혹 스 러 웠 습 니 다. 이것 은 안 드 로 이 드 메시지 체 제 를 모 르 기 때문에 안 드 로 이 드 의 운행 은 사실은 메시지 구동 모델 입 니 다. 즉, 안 드 로 이 드 메 인 스 레 드 에서 기본적으로 Handler 를 만 들 었 습 니 다.또한 이 메 인 스 레 드 에 실행 대기 열 에 있 는 Message 를 반복 적 으로 옮 겨 다 니 는 Looper 를 만 들 었 습 니 다. 이 실행 은 동기 화 되 었 습 니 다. 즉, Message 를 실행 한 후에 야 다음 을 계속 실행 할 수 있 습 니 다. permTraversals () 를 호출 하 는 방법 은 메 인 스 레 드 의 Looper 를 통 해 실 행 된 것 입 니 다. 이 방법 은 아직 실행 이 끝나 지 않 았 습 니 다.그리고 우 리 는 이 방법 에서 mAttach Info 에서 Handler 를 통 해 View. post 의 Runnable 을 실행 합 니 다. mAttach Info 에서 Handler 도 메 인 스 레 드 에 만 들 어 졌 기 때문에 이전 메시지 가 실 행 된 후에 실 행 됩 니 다. 즉, 측정, 레이아웃, 그리 기 가 실 행 된 후에 실 행 됩 니 다. 그러면 자 연 스 럽 게 컨트롤 의 너비 와 높이 를 얻 을 수 있 습 니 다.
나 는 안 드 로 이 드 팀 의 이 메커니즘 이 너무 교묘 하 게 설계 되 었 다 는 것 에 탄복 하지 않 을 수 없다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.