ViewStubについて聞いたことがあるはずですが、これらの詳細を理解していますか?

1 ViewStubとは

1. ViewStubは非表示で、サイズがなく、レイアウト位置を占有しないため、レイアウトの遅延読み込みに使用できます。

2. ViewStubが表示または展開()されると、レイアウトが読み込まれます(ViewStubを置き換えます)。したがって、ViewStubは、setVisibility(int)またはinflate()が呼び出されるまでビュー階層にとどまります。

3. ViewStubがロードされると削除され、占有していたスペースが新しいレイアウトに置き換えられます。

ここでは、ナレッジポイントのメモと高頻度のインタビューの問題分析、およびすべての人のためのいくつかのナレッジポイントのビデオの説明を含む、2か月のAndroidの高度なインタビュー分析のメモドキュメントを共有します 閲覧に影響を与えないように、カタログのスクリーンショットに表示されているコンテンツの一部を以下に示します。問題がある場合は、気に入って下のオンラインリンクをクリックして、無料で受け取ることができます。
Ali P6P7 [Android]高度な情報共有+重要な就職の面接の質問

2 ViewStub構築メソッド

最初に建設方法を見てください:

public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context);

    final TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.ViewStub, defStyleAttr, defStyleRes);
    // 要被加载的布局 Id
    mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
    // 要被加载的布局
    mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
    // ViewStub 的 Id
    mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
    a.recycle();

    // 初始状态为 GONE
    setVisibility(GONE);
    // 设置为不会绘制
    setWillNotDraw(true);
}

次に、主要なメソッドを見てから、初期化状態のsetVisibilityメソッドを見てみましょう。

// 复写了 setVisibility(int) 方法
@Override
@android.view.RemotableViewMethod
public void setVisibility(int visibility) {
    // private WeakReference<View> mInflatedViewRef;
    // mInflatedViewRef 是对布局的弱引用
    if (mInflatedViewRef != null) {
        // 如果不为 null,就拿到懒加载的 View
        View view = mInflatedViewRef.get();
        if (view != null) {
            // 然后就直接对 View 进行 setVisibility 操作
            view.setVisibility(visibility);
        } else {
            // 如果为 null,就抛出异常
            throw new IllegalStateException("setVisibility called on un-referenced view");
        }
    } else {
        super.setVisibility(visibility);
        // 之前说过,setVisibility(int) 也可以进行加载布局
        if (visibility == VISIBLE || visibility == INVISIBLE) {
            // 因为在这里调用了 inflate()
            inflate();
        }
    }
}

3インフレート()メソッド分析

コアはここにあり、このメソッドは通常使用されるときにしばしば呼び出されます。inflate()はキー読み込みの実装であり、コードは次のとおりです。

public View inflate() {
    // 获取父视图
    final ViewParent viewParent = getParent();

    if (viewParent != null && viewParent instanceof ViewGroup) {
        // 如果没有指定布局,就会抛出异常
        if (mLayoutResource != 0) {
            // viewParent 需为 ViewGroup
            final ViewGroup parent = (ViewGroup) viewParent;
            final LayoutInflater factory;
            if (mInflater != null) {
                factory = mInflater;
            } else {
                // 如果没有指定 LayoutInflater
                factory = LayoutInflater.from(mContext);
            }
            // 获取布局
            final View view = factory.inflate(mLayoutResource, parent,
                    false);
            // 为 view 设置 Id
            if (mInflatedId != NO_ID) {
                view.setId(mInflatedId);
            }
            // 计算出 ViewStub 在 parent 中的位置
            final int index = parent.indexOfChild(this);
            // 把 ViewStub 从 parent 中移除
            parent.removeViewInLayout(this);

            // 接下来就是把 view 加到 parent 的 index 位置中
            final ViewGroup.LayoutParams layoutParams = getLayoutParams();
            if (layoutParams != null) {
                // 如果 ViewStub 的 layoutParams 不为空
                // 就设置给 view
                parent.addView(view, index, layoutParams);
            } else {
                parent.addView(view, index);
            }

            // mInflatedViewRef 就是在这里对 view 进行了弱引用
            mInflatedViewRef = new WeakReference<View>(view);

            if (mInflateListener != null) {
                // 回调
                mInflateListener.onInflate(this, view);
            }

            return view;
        } else {
            throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
        }
    } else {
        throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
    }
}

