日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網(wǎng)站首頁 編程語言 正文

Android自定義View實現(xiàn)LayoutParams的方法詳解_Android

作者:dora ? 更新時間: 2023-04-01 編程語言

這一期我們來講一講LayoutParams這個玩意兒。Android入門的第一行代碼就牽扯到這個東西,然而,你真的理解夠了嗎?

第一層理解

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
</RelativeLayout>

layout_width和layout_height這個是不是最開始學的時候,就要搞清楚的基礎知識,match_parent代表填充屏幕,wrap_content代表包裹內(nèi)容。這些其實是系統(tǒng)控件定義的屬性,通過TypedArray進行解析。

第二層理解

val lp = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
lp.addRule(RelativeLayout.CENTER_VERTICAL)
lp.addRule(RelativeLayout.BELOW, viewId)
lp.setMargins(10, 20, 10, 20)

使用代碼動態(tài)布局的時候設置LayoutParams。

第三層理解

好了,知識是在不斷打破舊的認識中進步的,第一層實際還沒到LayoutParams,還只是AttributeSet。系統(tǒng)何時將布局中的AttributeSet解析成LayoutParams的呢?

@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new RelativeLayout.LayoutParams(getContext(), attrs);
}

protected LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}

ViewGroup有個關鍵的方法,generateLayoutParams()。

public LayoutParams(Context c, AttributeSet attrs) {
    super(c, attrs);

    TypedArray a = c.obtainStyledAttributes(attrs,
            com.android.internal.R.styleable.RelativeLayout_Layout);

    final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
    mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
            !c.getApplicationInfo().hasRtlSupport());

    final int[] rules = mRules;
    //noinspection MismatchedReadAndWriteOfArray
    final int[] initialRules = mInitialRules;

    final int N = a.getIndexCount();
    for (int i = 0; i < N; i++) {
        int attr = a.getIndex(i);
        switch (attr) {
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
                alignWithParent = a.getBoolean(attr, false);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
                rules[LEFT_OF] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
                rules[RIGHT_OF] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
                rules[ABOVE] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
                rules[BELOW] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
                rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
                rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
                rules[ALIGN_TOP] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
                rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
                rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
                rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
                rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
                rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
                rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
                rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
                rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
                rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
               break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
                rules[START_OF] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
                rules[END_OF] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
                rules[ALIGN_START] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
                rules[ALIGN_END] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
                rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
                rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
        }
    }
    mRulesChanged = true;
    System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);

    a.recycle();
}

這個代碼熟悉吧,這就是我們之前講過的自定義屬性啊!沒錯,xml布局中的屬性會先被解析成LayoutParams。那么我問你個問題,你覺得generateLayoutParams()和generateDefaultLayoutParams()的這個LayoutParams是給自己用的呢?還是給它的子控件用的呢?它是給子控件用的。自己的那個直接在構造方法中就從AttributeSet解析出來了。這樣你就理解了,為什么RelativeLayout的那些個

android:layout_centerVertical="true"
android:layout_alignParentEnd="true"

怎么全部定義在子控件里面了。然后ViewGroup的addView()方法中就可以帶上這個LayoutParams了。

/**
 * Adds a child view. If no layout parameters are already set on the child, the
 * default parameters for this ViewGroup are set on the child.
 *
 * <p><strong>Note:</strong> do not invoke this method from
 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
 *
 * @param child the child view to add
 * @param index the position at which to add the child
 *
 * @see #generateDefaultLayoutParams()
 */
public void addView(View child, int index) {
    if (child == null) {
        throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
    }
    LayoutParams params = child.getLayoutParams();
    if (params == null) {
        params = generateDefaultLayoutParams();
        if (params == null) {
            throw new IllegalArgumentException(
                    "generateDefaultLayoutParams() cannot return null  ");
        }
    }
    addView(child, index, params);
}

你不重寫generateLayoutParams()方法,怎么在添加子控件的時候,讓子控件用你的LayoutParams呢?

public static class LayoutParams extends ViewGroup.MarginLayoutParams {
}

以上是LinearLayout.LayoutParams的摘要,我們自定義ViewGroup的時候,是不是也可以繼承個ViewGroup的LayoutParams玩一玩呢?然后重寫generateLayoutParams()和generateDefaultLayoutParams()方法。

@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new LinearLayout.LayoutParams(getContext(), attrs);
}

這里return你的ViewGroup的LayoutParams,然后在你的ViewGroup的LayoutParams的構造方法中就可以解析自定義屬性attrs了。如果忘記了解析方式,我給你個提示,使用context的obtainStyledAttributes()方法。

大部分停留在第二層理解,你如果學會了第三層,那么你自定義View又可以玩出新的高度了。

原文鏈接:https://juejin.cn/post/7193761694865162300

欄目分類
最近更新