DEV Community

Samuel Agbesinyale
Samuel Agbesinyale

Posted on • Updated on

Too many instances of View.setVisibility(...)?


Find yourself "navigating" through multiple views with an inordinate amount of setVisibilty(...) instances in your code? Here's how to simplify that process and improve the maintainability of your code.

The problem

I inherited a project a few months back. In this project, a fragment needed to manage several views, but each of these views had too little functionality to warrant creating a separate fragment for each of them.

The original author, also making the same assertion decided to cycle through the views by controlling their visibility parameter.

view1.visibility = View.GONE
view2.visibility = View.VISIBLE
Enter fullscreen mode Exit fullscreen mode

You could find several instances of the code above in the fragment class.

Now, this approach was simple enough and worked, but there were a few issues.

  1. Anytime a new view was added, the navigation logic needed to be updated.

  2. Also, as the number of views increased, the code became cluttered pretty quickly because the number of setVisibility() instances increased.

  3. Anytime the user wanted to route back to the previous view, we first had to check which view was currently visible and based on the result, determine which view to display.

This approach, though not incorrect, made it challenging for another developer to understand the navigation flow. It became even more challenging to make simple changes like adding new views or even altering the existing flow.

eg: if the initial flow was
view1 -> view2 -> view3
and it became necessary to refactor the order to
view2 -> view1 -> view3

Such a task required changing multiple sections of the code, which was not ideal.

The solution

After spending a few hours trying to understand the navigation flow, the need to refactor became a necessity.

The first thing to do was to try to get rid of all the setVisibility() code repetitions, hence the method below.

Avoid code repetitions
fun navigate(from: View, to: View) {
    // outgoing view
    from.visibility = View.GONE

    // incoming view
    to.visibility = View.VISIBLE
Enter fullscreen mode Exit fullscreen mode

This was great, we could at least get rid of the repeated code by calling the navigate(...) method above whenever navigation was necessary. But there was still the problem of navigating backward.

Since we did not want to depend on the view that is currently visible to determine the previous view, we needed a way to keep track of all our view navigations.

Tracking navigation history

Let's create a data class that will hold the current view and the next.

We then employ the use of a Stack() to preserve the navigation history and update the previous navigate(from: View, to: View) method to make use of the ViewNavigation class.
(This process is similar to how a backStack is used for Fragments)

Now let's see how we make use of our newly created navigationStack to navigate forward and handle back presses.

Each time we call the navigateViews(...) method we create a ViewNavigation object and push it onto the stack.

Handling back navigations

When attempting to navigate backward, we pop the last item in the stack, as that item represents our most recent navigation.

As indicated in the comments, we invert the from and to params when creating a ViewNavigation object that will be used for navigating in the opposite/reverse direction.

The back() method returns true if back navigation was successful and false if there are no more views left in the stack. When that occurs, we hand over control back to the fragment/activity.


That's it! We're done. Now, whenever we need to display a particular view, all we need to do is call the CustomNavigator.navigateViews(from: View, to: View) method.

We no longer need to manually check which view is currently displayed before navigating.

And when new views are added, the navigation logic will not need to be updated!

Check out the full implementation here!

Top comments (0)