在其构造函数中设置自定义ViewGroup / composite View LayoutParameters

时间:2016-04-12 15:49:25

标签: android android-layout android-view android-viewgroup

我正在扩展AppBarLayout以制作我自己的版本。我的目标是在运行时设置一些LayoutParameters,例如。 AppBar的高度。

我得到一个NPE,如果我尝试设置任何Paramteres,我想因为尚未创建和设置LayoutParameters。

public MyAppBar(Context context, AttributeSet attrs) {
    super(context, attrs);
    ...

    LayoutInflater.from(context).inflate(R.layout.layout_my_app_bar, this, true);

    ViewGroup.LayoutParams params = this.getLayoutParams();
    params.height = calculateExpandedHeight(selectedAspectRatio);
    this.setLayoutParams(params);

    ...
}

我目前的解决方法是在LayoutParams内设置onMeasure

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if(!hasSetHeight) {
        ViewGroup.LayoutParams params = this.getLayoutParams();
        params.height = mExpandedHeight;
        this.setLayoutParams(params);
    }
}

有没有办法在自定义ViewGroup /复合视图的构造函数中设置LayoutParameters?

layout_my_app_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/tools">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:popupTheme="@style/AppTheme.PopupOverlay"/>
</merge>

1 个答案:

答案 0 :(得分:0)

嗯,您获得的NullPointerException与LayoutParams为空相关,因为系统尚未布置View。这为您提供了两个选项:您可以延迟参数调整直到View布局,或者使用View的内部onMeasure(类似于您的使用方式)来告诉它如何布局。

前者可以使用ViewTreeOberserver中的OnGlobalLayoutListener实现:

public MyAppBar(Context context, AttributeSet attrs) {
    super(context, attrs);
    ...

    getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                ViewGroup.LayoutParams params = this.getLayoutParams(); 
                // Set the params here

                removeOnGlobalLayoutListener(MyAppBar.this, this); // Remove the listener
            }
        });
    requestLayout();
}

@SuppressLint("NewApi")
public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener) {
    if(Build.VERSION.SDK_INT < 16)
        v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
    else v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}

后者可能(略微)更好,因为你只应用看似宽高比的东西,就是使用onMeasure方法并将转换应用到那里的widthMeasureSpec。 e.g。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int adjustedHeight = MeasureSpec.getSize(widthMeasureSpec) / selectedAspectRatio

    super.onMeasure(widthMeasureSpec, 
        MeasureSpec.makeMeasureSpec(adjustedHeight, MeasureSpec.getMode(MeasureSpec.EXACTLY)));
}

请记住,onMeasure并没有真正考虑其他MeasureSpec的可能性,而且我做了一个巨大的假设,你总是知道宽度,并希望高度根据它自行调整。如果你想要更有活力并考虑其他案例,easy View measuring做了很好的解释。只是想到这可能会指出你正确的方向。