Skip to content

Instantly share code, notes, and snippets.

@julien-h
Last active December 3, 2016 14:13
Show Gist options
  • Save julien-h/85c2310405bd3d3c834bd6114b3f0f7e to your computer and use it in GitHub Desktop.
Save julien-h/85c2310405bd3d3c834bd6114b3f0f7e to your computer and use it in GitHub Desktop.
Example showing how to design a decoupled fragment UI.

In android development, a fragment is a reusable piece of User Interface. It should contains no business logic and (in the best scenario) should be usable by different apps. Here are some thought about how this can be achieved.

Suppose we want to design a fragment able to display a list of Note with their content, title, date of creation. We want to open the NoteEditor when a note is clicked, and we want to be able to delete one or more notes at once.

capture d ecran 2016-12-03 a 14 36 11

Step 1: design the view

"Ok, I want to display the notes like this. I'm going to display their title in bold here, the creation date in small font there and an excerpt of their content here. I want checkboxes on the left, and I'm going to add two button at the bottom. Oh and I'm putting the search bar here to filter the list. etc."

Step 2: identify the different types of data and actions that your view want to communicate

  • filtering the list doesn't need to be communicated to another class
  • I need to fetch the list of notes with their {title, content, date of creation} that I'm displaying
  • I need to tell the controler to delete some notes (those that are checked)
  • I need to tell the controler to open a note (when it's clicked)
  • I need to tell the controler to create a note (when the add button is clicked)

Step 3: reword actions using generic names

  • data to receive: Items { title, summary, date }
  • action: process checked items
  • action: item clicked
  • action: primary button clicked

Step 4: design an interface to allow communication with your fragment

class ListItem {
    String title;
    String summary;
    Date date;
}
interface ListPresenterController {
    void processItems(List<ListItem> items);
    void primaryButtonClicked();
    void itemClicked(ListItem item);
}
interface ListPresenter {
    void receiveItems(List<ListItem> items, ListPresenterController c);
    void setPrimaryButtonText(String text);
    void setProcessButtonText(String text);
    void displayFeedback(String feedback);
    
    void onProcessItemsFailure(List<ListItem> unprocessed, String errorMessage);
    void onProcessItemsSuccess(List<ListItem> processed);
    
    void onItemClickedSuccess(ListItem item);
    void onItemClickedFailure(ListItem item, String errorMessage);
    
    void onPrimaryButtonClickedSuccess();
    void onPrimaryButtonClickedFailure(String errorMessage);
}

Step 5: create the fragment, implement the interface

class ListFragment extends Fragment implements ListPresenter { ... }

Step 6: create an adapter for readability

Now, do not use this fragment directly in your controller ! You want the controller to use an interface without knowing if it's implemented by a ListPresenter or something else.

Instead, design an interface to let the controler communicate clearly.

interface NotesPresenterController {
    void deleteNotes(List<Note> toDelete);
    void createNote();
    void openNote(Note toOpen);
}
interface NotesPresenter {
    void receiveNotes(List<Note> items);
    
    void onDeleteNoteFailure(List<Note> notDeleted);
    void onDeleteNoteSuccess(List<Note> deleted);
    
    void onOpenNoteFailure(Note notOpened);
    void onOpenNoteSuccess(Note opened);
    
    void onCreateNoteFailure();
    void onCreateNoteSuccess(Note created);
}

And implement it using an adapter:

class NotesPresenterFragmentAdapter implements NotesPresenter {

    NotesPresenterFragmentAdapter(NotesPresenterController controller, ListFragment adapted) {
        adapted.setPrimaryButtonText("Add");
        adapted.setProcessButtonText("Deleted selected notes");
        // etc.
    }
    
    void receiveNotes(List<Note> notes) { 
        List<ListItem> items = notes.map(...); // process the list of notes 
        adapted.receiveItems(items, this);
    }
    
    // etc.
}
@julien-h
Copy link
Author

julien-h commented Dec 3, 2016

capture d ecran 2016-12-03 a 14 36 11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment