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”.