膨張特性

1. ViewStubは1度しかインフレートできません。インフレート後、ViewStubオブジェクトは空に設定されます。つまり、ViewStubで指定されたレイアウトがInflateになると、ViewStubでそれを制御できなくなります。

2. ViewStubは、特定のビューではなく、レイアウトファイルをインフレートするためにのみ使用できます。もちろん、レイアウトファイルにビューを書き込むこともできます。

4 WeakReferenceの使用

弱参照管理オブジェクトの作成の使用、コードは次のとおりです

ここではgetメソッドを使用しています

@Override
@android.view.RemotableViewMethod(asyncImpl = "setVisibilityAsync")
public void setVisibility(int visibility) {
    if (mInflatedViewRef != null) {
        View view = mInflatedViewRef.get();
        if (view != null) {
            view.setVisibility(visibility);
        } else {
            throw new IllegalStateException("setVisibility called on un-referenced view");
        }
    } else {

    }
}

ここに弱参照オブジェクトを作成しました

public View inflate() {
    final ViewParent viewParent = getParent();
    if (viewParent != null && viewParent instanceof ViewGroup) {
        if (mLayoutResource != 0) {
            mInflatedViewRef = new WeakReference<>(view);
            return view;
        } else {
            throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
        }
    } 
}

5 ViewStubにサイズがないのはなぜですか

最初に、次のようにソースコードの一部を確認します。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(0, 0);
}

@Override
public void draw(Canvas canvas) {
}

@Override
protected void dispatchDraw(Canvas canvas) {
}

とても違うと思いますか?

drawとdispatchDrawは書き直されましたが、コードを見ると何もしません!そしてonMeasureは何もせず、直接setMeasuredDimension(0,0);ビュー領域を0に設定すると、元のViewStubはビューになりますただし、これはコンテンツを表示しない、またはコンテンツを表示しない特別なビューであり、ロード時にレイアウトからは見えません。

6 ViewStubが描画しないのはなぜですか

setWillNotDraw(true)メソッドを具体的に見てみましょう。コードは次のとおりです。

public void setWillNotDraw(boolean willNotDraw) {
    setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}

ビューでは、WILL_NOT_DRAWの定義は次のとおりです。

/**
 * This view won't draw. {@link #onDraw(android.graphics.Canvas)} won't be
 * called and further optimizations will be performed. It is okay to have
 * this flag set and a background. Use with DRAW_MASK when calling setFlags.
 * {@hide}
 */
static final int WILL_NOT_DRAW = 0x00000080;

WILL_NOT_DRAWを設定すると、onDraw()は呼び出されなくなり、描画処理をスキップすることでパフォーマンスが最適化されます。ViewGroupでは、初期化中にWILL_NOT_DRAWが設定され、コードは次のようになります。

public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);

    initViewGroup();
    initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
}

private void initViewGroup() {
    // ViewGroup doesn't draw by default
    if (!debugDraw()) {
        setFlags(WILL_NOT_DRAW, DRAW_MASK);
    }
    mGroupFlags |= FLAG_CLIP_CHILDREN;
    mGroupFlags |= FLAG_CLIP_TO_PADDING;
    mGroupFlags |= FLAG_ANIMATION_DONE;
    mGroupFlags |= FLAG_ANIMATION_CACHE;
    mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;

    if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
        mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
    }

    setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);

    mChildren = new View[ARRAY_INITIAL_CAPACITY];
    mChildrenCount = 0;

    mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
}

