Cohort+Problem+4+-+RecyclerView+and+JSON+parsing

=What is the Recycler view?=

When you have large amounts of data e.g. a Twitter feed, you'd like to display it in a list of items. The ListView widget used to be the default choice for such tasks, but Google now offers a new option called a Recycler View.

Recycler View offers one feature not present in ListView. RecyclerView just loads enough data to display in the screen, and only when the view is scrolled up, will more data be loaded. This example will demonstrate this.

=Part 1 - Setting it up. Add the libraries and import the data.=

Modify the gradle script
We'll need the GSON library and the recycler view library, so add the following lines to the dependencies in the gradle script for your app.

code format="java" compile 'com.android.support:recyclerview-v7:21.0.+' compile 'com.google.code.gson:gson:2.8.1' code

Put the resources in the folders
I have prepared a JSON file and their associated images, which you can download here.

Here are the folders where the resources should go
 * res/drawable: the images
 * res/raw (create if it does not exist): the JSON file

Understanding the JSON file
Background information on the JSON file format can be found at the w3schools website.

The specific format that I have given you is a JSON array of objects. Study it and answer the following questions:
 * How many objects does the array have?
 * What attributes does each object have?

Set The Templates Up
You'll need two classes for this,
 * MainActivity.java (see part 3)
 * AnimeAdapter.java (see part 4)

You may want to get ready the code skeleton files ready.

=Part 2 - Overview of this app. UML Class Diagram=

Adapter Design Pattern
As you see in the documentation, you'll need an adapter to link your data to the Recycler View widget.

In the Adapter Design Pattern, an adapter class enables two classes with incompatible interfaces to talk to each other.

About Recycler View
The textbook has a chapter.

MainActivity.java
code format="java" public class MainActivity extends AppCompatActivity {

AnimeJsonData[] animeJsonData; private RecyclerView animeList; private AnimeAdapter mAnimeAdapter;

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

//TODO 3 - read the saved json file and parse it (see the TODOs below) parseJson;

//TODO 4.9 get a reference to the recycler view widget //TODO 4.10 create an instance of LinearLayoutManager and //         assign it to the recycler view object //TODO 4.11 create an instance of the Adapter and //         assign it to the recycler view object

}

//TODO 3.1 Create an inner class matching the keys of the JSON array public class AnimeJsonData{

String name; //complete the rest

}

private String readTxt(int resource){

InputStream inputStream = getResources.openRawResource(resource); //TODO 3.2 Complete readTxt to take in a resource ID of a file, //         read it and return it as a single string

}

void parseJson{

Gson gson = new Gson; //TODO 3.3 Invoke readTxt //TODO 3.4 parse the JSON file

} } code

AnimeAdapter.java
This will be given in Part 4.

UML Class Diagram
Part of the for our app is shown.

This shows the relationships between the classes that we are going to use.

Strategy
The strategy for this app is as follows.
 * Part 3 explains how to read data in JSON format and store it in an array.
 * Part 4 explains how to construct a RecyclerView and give it the data that you read in Part 3.
 * Part 5 explains how each item in the RecyclerView can have a callback to show more information.

=Part 3 - Storing and Reading Data. Parsing JSON using the GSON library=

This step explains how to read data stored in JSON format into an array. The JSON format is a commonly-used way for web servers to share data. Many web servers have API where search queries are returned in JSON format.

**Layout**
For the purposes of this part, create an activity with a simple layout e.g. one TextView widget. Use this widget to display whatever you wish.

**TO DO 3.1 - Inner Class Definition**
Create an inner class definition whose attributes have exactly the same format and spelling as each object in the JSON data. This is essential for the GSON library.

TO DO 3.2 - Read the JSON file and store it as a string
Next, you'll need to read the JSON file and store its entire contents as a single string by writing the readTxt method.

You could refer to Cohort Problem 6 for an example, or you could read this page and scroll down to "Converting the Input Stream to a String".

