RecyclerELE – Handle those edgy RecyclerView situations with ease!

So, As a an Android Developer, with the new Material Design, I tend to often run into the RecyclerView. It’s awesome! We can use any kind of views inside it, and it works beautifully! Those CardViews are such great eye-candy, and the RecyclerView make them fun and flexible to use. But, coming from using the standard ListView,¬†I saw a problem. There’s no inbuilt way to just set the EmptyView in RecyclerView in case the list is empty. Every time I created a RecyclerView, no matter how small the application, The activity code would contain a huge amount of lines altering (read, changing visibility of) the different views according to the situation (Empty List, Error while making network call, Loading from the network etc.). The XML was not much different either. Too much code, too little accomplished. I always wanted a fix for it. So I created one.

Github/the-cybersapien/RecyclerELE


Recycler-ELE for recyclerView

The Problem with RecyclerView

Let’s first see, what exactly am I talking about. This is the code from one of my first projects as an Android Developer. The Application fetches data from The Guardian’s News API and displays them in a nice list. My Rubric mentioned that we needed a list of news, and an empty view in case there is some error, or while loading. I was new to Android, just exploring new technologies, so I decided to use the RecyclerView. After a writing the complete code, the Activity contained all of this:

public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks < List < GuardianStory >> {
/*Adapter for the stories*/
private GuardianStoryAdapter guardianStoryAdapter;
/*Create the Recycler View*/
private RecyclerView storyListView;
/*Reference to the progress Bar*/
private ProgressBar progressBar;
/*Get a reference to the LoaderManager to intereact with the Loaders*/
private LoaderManager loaderManager;
/*Reference to the error(hint) TextView*/
private TextView hintView;
/*Reference the stories.*/
List < GuardianStory > stories;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loaderManager = getLoaderManager();
//Initialize Progress bar
progressBar = (ProgressBar) findViewById(R.id.progressBar);
//Initialize TextView
hintView = (TextView) findViewById(R.id.errorHint);
//Create RecyclerView
storyListView = (RecyclerView) findViewById(R.id.stories_list);
storyListView.setVisibility(View.GONE);
//Create GuardianStory list;
stories = new ArrayList < > ();
//get GuardianStoryAdapter
guardianStoryAdapter = new GuardianStoryAdapter(this, stories);
storyListView.setAdapter(guardianStoryAdapter);
storyListView.setLayoutManager(new LinearLayoutManager(this));
startLoad();
}
/**
* This method starts the loader to get news Stories,
* if there is no connection, it shows the hintView stating the error
*/
private void startLoad() {
//Create the connectivity Manager and NetworkInfo objects to check for Network connectivity
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
//Initialize the loader. Pass the itn ID constant defined above and pass in null
loaderManager.initLoader(STORY_LOADER_ID, null, this);
} else {
hintView.setText(R.string.no_internet);
progressBar.setVisibility(View.GONE);
hintView.setVisibility(View.VISIBLE);
storyListView.setVisibility(View.GONE);
}
}
/**
* On Create Loader handles the setting of the View to loading and then goes on to initiate the network call i the background.
*/
@Override
public Loader < List < GuardianStory >> onCreateLoader(int id, Bundle args) {
progressBar.setVisibility(View.VISIBLE);
hintView.setVisibility(View.GONE);
.
.
.
return new StoryLoader(this, URI());
}
/**
* OnLoadFinish Method handles the loading of stories in the list,
* If the list is empty, it shows the error view
*/
@Override
public void onLoadFinished(Loader < List < GuardianStory >> loader, List < GuardianStory > guardianStoryList) {
guardianStoryAdapter.clearData();
if (guardianStoryList != null && !guardianStoryList.isEmpty()) {
stories.addAll(guardianStoryList);
guardianStoryAdapter.notifyDataSetChanged();
storyListView.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
hintView.setVisibility(View.GONE);
} else {
storyListView.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
hintView.setVisibility(View.VISIBLE);
hintView.setText(R.string.error_getting_data);
}
}

<!--?xml version="1.0" encoding="utf-8"?-->

 

 

 

 

Here’s the thing, this is a small activity, there are only a few lines of code, no animations etc. and yet, each action takes so many visibility actions.

Enter RecyclerELE

RecyclerELE is a wrapper around the normal RecyclerView adapter. The RecyclerELEAdapter class takes a simple RecyclerView.Adapter, an emptyView, a loadingView and an errorView (All three Views can be inflated from their respective XML layout files, or created dynamically, as per you needs).

Here’s the same code as above, now using the RecyclerELE Adapter.

