Android UI変更メカニズム


メインスレッドでUI変更メカニズムを簡単に理解してみましょう.UIがいつ変更されるか、変更されないかを理解している場合は、コードをより正確に記述できます.
UIを変更する方法はTextViewの方法と同じで、主にsetXxx()です.また,カスタムビューを作成する場合,このようなsetterメソッドを作成する際には,POJO(plain old java object)のように単純な代入のみを行うエラーも発生する.値を1つ指定すればいいわけではありません.invalidate()メソッドを呼び出して再描画する必要があります.
public void setTitle(String title) {
	this.title = title;
    invalidate(); // 1)
 }
書き直すには、1)のようにinvalidate()メソッドを呼び出してから메인 Looper의 MessageQueueに入り、次のタイミングで画面に描く必要があります.これは、onDraw()にパラメータ伝達として反映されるtitleである.
invalidate()メソッドの呼び出しスタックを確認する
invalidate()から始まるメソッドに従います.
View.invalidate()

ViewGroup.invalidateChild()
parent = parent.invalidateChildInParent() : ViewGroup.invalidateChildInParent()

parent = parent.invalidateChildInParent() : ViewRootImpl.invalidateChildInParent()
ViewRootImpl.scheduleTraversals() : traversal 작업을 스케줄링
ViewRootImpl.performTraversal() : traversal 작업을 실행 (draw 메세지를 보냄)

  • Viewのinvalidate()メソッドは、親View Groupで自分の領域を再描画する必要があることを示す、ViewのinvalidateChild(ViewChild,final Rect dirty)メソッドを呼び出します.

  • invalidateChild()メソッドはdowhile文で親を親とします.invalidateChildInParent(location,drity)を置き換え、親がnullでない場合に呼び出しを続行します.(親がRootじゃないまで)
  • ここではViewParent 인터페이스が表示されます.ViewにはgetView Group()という方法があるはずですが、この方法はありません.代わりにgetParent()メソッドが使用され、getParent()はView Parentを返します.
    したがって、親ビューグループを取得する方法は、getParent()を実行し、ビューグループとして選択することである.
  • 現在親がRootである場合、ViewGroup.invalidateChildInParent()ではなくViewRootImpl.invalidateChildInParent()が呼び出されます.
  • dowhile文では、View/View Groupが再描画した領域を上に渡し、どうしても一番上に渡します.最上級はやはりViewGroup(com.android.internal.policy.PhoneWindow$DecorView)で、ここで再描画のメッセージを送ることもできます.
    しかし論理を分離するために,仮想は別の位相を作成した.これはViewRootImpl類です.ViewGroupとViewRootImlはViewParentインタフェースを実現し、invalidateChildInParent()はViewParentインタフェースの方法である.
  • すなわちinvalidateChild()メソッドは、dowhile文でViewRootImplのinvalidateChildInParent(int[]location,Rectdirty)を最終的に呼び出す.
  • ViewRootImplでは、invalidateChildInParent()の主なタスクはscheduleTrearsals()メソッドを呼び出すことです.scheduleTransversals()メソッドは、無効な(無効な)領域を再描画するためにポーリング(遍歴)タスクをスケジュールします.
    スケジュールは既にメインLooperのMessageQueueにMessageを入れていたが、JellyBinからChoreogerに再依頼.
  • ViewRootImplのinvalidateChildInParent()は、まずcheckThread()メソッドを呼び出し、プライマリスレッドでない場合はCalledFromWrongThreadExceptionを生成する.
    invalidateChild(), invalidateChildInParent() Deprecated
  • onDescentInvalidate()を使用する必要があります.
  • invalidate()呼び出しスタック
    View.invalidate()
    
    ViewGroup.onDescendantInvalidated()
    parent = parent.onDescendantInvalidated() 
    
    //다음 노드가 Root가 아닌 경우 (do-while문)
    ViewGroup.onDescendantInvalidated()
    
    //다음 노드가 Root인 경우
    ViewRootImpl.onDescendantInvalidated()
    ViewRootImpl.scheduleTraversals() : traversal 작업을 스케줄링
    ViewRootImpl.performTraversal() : traversal 작업을 실행 (draw 메세지를 보냄)
    
    invalidate()を複数回呼び出すと
    fun onClick(view: View) {
            for (i in 0..4) {
                currentValue.setText("Current Value=$i")
                SystemClock.sleep(1000)
            }
        }
    TextViewテキストを毎秒変換するコードのように見えます.

    しかし、運転後、画面は1秒ごとに変化せず、5秒後に最後に入れたCurrent Value=4のみが表示されます.5秒以内にメインスレッドを捉えたため、5秒以内に画面をリフレッシュできない.
    TextViewのsetText()メソッドは論理的に複雑で、最終的にはinvalidate()メソッドを呼び出して再描画する必要があります.ここに疑問があります.setText()でinvalidate()を実行するたびに5秒以内に描画できませんが、invalidate()ではViewRootImplを経てscheduleTransversals()を実行し、MessageQueueで다시 그리기メッセージを蓄積する必要がある場合があります.
    Q.では、Current Value=0からCurrent Value=4まで、短時間で肉眼では中間過程が見えないように出力しているのではないでしょうか.
    そうではない.
    Viewでは、mPrivateFlagsタグを使用して、メソッド内でinvalidate()を複数回呼び出しても、1回目の呼び出しだけでViewRootImplに渡すことができます.
    ビューのinvalidateInternal()メソッドの先頭にmPrivateFlags値でチェックされたif条件文が含まれます.
    1)最初の無効な内部()メソッドのif文でタグを変更する
    2)次のinvalidate()呼び出しでは、invalidateInternal()メソッドのif文からフィルタリングされ、ViewRootImplには到達しません.
    すなわち,メソッド内でinvalidate()を複数回呼び出しても,「再描画」のメッセージは1回だけ伝達される.
    しかし、無効な()メソッドを継続させることはできません.描き終わってinvalidate()を呼び出すと、もう一度描きますから.
    ViewのmPrivateFlagsはパッケージ化された変数であり、View、View Group、View RootImplの3つの場所で適切な変更を行うことでこの問題を解決します.
    Reference
  • アンドロイド<Next Step>
  • https://velog.io/@sery270/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-UI%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EB%90%A0%EA%B9%8C