したがって、カスタムレイアウトを作成するときにonDraw()を呼び出して描画する必要がある場合は、初期化中にsetWillNotDraw(false)を呼び出す必要があります。ビューでWILL_NOT_DRAWの関連するソースコードをさらに読みたい場合は、PFLAG_SKIP_DRAWに関連するコードを確認できます。

7は繰り返し)(それを膨らませることができます

ViewStubオブジェクトは一度だけInflateにすることができ、その後ViewStubオブジェクトは空に設定されます。同時に、注意が必要な問題は、ViewStubオブジェクトをインフレートした後、再度インフレートできないことです。そうしないと、エラーが発生します。ViewStubには、null以外のViewGroup viewParentが必要です。

実際、ソースコードを見るのは簡単です。

public View inflate() {
    //获取viewStub的父容器对象
    final ViewParent viewParent = getParent();

    if (viewParent != null && viewParent instanceof ViewGroup) {
        if (mLayoutResource != 0) {
            final ViewGroup parent = (ViewGroup) viewParent;
            //这里是加载布局,并且给它设置id
            //布局的加载是通过LayoutInflater解析出来的
            final View view = inflateViewNoAdd(parent);
            //这行代码很重要,下面会将到
            replaceSelfWithView(view, parent);

            //使用弱引用
            mInflatedViewRef = new WeakReference<>(view);
            if (mInflateListener != null) {
                mInflateListener.onInflate(this, view);
            }
            return view;
        } else {
            //如果已经加载出来,再次inflate就会抛出异常呢
            throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
        }
    } else {
        throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
    }
}

実際、以下のようにネットワークから取得した写真でそれを理解することもできます

つまり、上記のメソッドが呼び出されると、ViewStubはnullになるため、このオブジェクトを使用するには、nullポインターの問題に特別な注意が必要です。

8 ViewStubはマージをサポートしていません

マージタグを含むレイアウトをViewStubに導入することはできません。そうでない場合、エラーが報告されます。

android.view.InflateException:バイナリXMLファイルの行#1:有効なViewGroupルートとattachToRoot = trueでのみ使用できます

9 ViewStubの使用シナリオ

一般的なアプリのほとんどにこのような機能があります。読み込まれたデータが空の場合、データが空のビューが表示され、データの読み込みに失敗した場合、失敗した読み込みに対応するUIが表示されます。ネットワークがない場合、ネットワークのないUIが読み込まれ、サポートされます。クリックして再試行すると、白い画面のユーザーエクスペリエンスよりも優れています。

一般にページ状態切り替え管理と呼ばれます。一般的に、読み込み、読み込み失敗、空のデータ、その他の状態のUIスタイルは、アプリ内のすべてのページで一貫している必要があります。つまり、グローバルに統合され、ローカルのカスタマイズもサポートされている必要があります。

ViewStubの利点は、上記のシナリオでは、すべてのコンテンツを表示する必要がないことです。ユーザーが表示する必要がある場合は、一部のビュービューを非表示にして、現在のレイアウトに読み込むことができます。現時点では、ViewStubを使用できます。コントロール、これはリソース消費を削減し、初期読み込み速度を速くすることができます。

次に、以前に開発および使用されたステートマネージャーのオープンソースライブラリがあり、ViewStubコントロールを使用して、ビューステートの切り替えとアクティビティを完全に分離しています。ビルダーモードを使用して、必要な状態ビューを自由に追加します。データを設定できます。データは空で、データエラー、ネットワークエラー、メディアと複数の状態の読み込み、カスタム状態レイアウトのサポートが可能です。性能には全く影響を与えないと言えますが......

10 ViewStubサマリー分析

ソースコードの原理を分析します。どのステップが認識されても、最終的な目標はアプリケーションにあります。つまり、ソースコードを見て得られた知識が実際の開発に使用されます。

488件のオリジナル記事を公開 85 件を賞賛 230,000回の閲覧+

おすすめ

転載: blog.csdn.net/Coo123_/article/details/104437061