背景
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 の公式コメントでは、描画は次のステップに分かれています。
- 背景を描く
- 必要に応じて、グラデーションの準備としてキャンバスのレイヤーを保存します。
- ビューの内容を描画する
- サブビューを描画する
- グラデーションエッジを描画し、必要に応じてレイヤーを復元します
- 装飾を描画する (スクロールバーなど)
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
適切なタイミングでアニメーションを中止してください。
要約する
- 最初にこの問題に遭遇したとき、それを呼び出すことで
clearAnimation
解決しました。でも原因が分からないので、次に同じような問題に遭遇した時にはもう無理です。原理を理解するのに1分で解ける問題を1日もかかりませんでしたし、個人的にはそれだけの価値があると感じました。 - 事前にイベント配信の知識をマスターし、Touch イベントを分析する際の対象となります。
- Choreographer とインターフェイスの更新プロセスに慣れていると、アニメーションの実行プロセスをすぐに理解できます。
- 蓄積を重視しており、今は役に立たない知識もあるかもしれませんが、使ってみるとその快適さがわかります。
参考資料:
インターフェース更新の仕組み
アニメーション動作原理を見る