在自定义复合视图中使用<include>会导致onFinishInflate()

时间:2016-04-04 12:24:20

标签: java android android-layout android-custom-view android-xml

我试图实现一个简单的自定义拨号盘视图。在我尝试使用<include>包含&#34;模板&#34;之前,一切正常。对于我的按钮而不是显式定义XML文件中的每个按钮。由于我将有12个几乎相同的按钮,我认为我只是通过这样做使我的XML更整洁更短。

问题是在我的自定义视图中的onFinishInflate()中,当我调用findViewById(R.id.dialpad_view_btn1)时,它返回null。我当然正在为我的dialpad_view_btn1之一分配ID <include>。如果我只是明确定义按钮而不是从我的&#34;模板&#34;它工作正常。是否有任何解决方法,或者我应该接受findViewById()<include>不能很好地合作?

DialPadView.java

public class DialPadView extends android.support.v7.widget.GridLayout {


    public DialPadView(Context context) {
        super(context);
        initializeViews(context);
    }

    public DialPadView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initializeViews(context);
    }

    public DialPadView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initializeViews(context);
    }

    private void initializeViews(Context context) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.dialpad_view, this);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        // This call to findViewById() results in a NullPointerException
        ((ImageButton) findViewById(R.id.dialpad_view_btn1)).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // I'm just a dummy
            }
        });
    }

}

dialpad_view.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.GridLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.me.myproject.DialPadView"
    xmlns:grid="http://schemas.android.com/apk/res-auto"
    android:id="@+id/dialpad_grid_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerHorizontal="true"
    grid:alignmentMode="alignMargins"
    grid:columnCount="3"
    grid:rowCount="4">

    <include
        android:id="@+id/dialpad_view_btn1"
        layout="@layout/dialpad_button" />

    <include
        android:id="@+id/dialpad_view_btn2"
        layout="@layout/dialpad_button" />


</android.support.v7.widget.GridLayout>

dialpad_button.xml

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

    <Button
        android:layout_width="0dp"
        android:layout_height="0dp"
        grid:layout_columnWeight="1"
        grid:layout_rowWeight="1"
        grid:layout_gravity="fill"
        android:layout_margin="5dp"
        android:scaleType="centerInside"
        android:text="Test" />

</merge>

1 个答案:

答案 0 :(得分:0)

ID问题:

您遇到的问题是混合<include><merge>标签造成的。 请注意,<merge>有不同的用途,在这种情况下不应使用,因此请仅使用<incluce>并在dialpad_button.xml文件中设置<Button>根。然后应正确分配所有ID。

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:grid="http://schemas.android.com/apk/res-auto">
    android:layout_width="0dp"
    android:layout_height="0dp"
    grid:layout_columnWeight="1"
    grid:layout_rowWeight="1"
    grid:layout_gravity="fill"
    android:layout_margin="5dp"
    android:scaleType="centerInside"
    android:text="Test" />

详细说明:

如果您对此问题感到好奇,可以查看LayoutInflater源文件中的parseInclude()方法。您可以找到代码处理<merge>通胀。您可以看到,<include>未调用将布局和id属性传递给<merge>子项的代码,因为<merge>应该用于不同的场景。有:

if (TAG_MERGE.equals(childName)) {
    // Inflate all children.
    rInflate(childParser, parent, childAttrs, false);
} else {
    [...]

    // We try to load the layout params set in the <include /> tag. If
    // they don't exist, we will rely on the layout params set in the
    // included XML file.

    [...]

    // Attempt to override the included layout's android:id with the
    // one set on the <include /> tag itself.
    TypedArray a = mContext.obtainStyledAttributes(attrs,
        com.android.internal.R.styleable.View, 0, 0);
    int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
    // While we're at it, let's try to override android:visibility.

    [...]
}

其他问题:

这与您丢失ID的问题并不严格相关,但正如pskink提到的那样,您在自定义<android.support.v7.widget.GridLayout>内错误地夸大了DialPadView,因此您最终会得到这样的层次结构:

<DialPadView>
    <android.support.v7.widget.GridLayout>
        <Button/>
        <Button/>
    </android.support.v7.widget.GridLayout>
</DialPadView>

<DialPadView>已经是android.support.v7.widget.GridLayout的一个实例,所以基本上你最终在GridLayout内有GridLayout。除了不是最佳之外,它可能会导致布局看起来与您想象的外观和行为不同。