Paul Brower bio photo

Paul Brower

Android development for fun and profit.

Email Twitter Github Stackoverflow Codementor

An Android UI pattern that I have run into in almost every project is the widget that needs to hide/show in response to an event. If the event was a message broadcast, usually you would do something like this:

View viewToHide = findViewById(R.id.my_view);
BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                viewToHide.setVisibility(View.INVISIBLE);
            }
        };
context.registerReceiver(receiver, new IntentFilter("com.mypackage.HIDE_WIDGET"));

If you needed to show it instead of hiding it on some other broadcast it would need twice as much code and then repeat that for each widget that needs to be shown or hidden. You could simplify it by creating a helper function, but you still end up with lots of boilerplate code. And if you have several activities it needs to be genericized into a Util class or the super class. Enter, the HideableLinearLayout

package com.mypackage.widgets;

public class HideableLinearLayout extends LinearLayout {

    private BroadcastReceiver hideOnReceiver;
    private BroadcastReceiver showOnReceiver;

    public HideableLinearLayout(Context context) {
        super(context);
    }

    public HideableLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setupListeners(context, attrs);
    }

    private void setupListeners(final Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable._widgets_HideableLinearLayout);
        final String hideOnIntent = a.getString(R.styleable._widgets_HideableLinearLayout_hideOn);
        final String showOnIntent = a.getString(R.styleable._widgets_HideableLinearLayout_showOn);

        if (!StringUtils.isEmpty(hideOnIntent)) {
            hideOnReceiver = new HideBroadcastReceiver();
            context.registerReceiver(hideOnReceiver, new IntentFilter(hideOnIntent));
        }

        if (!StringUtils.isEmpty(showOnIntent)) {
            showOnReceiver = new ShowBroadcastReceiver();
            context.registerReceiver(showOnReceiver, new IntentFilter(showOnIntent));
        }

        a.recycle();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        for(BroadcastReceiver broadcastReceiver : new BroadcastReceiver[]{hideOnReceiver,showOnReceiver}){
            try {
            	if (broadcastReceiver != null) {
            		getContext().unregisterReceiver(broadcastReceiver);
            	}
            }catch (Exception e){
                LogIt.e(this,e,"error trying to unregister receiver");
            }
        }
    }

    private class HideBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {

            setVisibility(View.GONE);
        }
    }

    private class ShowBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {

            setVisibility(View.VISIBLE);
        }
    }
}

We are assigning a custom attribute set so they need to be declared in an attrs.xml file: res/values/attrs.xml

<resources>
    <declare-styleable name=".widgets.HideableLinearLayout">
        <attr name="hideOn" format="string"/>
        <attr name="showOn" format="string"/>
    </declare-styleable>
</resources>

It can be used in a layout file like this:

<com.mypackage.widgets.HideableLinearLayout
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                app:hideOn="com.mypackage.HIDE_WIDGET"
                app:showOn="com.mypackage.SHOW_WIDGET"
                android:visibility="visible"
                >
    <TextView android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="FOO"
                >
</com.mypackage.widgets.HideableLinearLayout>

This makes the text view hide and show with one line like this

// hide
    	context.sendBroadcast(new Intent("com.mypackage.HIDE_WIDGET"));
        // show
    	context.sendBroadcast(new Intent("com.mypackage.SHOW_WIDGET"));

This pattern can be used great as a button toggle, like this:

<com.mypackage.widgets.HideableLinearLayout
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                app:hideOn="com.mypackage.HIDE_WIDGET"
                app:showOn="com.mypackage.SHOW_WIDGET"
                android:visibility="visible"
                >
    <TextView android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="FOO"
                >
</com.mypackage.widgets.HideableLinearLayout>
<com.mypackage.widgets.HideableLinearLayout
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                app:hideOn="com.mypackage.SHOW_WIDGET"
                app:showOn="com.mypackage.HIDE_WIDGET"
                android:visibility="gone"
                >
    <TextView android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="BAR"
                >
</com.mypackage.widgets.HideableLinearLayout>

this way “FOO” will be hidden and “BAR” will be shown on “com.mypackage.HIDE_WIDGET” and “FOO” will be shown and “BAR” will be hidden on “com.mypackage.SHOW_WIDGET”.