TO DO 3.3, 3.4 - Parse the JSON data
Invoke readTxt and you need to extract the information in the JSON file and store it in the **animeJsonData** instance variable. This has also been discussed in Cohort Problem 6.

Run the app
At this stage, you can run the app. You need to check whether the JSON file has ben read successfully. You can either display it in the log or in widgets on the Activity itself.

Once you are ready, set up the layout for Part 4 by including one recycler view widget in your activity.

=Part 4 - The recycler view adapter=

Adapter
The adapter class is the link between the RecyclerView widget and the data that it is supposed to contain.

It retrieves the data and places it in each item view.

It extends **RecyclerView.Adapter**, where VH is the ViewHolder class explained below.

ViewHolder
Within the Recycler View Adapter is an inner class that represents each item view in your Recycler View.

It extends **RecyclerView.ViewHolder**.

It is meant to contain data, execute any callbacks and provide data about each item view's position in the Recycler View.

Code Skeleton
code format="java" //TODO 4.1 (in a separate XML File) Design your list item layout

//TODO 4.2 go back to activity_main.xml and put in the recycler view widget

public class AnimeAdapter extends RecyclerView.Adapter {

private MainActivity.AnimeJsonData[] data; private static int viewHolderCount = 0; Context parentContext;

//TODO 4.4 - Constructor //TODO 4.5 - onCreateViewHolder //TODO 4.7 - onBindViewHolder //TODO 4.8 - getItemCount

class AnimeViewHolder extends RecyclerView.ViewHolder { TextView characterName; TextView itemNumber; ImageView picture;

AnimeViewHolder(View v){

//TODO 4.3 Invoke the superclass constructor // and get references to the various widgets in the List Item Layout }

//TODO 4.6 - write a bind method to attach content //           to the respective widgets

} } code

What you need to do to get a working app

 * Your **activity_main.xml** layout should contain a recycler view widget. I recommend that it is the only widget on this layout. Do this before carrying on.
 * Within the Recycler View widget, it contains a list of items, each item will have their own layout (explained below).
 * You then need to write the recycler view adapter class, which I'll call **AnimeAdapter.java** (explained below).
 * Finally, your **MainActivity.java** needs to bind the RecyclerView widget with its adapter.

TO DO 4.1 - List Item Layout
The recycler view widget displays a list of items. Each item has the same custom layout and it can comprise of more than one widget.

Hence, you need to design the layout of each individual list item.

Add a **recycler_item.xml** layout to your res/layout folder and design it in the way you wish. Or you can use mine:

code format="xml"  







 code

TO DO 4.2 - Replace all widgets in Activity_main.xml with the following
It will have only one recycler view widget.

code format="xml" 

code

Inner Class For Each ListItem
We'll need to create an inner class to help us manage actions on each list item layout.

The structure has already been done for you in the code fragment above.

The instance variables reflect the layout of your list item.

We'll first write the constructor, then come back to complete the bind method.

TO DO 4.3 - Constructor
An inflated layout in the form of a View object will be passed to the constructor.

Invoke the superclass constructor.

The constructor then gets references to the individual widgets in this layout.

Introduction
You have now created your individual list item layout. The basic requirements of the adapter class are
 * get the JSON data that was parsed (TODO 4.3)
 * inflate each list item layout (TODO 4.4)
 * store references of to the list item widgets (TODO 4.5)
 * bind the data to each list item (TODO 4.6 and 4.7)
 * return the number of list items (TODO 4.8)

**TO DO 4.4 - Constructor**
Your constructor just needs to receive the two things:
 * reference to the array that stores the JSON data and store it in the relevant attribute
 * a Context object

According to the android documentation, we have to get an instance of the LayoutInflater object. Hence, the context object is needed to get the LayoutInflater later in TO DO 4.5. You also need it to get references to your resources.


