S-K'
S-K'

Reputation: 3209

How to build a custom view that will add its content to one of its children?

I have the following android layout, this code is duplicated across all activity layouts in my application:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/activity_actionbar_background"/>

    <FrameLayout
        android:id="@+id/test_activity_content_frame_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="?attr/actionBarSize">

        <!-- content here -->

    </FrameLayout>

</RelativeLayout>

I would like to refactor this layout into a view that can contain nested items and then be used in an activity layout like:

<com.my.app.ContentLayout>
    <!-- content here -->
</com.my.app.ContentLayout>

How would I develop my ContentLayout view class to achieve this?

Upvotes: 0

Views: 299

Answers (1)

user
user

Reputation: 87064

You can refactor that layout in a custom component, you just need to take care of placing the content that you declare for CustomLayout in the inner FrameLayout(by overriding the default behavior of the addView() methods). Check the example below:

<!-- R.layout.Inner_content basic inner content that will be added to our custom component -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <FrameLayout
        android:id="@+id/dummy_id" <!-- we need an id that we could count on -->
        android:layout_width="match_parent"
        android:layout_height="?android:attr/actionBarSize"
        android:background="@color/activity_actionbar_background"/>

    <FrameLayout
        android:id="@+id/test_activity_content_frame_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="?android:attr/actionBarSize">

        <!-- content here -->

    </FrameLayout>

</merge>

And the component itself will be:

public class CustomLayout extends RelativeLayout {

    private FrameLayout mContent;

    public CustomLayout(Context context) {
        super(context);
        init(context);
    }

    public CustomLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public CustomLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View content = inflater.inflate(R.layout.inner_content, this);
        mContent = (FrameLayout) findViewById(R.id.test_activity_content_frame_layout);
    }

    @Override
    public void addView(View child) {
        if (isInnerContent(child)) {
            super.addView(child);
        } else {
            mContent.addView(child);
        }
    }

    @Override
    public void addView(View child, int index) {
        if (isInnerContent(child)) {
            super.addView(child, index);
        } else {
            mContent.addView(child, index);
        }
    }

    @Override
    public void addView(View child, int width, int height) {
        if (isInnerContent(child)) {
            super.addView(child, width, height);
        } else {
            mContent.addView(child, width, height);
        }
    }

    @Override
    public void addView(View child, ViewGroup.LayoutParams params) {
        if (isInnerContent(child)) {
            super.addView(child, params);
        } else {
            mContent.addView(child, params);
        }
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (isInnerContent(child)) {
            super.addView(child, index, params);
        } else {
            mContent.addView(child, index, params);
        }
    }

    /**
     * This method will be used in the addView() methods of ContentLayout to add the children
     * to the proper container.
     *
     * @param newChild the new child view to test
     * @return false if the child should be added to the content FrameLayout,
     * for true add the child to the parent RelativeLayout
     */
    private boolean isInnerContent(View newChild) {
        if ((newChild instanceof FrameLayout) && (newChild.getId() == R.id.dummy_id ||
                newChild.getId() == R.id.test_activity_content_frame_layout)) {
            return true;
        }
        return false;
    }

}

Upvotes: 2

Related Questions