JavaSwing基礎のLayoutレイアウトに関する知識詳細


一、View layout方法
まず、ViewRootImplから説明するか、インターフェースの描画はperformMeasure、performLayout方法をトリガし、performLayout方法でmViewのlayout方法を呼び出して、複数のビューのレイアウト作業を開始する。

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
 
        final View host = mView;
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    }
mViewは全部分かりました。最上階のView――DecorViewです。DecorViewのラyout方法を見に行きます。
すみません、DecorViewにはlayout方法がありません。
ですから、直接にViewのlayout方法を見てみます。

public void layout(int l, int t, int r, int b) {
 
        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
 
        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            onLayout(changed, l, t, r, b);
        }
    }
 
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    }
  • は、まず、方法は、それぞれviewの 、 、 、 の4つの値を表す4つのパラメータを導入する。
  • は、次いで、setOpticalFrame方法またはsetFrame方法によって、レイアウトパラメータが変更されたかどうかを判断する。
  • 具体的な判断過程は古い上下左右の値と新しい上下左右の値を比較することであり、論理はsetFrame方法である。
    
    protected boolean setFrame(int left, int top, int right, int bottom) {
            boolean changed = false;
     
            if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
                changed = true;
     
                // Remember our drawn bit
                int drawn = mPrivateFlags & PFLAG_DRAWN;
     
                int oldWidth = mRight - mLeft;
                int oldHeight = mBottom - mTop;
                int newWidth = right - left;
                int newHeight = bottom - top;
                boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
     
                // Invalidate our old position
                invalidate(sizeChanged);
     
                mLeft = left;
                mTop = top;
                mRight = right;
                mBottom = bottom;
                mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
            }
            return changed;
        }
    
    上下左右にパラメータ値が変化したら、このViewのレイアウトが変更されたと説明し、Viewの幅の高さ(newWidth、newHeight)を再計算し、Viewの新たな上下左右のパラメータ値を割り当てた。
    このlayoutの方法では主に4つのパラメータに関連しています。mLeft、mTop、mBottom、mRightはそれぞれViewの左座標、上座標、下座標、右座標を表しています。Viewを長方形として理解して、この4つの値を決定して、Viewの長方形の4つの頂点値を決定できます。
    ですから、layout方法は一体何をしましたか? に入力され、 に割り当てられ、終了することです。
    そして、これらの値に基づいて、Viewの幅のような一連のパラメータを取得することができます。
    
    public final int getWidth() {
            return mRight - mLeft;
        }
    これで、Viewのlayout方法は終了し、主に上下左右のパラメータの割当値によってViewのレイアウトが完了し、非常に簡単である。
    次にViewGroupを見ます。
    二、View Group layout方法
    
    @Override
        public final void layout(int l, int t, int r, int b) {
            if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
                if (mTransition != null) {
                    mTransition.layoutChange(this);
                }
                super.layout(l, t, r, b);
            } else {
                mLayoutCalledWhileSuppressed = true;
            }
        }
    
    layoutViewGroupの配置過程は同じで、自分の位置を確定しましたか?Viewの子供Viewはどうすればいいですか?大丈夫です。先ほどViewGroupの方法を話した時に、layoutの方法が漏れました。でも、この方法はonLayoutの中で空いています。Viewの中で抽象的な方法になりました。
    
    @Override
        protected abstract void onLayout(boolean changed,
                int l, int t, int r, int b);
    つまり、ViewGroupの任意の方法は、ViewGroupのレイアウト配置を完了するために実装されなければならない。
    具体的な配置ロジックは、 View方式でonLayoutのlayoutメソッドを呼び出し、各サブビューのレイアウトを完成させ、最終的に描画作業を完了することである。
    次に、 Viewを自分で実現して、次のセクションの ( LinearLayout)とこのセクションのonMearsureを復習します。
    三、カスタム垂直レイアウトVetical Layout
    まず、私達はこのカスタムonLayoutの役割を決定したいです。これは垂直方向のViewGroupと同様の機能です。このView Groupの下のLinearLayoutは、垂直ライン順に順次下に排出してもいいです。私たちは Viewという名前をつけました。
    View Groupを継承する
    まず、このレイアウトは必ずVerticalLayoutから継承され、対応する構成方法を実現する。
    
    public class VerticalLayout : ViewGroup {
     
        constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int = 0) : super(
            context,
            attrs,
            defStyleAttr
        )
     
        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        }
    }
    
    generate LayoutParaamsを書き換える方法
    また、View Groupをカスタマイズするには、Generate LayoutParaamsが必要です。このステップは、私たちのView GroupがMarginをサポートするために、その後にMargine LayoutParaamsを通じてサブViewのMargin値を取得することができます。
    
    override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams? {
            return MarginLayoutParams(context, attrs)
        }
    測定方法を書き換える
    その後、私たちは私たちのレイアウトを測定する必要があります。つまり、書き換えViewGroup方法です。
    この方法では、私たちのレイアウトを測定し、測定した幅の高さをonMeasure方法に導入し、測定を完了する必要がある。
    
    protected final void setMeasuredDimension(int measuredWidth, int measuredHeight)
    前に述べたように、setMeasuredDimension方法は2つのパラメータ、onMeasureおよびwidthMeasureSpecに伝達される。
    親Viewは、現在のビューのheightMeasureSpecと親ViewのLayoutParamsに基づいて計算され、現在のビューに対して期待される を含む。
  • 測定モードがMeass reSpec.EXACT LY
  • であるとき。
    つまり、幅が広い場合や高い場合は、現在のレイアウトViewの幅、つまり親Viewに設定して、測定サイズを設定してください。例えば、幅が であるなら、この固定値に直接呼び出し400dpを再測定する必要はない。
  • 測定モードがMeass reSpec.AT_である場合MOSTまたはUNSPECIFIED:
  • この場合、親Viewの現在のビューに対する要求が固定されていないことを説明し、このsetMeasuredDimensionの高さを設定するとVerticalLayoutの大きさが任意であるか、または最大値を超えない場合である。この適応高さはどのように計算するべきかを設計者だけが知っているので、改めて高度測定をしなければなりません。具体的にはwrap_contentは垂直な直線配置であり、高さは自然とすべてのサブビューの高さの和である。
    これでVerticalLayout方法の論理もほぼ分かりました。
    
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec)
            //              
            val widthMode = MeasureSpec.getMode(widthMeasureSpec)
            val heightMode = MeasureSpec.getMode(heightMeasureSpec)
            val sizeWidth = MeasureSpec.getSize(widthMeasureSpec)
            val sizeHeight = MeasureSpec.getSize(heightMeasureSpec)
     
            var mHeight = 0
            var mWidth = 0
     
            //   View,     
            for (i in 0 until childCount) {
                val childView = getChildAt(i)
                //   View    
                measureChild(childView, widthMeasureSpec, heightMeasureSpec)
                val lp = childView.layoutParams as MarginLayoutParams
                val childWidth = childView.measuredWidth + lp.leftMargin + lp.rightMargin
                val childHeight = childView.measuredHeight + lp.topMargin + lp.bottomMargin
     
                //        
                mWidth = Math.max(mWidth, childWidth)
                //      
                mHeight += childHeight
            }
     
            //    
            setMeasuredDimension(
                if (widthMode == MeasureSpec.EXACTLY) sizeWidth else mWidth,
                if (heightMode == MeasureSpec.EXACTLY) sizeHeight else mHeight
            )
        }
    
    主なロジックはViewを遍歴し、onMeasureの実際の幅の高さを導出することである。
  • 最終View Groupの高さ=すべてのサブビューの(高+margin値)
  • 最終的なView Groupの幅=最大の子供Viewの(幅+margin値)
  • 最後の呼び出しVerticalLayout 測定モードにより、幅の高さが伝わります。
    レイアウトの方法を書き換えるonLayout
    上記で述べたように、setMeasuredDimensionとして、子供Viewの通常のレイアウト配置を保証するために、ViewGroup方法を書き換えなければならない。
    垂直線形配置onLayoutもまたそうであるが、このレイアウトにおいてVerticalLayout方法のキー論理は何であるか?
    それとも、位置、つまりonLayoutの4つのパラメータ値を決定し、 、 、 、 において最も重要なパラメータはこれである。
    各ViewのVerticalLayout値は前のViewのbottom値でなければなりません。つまり前のViewに続いて並べられます。これがtop の効果です。ですから、各Viewのtop値を動的に計算する必要があります。つまり、次のビューのtop値として、Viewの高さを積み重ねていくのです。
    
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
            var childWidth = 0
            var childHeight = 0
            var childTop = 0
            var lp: MarginLayoutParams
     
            //   View,     View
            for (i in 0 until childCount) {
                val childView = getChildAt(i)
                childHeight = childView.measuredHeight
                childWidth = childView.measuredWidth
                lp = childView.layoutParams as MarginLayoutParams
     
                //    top 
                childTop += lp.topMargin
     
                //   View
                childView.layout(
                    lp.leftMargin,
                    childTop,
                    lp.leftMargin + childWidth,
                    childTop + childHeight
                );
     
                childTop += childHeight + lp.bottomMargin
            }
        }
    
    ロジックは簡単です。
  • は固定のサブビューのleftMaginである。
  • topは、アキュムレータ計算のサブビューの高さ+Margin値である。
  • leftは、left+サブビューの幅である。
  • topはtop+サブビューの高さです。
  • 最後にサブビューのlayoutメソッドを呼び出して、各サブビューをレイアウトします。
    成功しました。最後に私達のこの垂直直線レイアウトをカスタマイズした効果を見てみましょう。
    四、効果展示
    
    <com.panda.studynote3.VerticalLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
     
            <TextView
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:text="   "
                android:textSize="20sp"
                android:textColor="@color/white"
                android:background="@color/design_default_color_primary"
                />
     
            <TextView
                android:layout_width="300dp"
                android:layout_height="200dp"
                android:layout_marginTop="20dp"
                android:background="@color/cardview_dark_background"
                android:textSize="20sp"
                android:textColor="@color/white"
                android:text="   "
                />
     
            <TextView
                android:layout_width="140dp"
                android:layout_height="100dp"
                android:text="  "
                android:layout_marginLeft="10dp"
                android:layout_marginTop="10dp"
                android:textSize="20sp"
                android:gravity="center"
                android:textColor="@color/black"
                android:background="@color/teal_200"
                />
     
        </com.panda.studynote3.VerticalLayout>
    

    ここで、Javaの基礎となるLayoutレイアウトに関する詳細な文章を紹介します。Java Layoutレイアウトの内容については、以前の文章を検索したり、下記の関連記事を見たりしてください。これからもよろしくお願いします。