 * TO DO 4.5 - onCreateViewHolder**

This method is called by RecyclerView when it needs a new ViewHolder (i.e. a new item in the list). This overrides the method of the same name in the abstract class.

In this method, we
 * inflate the layout for each item, which returns then View object
 * pass this view object to the AnimeViewHolder class to create a new instance

The inflate method is described in the documentation.

You may also wish to add a code to print to the Logcat everytime this method is invoked.

code format="java" @Override public AnimeAdapter.AnimeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

int layoutIDForListItem = R.layout.recycler_item; LayoutInflater inflater = LayoutInflater.from(parentContext); boolean shouldAttachToParentImmediately = false;

View view = inflater.inflate(layoutIDForListItem,parent,shouldAttachToParentImmediately);

AnimeViewHolder animeViewHolder = new AnimeViewHolder(view);

return animeViewHolder; }

code

TO DO 4.6 - Bind method within the inner class
This method is called whenever data needs to be bound to the list ViewHolder. Each view holder contains an image, so we will access the image from the resources and bind it to that widget.

A) We first have to get the resource ID of the image file stored in the drawables folder. B) Next, with this ID, we can then pass it to the imageView widget; C) Assign the character name to the characterName widget D) For counting purposes, we also display the position number.

code format="java" public void bind(int position ){

//TODO 4.6A - get the filename of the image String filename = ;

String packageName = parentContext.getPackageName; String typeOfResource = "drawable";

int resID = parentContext.getResources.getIdentifier(filename, typeOfResource, packageName); //TODO 4.6 B pass the resource ID to the image widget picture.setImageResource;

//TODO 4.6 C pass the character name to the characterName widget characterName  ;

//TODO 4.6 D display the position number itemNumber ;

} code

**TO DO 4.7 Invoke the bind method in onBindViewHolder.**
code format="java" @Override public void onBindViewHolder(AnimeAdapter.AnimeViewHolder holder, int position) {

//TODO 4.7

}

code

**TO DO 4.8 - override the getItemCount method**
Return the number of items available for display. The recyclerView widget needs this method for the correct number of views to display.

Try and see what would happen if you returned 0 or another figure.

TO DO 4.9 - 4.11 - In the onCreate method of MainActivity.java,
You should now
 * get a reference to the RecyclerView widget
 * assign it a LayoutManager. As we intend our list items to be in rows on top of each other, a LinearLayoutManager is necessary.
 * assign it the adapter

For the necessary code, refer to the textbook for an example.

Run the app!

code format="java" @Override protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); parseJson;

//TODO 4.9 - get a reference to the recycler view widget animeList = ;

//TODO 4.10 & 4.11 refer to textbook

} code

Run the app
You should be able to run the app, and scroll up and down. As you scroll up and down, observe the log. You should see that not all the data are bound to the list. As you scroll up, you will see that the binding happens. This is the feature of recycler view that helps to conserve memory.

=Part 5 - Getting each list item to respond to clicks=

With AnimeViewHolder, implement View.OnClickListener and override onClick
Getting each list item to respond to a click requires that the entire list item has an onClickListener.

Hence, we have to modify the AnimeViewHolder class to have one.

I make AnimeViewHolder implement View.OnClickListener.

As an exercise, explain why, in the constructor, we call the setOnClickListener method of v and pass this as an argument.

code format="java" class AnimeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { TextView characterName; TextView itemNumber; ImageView picture;

AnimeViewHolder(View v){ //other code not shown

//First, we have to let each list item listen for clicks. Recall that there are several ways to do this. //I choose to have this class implement View.OnClickListener interface. v.setOnClickListener(this); }

//Secondly, because we override View.OnClickListener, we implement this method.

@Override public void onClick(View view) {

int clickedPosition = getAdapterPosition; //TODO 5.1 - Implement a simple alert dialog showing the name of the anime when the list item is clicked. I leave this to you as an exercise.

}

code

=Going Further - make onClick display a larger image and more information in the alert dialog=

If you'd like to do more with the Alert Dialog, try making the Alert Dialog show the image and provide an information.

This page in StackOverflow explains your options, and you should explore what happens with the XML file as well as without.

Lastly, you should try to control the size of your image.