[Android] アニメーションとゴーンが語らなければならない物語: View におけるアニメーションの影響を分析する

背景

View のアニメーションと Gone はすでに誰もがよく知っています。アニメーションはビューにアニメーションを追加する役割を果たし、Gone はビューを非表示にすることができます。

では、ビューのアニメーションが終了していない場合、Gone を設定するとアニメーションが終了するのでしょうか? ビューは非表示になりますか?

これは開発プロセス中に遭遇した現象です。シーンを復元するだけです。

まず LoadingView をカスタマイズします。実装は非常に簡単です。背景を設定した後、表示されたら、それ自体を中心に回転するアニメーションを開始します。

public class LoadingView extends View {
    
    

    private RotateAnimation animation;

    private void init(){
    
    
        animation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, .5f);
        animation.setRepeatMode(Animation.RESTART);
        animation.setInterpolator(new LinearInterpolator());
        animation.setRepeatCount(-1);
        animation.setDuration(1250);
	    setBackgroundResource(R.drawable.loading_frame);
    }

    @Override
    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
    
    
        super.onVisibilityChanged(changedView, visibility);
        if(visibility == View.VISIBLE){
    
    
            if (!animation.hasStarted()||animation.hasEnded()) {
    
    
                startAnimation(animation);
            }
        }
    }
}

次に、レイアウト ファイルで LoadingView を使用し、最外層として FrameLayout を使用します。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
            <LoadingView
            android:id="@+id/progress"
            android:layout_width="42dp"
            android:layout_height="42dp"
            android:layout_gravity="center"
            />
    
</FrameLayout>

最後にアクティビティで Gone を呼び出します。

mProgressView.setVisibility(GONE)

その結果、ロードは依然としてそこで回転しています。
ここに画像の説明を挿入

LoadingView のプロパティを確認すると、可視性が失われています。

ここに画像の説明を挿入

width と height にも値があります。

ここに画像の説明を挿入

問題があるように見えますが、その背後には少なくとも 2 つの詳細が隠されています。

1. アニメーションと描画の表示
2. 主流のレイアウトでのゴーン処理

アニメーションと描画を表示する

View の公式コメントでは、描画は次のステップに分かれています。
ここに画像の説明を挿入

  1. 背景を描く
  2. 必要に応じて、グラデーションの準備としてキャンバスのレイヤーを保存します。
  3. ビューの内容を描画する
  4. サブビューを描画する
  5. グラデーションエッジを描画し、必要に応じてレイヤーを復元します
  6. 装飾を描画する (スクロールバーなど)

4 番目のステップはサブビューを描画することです。ViewGroup には次のようなコードがあります。

protected void dispatchDraw(Canvas canvas) {
    
    
    for (int i = 0; i < childrenCount; i++) {
    
    
         ...
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
    
    
            more |= drawChild(canvas, child, drawingTime);
        }
    }
}

このコードは、サブビューが表示されている場合、またはアニメーションがある場合、サブビューの描画ロジックが呼び出されることを示しています。

したがって、アニメーションを前提として、 goon を使用して View を非表示にすることはできません

主流のレイアウトになくなった場合の処理

ビューは非表示になっています。通常、このビューの幅と高さは両方とも 0 であると考えられます。

実際、レイアウト測定プロセス中に、可視性プロパティがなくなったサブビューはスキップされます。

# FrameLayout

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    

   for (int i = 0; i < count; i++) {
    
    
      final View child = getChildAt(i);
      if (mMeasureAllChildren || child.getVisibility() != GONE) {
    
    
          if (measureMatchParentChildren) {
    
    
              if (lp.width == LayoutParams.MATCH_PARENT ||
                      lp.height == LayoutParams.MATCH_PARENT) {
    
    
                  mMatchParentChildren.add(child);
              }
          }
      }
  }

}
# LinearLayout

void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
    
    
	...
	for (int i = 0; i < count; ++i) {
    
    
		...
        if (child.getVisibility() == GONE) {
    
    
            i += getChildrenSkipCount(child, i);
            continue;
        }
     	...
	 }

}

したがって、プロパティがなくなったサブビューは再測定されず、そのサイズは最後に測定されたサイズのままになります。

タッチイベント

上記の問題を特定すると、別の疑問が生じました。

同時にアニメーションを実行するように設定されている子 View が表示されているので、Touch イベントを受け取ることができますか?

ログを再生したところ、それが可能であることがわかりました。

この理由はソースコードでわかります。

子ViewのTouchイベントは親コントロールから渡されますが、ViewGroupのイベント配信メソッドには以下のようなコードがあります。

#ViewGroup
    
public boolean dispatchTouchEvent(MotionEvent ev) {
    
    
	...
    if (!child.canReceivePointerEvents()
           || !isTransformedTouchPointInView(x, y, child, null)) {
    
    
       ev.setTargetAccessibilityFocus(false);
       continue;
   }
	...
}

child.canReceivePointerEvents()が返された場合false、配布はスキップされます。

canReceivePointerEventsコードは次のとおりです。

#View

protected boolean canReceivePointerEvents() {
    
    
    return (mViewFlags & VISIBILITY_MASK) == VISIBLE || getAnimation() != null;
}

Gone が設定されていますが、アニメーションは空ではないため、Touch イベントは受信できます。

他の

最初にビューが消えるように設定されており、ユーザーがビューを表示できない場合でも、ビューはアニメーションを実行し続け、システム パフォーマンスを消費します。

したがって、アニメーションを使用するときは注意し、clearAnimation適切なタイミングでアニメーションを中止してください。

要約する

  1. 最初にこの問題に遭遇したとき、それを呼び出すことでclearAnimation解決しました。でも原因が分からないので、次に同じような問題に遭遇した時にはもう無理です。原理を理解するのに1分で解ける問題を1日もかかりませんでしたし、個人的にはそれだけの価値があると感じました。
  2. 事前にイベント配信の知識をマスターし、Touch イベントを分析する際の対象となります。
  3. Choreographer とインターフェイスの更新プロセスに慣れていると、アニメーションの実行プロセスをすぐに理解できます。
  4. 蓄積を重視しており、今は役に立たない知識もあるかもしれませんが、使ってみるとその快適さがわかります。

参考資料:
インターフェース更新の仕組み
アニメーション動作原理を見る

おすすめ

転載: blog.csdn.net/qq_23049111/article/details/127531493