public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks < List < GuardianStory >> {

/*Adapter for the stories*/
private RecyclerELEAdapter RAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

loaderManager = getLoaderManager();

//Create RecyclerView
RecyclerView storyListView = (RecyclerView) findViewById(R.id.stories_list);
storyListView.setLayoutManager(new LinearLayoutManager(this));

// Initialize the loading View
View loadingView = getLayoutInflater().inflate(R.layout.loading_view, storyListView, false);

// Initialize the error View
View errorView = getLayoutInflater().inflate(R.layout.error_view, storyListView, false);

// Initialize the empty View
// In this case, we're going to just modify the text in the error_view.xml
View emptyView = getLayoutInflater().inflate(R.layout.error_view, storyListView, false);
((TextView) emptyView.findViewById(R.id.errorHint)).setText("No Stories available!");

stories = new ArrayList < > ();

// Initialize the guardianStoryAdapter
GuardianStoryAdapter guardianStoryAdapter = new GuardianStoryAdapter(this, stories);

// Initialize the RecyclerELE
RAdapter = new RecyclerELEAdapter(guardianStoryAdapter, emptyView, loadingView, errorView);

storyListView.setAdapter(RAdapter);
startLoad();
}

private void startLoad() {
//Create the connectivity Manager and NetworkInfo objects to check for Network connectivity
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

if (networkInfo != null && networkInfo.isConnected()) {
//Initialize the loader. Pass the itn ID constant defined above and pass in null
loaderManager.initLoader(STORY_LOADER_ID, null, this);
} else {
RAdapter.setCurrentView(RecyclerELEAdapter.VIEW_ERROR);
}
}

@Override
public Loader < List < GuardianStory >> onCreateLoader(int id, Bundle args) {

RAdapter.setCurrentView(RecyclerELEAdapter.VIEW_LOADING);
.....
}

@Override
public void onLoadFinished(Loader < List < GuardianStory >> loader, List < GuardianStory > guardianStoryList) {
if (guardianStoryList != null && !guardianStoryList.isEmpty()) {
stories.addAll(guardianStoryList);
RAdapter.setCurrentView(RecyclerELEAdapter.VIEW_NORMAL);
RAdapter.notifyDataSetChanged();
} else {
RAdapter.notifyDataSetChanged();
RAdapter.setCurrentView(RecyclerELEAdapter.VIEW_ERROR);
}
}


<!--?xml version="1.0" encoding="utf-8"?-->

 

<!--?xml version="1.0" encoding="utf-8"?-->

 

<!--?xml version="1.0" encoding="utf-8"?-->

 

 

As you see here, the amount of work for the developer decreases and he can focus on the more important parts of the job, rather than hiding/showing elements at each edge.That my friends is the reason to use the RecyclerELE. It helps us make our code cleaner.
Not just that, there is a second benefit. Since it takes advantage of memory management by the recyclerView Adapter, it has a much smaller memory footprint, not to mention, it is faster too.

Where to find the recyclerELE?

The library is available on jCenter as well as jitpack.

For Gradle

Simply add the following dependency to your project’s build.gradle:

compile 'com.github.the-cybersapien:recyclerELE:(latest release)'

For maven

Add the following snippet:

com.github.the-cybersapien
recyclerELE
v1.0
pom

Usage

In your Activity, create a regular RecyclerView.Adapter for your List Item

RecyclerAdapter listAdapter = new RecyclerAdapter(Object... args);

Get the reference to your RecyclerView

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

Inflate your empty view, loading view and error view with the recyclerView as their parent programmatically.

View loadingView = getLayoutInflator().inflate(R.layout.view_loading, recyclerView, false);
View emptyView = getLayoutInflator().inflate(R.layout.view_empty, recyclerView, false);
View errorView = getLayoutInflator().inflate(R.layout.view_error, recyclerView, false);

Initialize the RecyclerELEAdapter with the RecyclerAdapter and the different Views

RecyclerELEAdapter recyclerELEAdapter = new RecyclerELE(listAdapter, emptyView, loadingView, errorView);

Set the RecyclerELEAdapter to your recyclerView

recyclerView.setAdapter(recyclerELEAdapter);

Change the current View type of the adapter with setCurrentView() function

// Normal List View
recyclerELEAdapter.setCurrentView(RecyclerELEAdapter.VIEW_NORMAL);

// Empty View
recyclerELEAdapter.setCurrentView(RecyclerELEAdapter.VIEW_EMPTY);

// Loading View
recyclerELEAdapter.setCurrentView(RecyclerELEAdapter.VIEW_LOADING);

// Error View
recyclerELEAdapter.setCurrentView(RecyclerELEAdapter.VIEW_ERROR);

You can see the library source code as well as a sample application here:

Github/the-cybersapien/RecyclerELE

About Author:

Leave a Comment

Your email address will not be published. Required fields are marked *