***ListView is outdated. Use this instead***
The Adapter acquires data from some source, stores the data in a ViewHolder (which holds a findViewById reference to the root View object for the item), caching the View objects represented in the layout. The number of ViewHolders created will equal the number of Views that can fit on the device screen plus a couple above and a couple below (approx) off-screen to make the recycling smooth. Therefore findViewById is only called when these Views are created and not for every item in the list of data.
A LinearLayoutManager is responsible for measuring and positioning item views within a RecyclerView into a linear list. This means that it can produce either a horizontal or vertical list depending on which parameter you pass in to the LinearLayoutManager constructor. It will take old Views which have scrolled off-screen and repopulate them with the new data from the ViewHolders.
This example demonstrates View recycling by showing a list (0-99) with the number on the LHS of the item View, and on the RHS the index of the ViewHolder.
In the case of Nexus 6 there are 11 (0-10) of these, along with a green-shaded background of varying saturation to visually enforce the concept.
Create an Adapter class.
// Avoid unnecessary garbage collection by using RecyclerView and ViewHolders.
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.NumberViewHolder> {
private static final String TAG = RecyclerAdapter.class.getSimpleName(); // Standard practice for logging
/*
* The number of ViewHolders that have been created. Typically, you can figure out how many
* there should be by determining how many list items fit on your screen at once and add 2 to 4
* to that number. That isn't the exact formula, but will give you an idea of how many
* ViewHolders have been created to display any given RecyclerView.
*
* Here's some ASCII art to hopefully help you understand:
*
* ViewHolders on screen:
*
* *-----------------------------*
* | ViewHolder index: 0 |
* *-----------------------------*
* | ViewHolder index: 1 |
* *-----------------------------*
* | ViewHolder index: 2 |
* *-----------------------------*
* | ViewHolder index: 3 |
* *-----------------------------*
* | ViewHolder index: 4 |
* *-----------------------------*
* | ViewHolder index: 5 |
* *-----------------------------*
* | ViewHolder index: 6 |
* *-----------------------------*
* | ViewHolder index: 7 |
* *-----------------------------*
*
* Extra ViewHolders (off screen)
*
* *-----------------------------*
* | ViewHolder index: 8 |
* *-----------------------------*
* | ViewHolder index: 9 |
* *-----------------------------*
* | ViewHolder index: 10|
* *-----------------------------*
* | ViewHolder index: 11|
* *-----------------------------*
*
* Total number of ViewHolders = 11
*/
private static int viewHolderCount;
private int mNumberItems;
/**
* Constructor for RecyclerAdapter that accepts a number of items to display
*
* @param numberOfItems Number of items to display in list
*/
public RecyclerAdapter(int numberOfItems) {
mNumberItems = numberOfItems;
viewHolderCount = 0;
}
/**
*
* This gets called when each new ViewHolder is created. This happens when the RecyclerView
* is laid out. Enough ViewHolders will be created to fill the screen and allow for scrolling.
*
* @param viewGroup The ViewGroup that these ViewHolders are contained within.
* @param viewType If your RecyclerView has more than one type of item (which ours doesn't) you
* can use this viewType integer to provide a different layout. See
* {@link android.support.v7.widget.RecyclerView.Adapter#getItemViewType(int)}
* for more details.
* @return A new NumberViewHolder that holds the View for each list item
*/
@Override
public NumberViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
int layoutIdForListItem = R.layout.number_list_item; // number_list_item = layout xml for each item View
Context context = viewGroup.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
boolean shouldAttachToParentImmediately = false;
View view = inflater.inflate(layoutIdForListItem, viewGroup, shouldAttachToParentImmediately);
NumberViewHolder viewHolder = new NumberViewHolder(view); // NumberViewHolder is a ViewHolder class we have defined below
viewHolder.viewHolderIndex.setText("ViewHolder index: " + viewHolderCount); // Prints out the index number of the ViewHolder that is currently being used
int backgroundColorForViewHolder = ColorUtils
.getViewHolderBackgroundColorFromInstance(context, viewHolderCount); // This function exists in a separate class
viewHolder.itemView.setBackgroundColor(backgroundColorForViewHolder); // Sets varying saturation of green as background for visual impact
viewHolderCount++;
Log.d(TAG, "onCreateViewHolder: number of ViewHolders created: "
+ viewHolderCount); // Prints number of ViewHolders to Log.d
return viewHolder;
}
/**
* OnBindViewHolder is called by the RecyclerView to display the data at the specified
* position. In this method, we update the contents of the ViewHolder to display the correct
* indices in the list for this particular position, using the "position" argument that is conveniently
* passed into us.
*
* @param holder The ViewHolder which should be updated to represent the contents of the
* item at the given position in the data set.
* @param position The position of the item within the adapter's data set.
*/
@Override
public void onBindViewHolder(NumberViewHolder holder, int position) {
Log.d(TAG, "#" + position); // Prints Viewholder index to Log.d as Views are recycled
holder.bind(position); // Binds the index number (0-99) to the ViewHolder (see bind function below)
}
/**
* This method simply returns the number of items to display. It is used behind the scenes
* to help layout our Views and for animations.
*
* @return The number of items available
*/
@Override
public int getItemCount() {
return mNumberItems;
}
/**
* Cache of the children views for a list item.
*/
class NumberViewHolder extends RecyclerView.ViewHolder {
// Will display the position in the list, ie 0 through getItemCount() - 1
TextView listItemNumberView;
// Will display which ViewHolder is displaying this data
TextView viewHolderIndex;
/**
* Constructor for our ViewHolder. Within this constructor, we get a reference to our
* TextViews.
* @param itemView The View that you inflated in
* {@link RecyclerAdapter#onCreateViewHolder(ViewGroup, int)}
*/
public NumberViewHolder(View itemView) {
super(itemView);
listItemNumberView = (TextView) itemView.findViewById(R.id.tv_item_number); // Reference to TextView on LHS of View
viewHolderIndex = (TextView) itemView.findViewById(R.id.tv_view_holder_instance); // Reference to TextView on RHS of View
}
/**
* A method we wrote for convenience. This method will take an integer as input and
* use that integer to display the appropriate text within a list item.
* @param listIndex Position of the item in the list
*/
void bind(int listIndex) {
listItemNumberView.setText(String.valueOf(listIndex)); // Sets the item number (0-99) to the TextView on LHS of item View
}
}
}
Add relevant code to Activity.
public class MainActivity extends AppCompatActivity {
private static final int NUM_LIST_ITEMS = 100; //Sets number of list items to 100
/*
* References to RecyclerView and Adapter
*/
private RecyclerAdapter mAdapter;
private RecyclerView mNumbersList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
* Using findViewById, we get a reference to our RecyclerView from xml. This allows us to
* do things like set the adapter of the RecyclerView and toggle the visibility.
*/
mNumbersList = (RecyclerView) findViewById(R.id.rv_numbers);
/*
* A LinearLayoutManager is responsible for measuring and positioning item views within a
* RecyclerView into a linear list. This means that it can produce either a horizontal or
* vertical list depending on which parameter you pass in to the LinearLayoutManager
* constructor. By default, if you don't specify an orientation, you get a vertical list.
* In our case, we want a vertical list, so we don't need to pass in an orientation flag to
* the LinearLayoutManager constructor.
*
* There are other LayoutManagers available to display your data in uniform grids,
* staggered grids, and more! See the developer documentation for more details.
*/
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mNumbersList.setLayoutManager(layoutManager);
/*
* Use this setting to improve performance if you know that changes in content do not
* change the child layout size in the RecyclerView
*/
mNumbersList.setHasFixedSize(true);
/*
* The RecyclerAdapter is responsible for displaying each item in the list.
*/
mAdapter = new RecyclerAdapter(NUM_LIST_ITEMS);
mNumbersList.setAdapter(mAdapter);
}
}
ud851-Exercises-student\Lesson03-Green-Recycler-View\T03.07-Exercise-RecyclerViewClickHandling