Refactoring Android activities with Custom Views

If you're not careful, your Android activities can get crippled with too much functionality. And the hard part is refactoring the code managing the views. That's what the Activity supposed to do.

Google developers recommend organizing views in Fragments. All the Activity should do is manage the Fragments lifecycle.

But some developers are favoring Custom Views. The Square dev team wrote about it in Advocating Against Android Fragments (the discussion). The main argument is the overcomplicated Fragment lifecycle.

So, what are Custom Views? We extend an Android View class and add custom functionality to it. A simple exemple:

public class NotesListView extends ListView {
    private MainActivity activity;

    public NotesListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.activity = (MainActivity) context;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        // "onCreate" code here
    }
    
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        // "onDestroy" code here
    }
}

And then use your Custom Views in layouts:

<com.example.NotesListView
    android:id="@+id/notes_list_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

onDetachedFromWindow is not the opposite of onFinishInflate (there's a onAttachedToWindowas well), but it serves most purposes.

Need to handle configuration changes? There's something similar to Activities, using Parcelables:

public class NotesListView extends ListView {
    
    // ...

    @Override
    public Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        bundle.putParcelable("instanceState", super.onSaveInstanceState());
        bundle.putSerializable(YOUR_KEY, yourValue);
        return bundle;
    }
    
    @Override
    public void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            yourValue = bundle.getSerializable(YOUR_KEY);
            state = bundle.getParcelable("instanceState");
        }
        super.onRestoreInstanceState(state);
    }
}

Neither Custom Views nor Fragments solve the communication problem though. Decoupling Activity functionality requires more messages/calls across Activities and Custom Views.

You can store the Activity (like the example above) and call it from the Custom Views. But it's not the best solution. It doesn't set expectations for other developers. It's hard to know what they should and shouldn't use.

A better practice would be defining an Interface for the Custom View, that any Activity can implement.

Still too much information going through views? Then use an event bus like Otto.


I'm still not entirely sold to Custom Views, but it's a simpler pattern to get used to. I'll share more feedback as I use them more in my projects.