自定义视图 - 将视图移动到嵌套布局中

Custom View - Move Views into nested Layout

我想创建一个自定义视图,它显示一张包含以下内容的卡片:

所以我只是扩展了一个 LinearLayout 并用它扩充了我的布局文件:

public class FrageContainerView extends LinearLayout {
    private TextView objTextViewCaption;
    private TextView objTextViewDescription;

    private String caption;
    private String description;

    private LinearLayout objLayoutInner;

    public FrageContainerView(Context context) {
        this(context, null);
    }

    public FrageContainerView(Context context, AttributeSet attrs) {
        super(context, attrs);

        initialize(context, attrs);
    }

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

        initialize(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public FrageContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        initialize(context, attrs);
    }

    private void initialize(Context context, AttributeSet attrs) {
        TypedArray a =
                context.obtainStyledAttributes(attrs, R.styleable.options_frageContainerView, 0, 0);

        caption = a.getString(R.styleable.options_frageContainerView_caption);
        description = a.getString(R.styleable.options_frageContainerView_description);

        a.recycle();

        setOrientation(LinearLayout.HORIZONTAL);
        setGravity(Gravity.CENTER_VERTICAL);

        LayoutInflater inflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.view_fragecontainer, this, true);

        objLayoutInner = (LinearLayout) findViewById(R.id.linearlayout_inner);
        objTextViewCaption = (TextView) findViewById(R.id.textview_caption);
        objTextViewDescription = (TextView) findViewById(R.id.textview_description);

        objTextViewCaption.setText(caption);
        objTextViewDescription.setText(description);
    }

使用我的自定义视图的用户应该能够像这样在 XML 中添加自己的组件:

    <FrageContainerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        custom:caption="Hallo"
        custom:description="LOLOLOL"
        android:background="#FF00FF00">
        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="sdsdfsdf"/>
        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="sdsdfsdf"/>
        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="sdsdfsdf"/>
        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="sdsdfsdf"/>

    </FrageContainerView>

当前状态是,定义的 EditText 在我的自定义视图中膨胀。我希望将示例中的 EditText 添加到 InnerLayout,而不是将它们附加到自定义视图。

执行此操作的最佳方法是什么?

这个问题的实质是如何将子视图添加到 GroupView 本身就是自定义布局的子视图。

这在编程上相对简单,但在 XML 中更像是一个问题。

Androids LayoutInflater 逻辑解释 XML 文件中的嵌套级别,并在其创建的视图层次结构中构建相同的结构。
您的示例 XML 将 4 EditText 视图定义为 FrageContainerView 的第一层子视图,但您希望将它们创建为 FrageContainerView 的第二层子视图,这些视图位于您的 [=17= 中].这将意味着更改 Androids LayoutInflater,这是整个 Android 系统的核心组件。

要以编程方式执行此操作,您可以执行以下操作:

public class FrageContainerView extends LinearLayout {
    private TextView objTextViewCaption;
    private TextView objTextViewDescription;

    private String caption;
    private String description;

    private LinearLayout objLayoutInner;

    public FrageContainerView(Context context) {
        this(context, null);
    }

    public FrageContainerView(Context context, AttributeSet attrs) {
        super(context, attrs);

        initialize(context, attrs);
    }

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

        initialize(context, attrs);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public FrageContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        initialize(context, attrs);
    }

    private void initialize(Context context, AttributeSet attrs) {

        // Create your 3 predefined first tier children

        // Create the Caption View
        objTextViewCaption = new TextView(context);
        // You can add your new Views to this LinearLayout
        this.addView(objTextViewCaption)

        // Create the Description View
        objTextViewDescription = new TextView(context);
        // You can also provide LayoutParams when you add any of your new Views if you want to
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        this.addView(objTextViewDescription, params);

        // Create your inner LinearLayout
        objLayoutInner = new LinearLayout(context);
        objLayoutInner.setOrientation(VERTICAL);
        // Add it
        this.addView(objLayoutInner);

        TypedArray a =
                context.obtainStyledAttributes(attrs, R.styleable.options_frageContainerView, 0, 0);

        caption = a.getString(R.styleable.options_frageContainerView_caption);
        description = a.getString(R.styleable.options_frageContainerView_description);

        a.recycle();

        setOrientation(LinearLayout.HORIZONTAL);
        setGravity(Gravity.CENTER_VERTICAL);

/** 
 * Oops! Only just spotted you're inflating your three predefined views 
 * here. It's fine to do this instead of programmatically adding them as I
 * have above. Obviously they should only be added once, so I've commented out
 * your version for the moment.
 **/

//        LayoutInflater inflater =
//                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//        inflater.inflate(R.layout.view_fragecontainer, this, true);
//
//        objLayoutInner = (LinearLayout) findViewById(R.id.linearlayout_inner);
//        objTextViewCaption = (TextView) findViewById(R.id.textview_caption);
//        objTextViewDescription = (TextView) findViewById(R.id.textview_description);

        objTextViewCaption.setText(caption);
        objTextViewDescription.setText(description);
    }
}

/** Public method for adding new views to the inner LinearLayout **/
public void addInnerView(View view) {
    objLayoutInner.addView(view);

}

/** Public method for adding new views to the inner LinearLayout with LayoutParams **/
public void addInnerView(View view, LayoutParams params) {
    objLayoutInner.addView(view, params);
}

您可以在您的代码中使用它,如下所示:

FrageContainerView fragContainerView = (FrageContainerView) findViewById(R.id.my_frag_container_view);

TextView newView = new TextView(context);
newView.setText("whatever");

fragContainerView.addInnerView(newView);

你可以做的另一件事是

1) 缓存所有当前子节点并全部移除

ArrayList<View> nestedViews = ViewUtil.getAllChildren(this); removeAllViews();

2) 膨胀你已经包含东西的布局

View myLayout = LayoutInflater.from(getContext()).inflate(R.layout.my_layout_with_stuff, null);

3) 将缓存的视图添加到新膨胀的布局中,并将其添加到根后面

    for (View view : nestedViews) {
        myLayout.<ViewGroup>findViewById(R.id.contentLayout).addView(view);
    }

    addView(myLayout);