Skip to content

Instantly share code, notes, and snippets.

@DavidEdwards
Created September 28, 2016 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DavidEdwards/b63388a7e138a56f9f6b46131ec97a27 to your computer and use it in GitHub Desktop.
Save DavidEdwards/b63388a7e138a56f9f6b46131ec97a27 to your computer and use it in GitHub Desktop.
RecyclerView Grid Example
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".ExampleOneActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/list_person"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
/**
* This example contains 3 main parts.
*
* The main java code in ExampleTwoActivity. This contains a dataset, a ViewHolder and an Adapter.
*
* The two XML snippets refer to the Activity layout XML and the ViewHolder (PersonHolder) layout XML.
*
* This example supports a GridLayoutManager, an OnClickListener and a dynamically updating dataset. Clicking on a cell will delete the cell from the dataset. Clicking on Add Person will add a Person to the dataset. Followed by immediate updates to the PersonAdapter.
*
* The code for the Activity:
*/
public class ExampleTwoActivity extends AppCompatActivity {
private PersonAdapter mAdapter = null;
/**
* Our Data that is given to the RecyclerView
*/
private List<Person> mDataset = null;
private Random mRandom = new Random();
private String[] mGivenNames = new String[] {
"David", "Alice", "Paul", "Anna", "Andrew", "Patricia", "John", "Jane", "Dorothy", "Michael", "Zoe"
};
private String[] mFamilyNames = new String[] {
"Smith", "Brown", "Johnson", "Jones", "Williams", "Davis", "Miller", "Wilson", "Taylor", "Clark"
};
/**
* Helper function to generate a new Person from a randomly selected given name and
* family name.
*/
private Person generatePerson() {
String givenName = mGivenNames[mRandom.nextInt(mGivenNames.length - 1)];
String familyName = mFamilyNames[mRandom.nextInt(mFamilyNames.length - 1)];
Log.v("DAE", String.format("Generating: %s %s", givenName, familyName));
return new Person(givenName, familyName);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_example_two);
// Initialize your dataset.
mDataset = new ArrayList<Person>();
// Give the dataset some objects to display.
// You could link this to a SQLiteDatabase for example.
for(int i = 0; i < 10; i++) {
mDataset.add(generatePerson());
}
// Find our RecyclerView from our XML layout.
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list_person);
Button addButton = (Button) findViewById(R.id.person_add);
// The views should not be null, given that we have the correct layout and ID set.
if(recyclerView == null || addButton == null) {
throw new IllegalStateException("recyclerView and addButton should never be null at this point.");
}
// This function when set to true will allow for some performance enhancements
// as long as the size of its children do not change.
recyclerView.setHasFixedSize(true);
// Set the LayoutManager for the RecyclerView. This is very powerful. You can create
// a Linear (vertical or horizontal) layout or a Grid layout by default. This class
// can be extended to provide custom functionality.
recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
// Set our RecyclerViews Adapter. See the example PersonAdapter below.
// It is not default functionality to allow a RecyclerView holder to respond to
// a click. We must provide that functionality. The easiest way is to provide
// an OnClickListener directly to the Adapter by its constructor.
mAdapter = new PersonAdapter(mDataset, new View.OnClickListener() {
@Override
public void onClick(View v) {
// Take the clicked Person from the tag of the RecyclerView holder.
Person person = (Person) v.getTag();
int index = mDataset.indexOf(person);
// If the index exists between the bounds of our dataset.
// Sometimes, if you tap fast, you can click on items in the process of being removed.
if(index >= 0 && index < mDataset.size()) {
mDataset.remove(index);
// IMPORTANT. When you change the dataset, you must always
// call the specific command for notifying the adapter.
// In this case, we are removing one specific item from the dataset, so we
// must tell the Adapter which index it was that we removed.
// DO NOT use notifyDataSetChanged(); - This will be much less efficient.
mAdapter.notifyItemRemoved(index);
Toast.makeText(ExampleTwoActivity.this, String.format("Removing: %s %s", person.getGivenName(), person.getFamilyName()), Toast.LENGTH_SHORT).show();
}
}
});
recyclerView.setAdapter(mAdapter);
// When we click the Add Button, we want to add a new item to the dataset
addButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Add the new item to the list at a random point (to show off animations)
// We check whether the dataset size is greater than 1 in order to be safe for
// our random integer function (with a size of 0, you will get an index of -1)
int randomIndex = mDataset.size() > 1 ? mRandom.nextInt(mDataset.size()-1) : 0;
// Generate a new Person
Person person = generatePerson();
// Add the new Person to our dataset
mDataset.add(randomIndex, person);
// IMPORTANT. When you change the dataset, you must always
// call the specific command for notifying the adapter.
// In this case, we are adding a new item to the dataset, so we
// must tell the Adapter which index it was that we added it to.
// If you do not used the index in mDataset.add, you should simply use
// mDataset.size() - 1 instead. As the item is added to the end of the
// dataset.
// DO NOT use notifyDataSetChanged(); - This will be much less efficient.
mAdapter.notifyItemInserted(randomIndex);
Toast.makeText(ExampleTwoActivity.this, String.format("Adding: %s %s", person.getGivenName(), person.getFamilyName()), Toast.LENGTH_SHORT).show();
}
});
}
/**
* An extremely basic POJO that provides a given and family name for this example
*/
public class Person {
private String mGivenName;
private String mFamilyName;
public Person(String givenName, String familyName) {
this.mGivenName = givenName;
this.mFamilyName = familyName;
}
public String getGivenName() {
return mGivenName;
}
public String getFamilyName() {
return mFamilyName;
}
}
/**
* This extends ViewHolder, giving it the properties of the typical ViewHolder pattern.
* This class will be reused by the RecyclerView to be optimally memory efficient.
* Once an instance of this class disappears out of the RecyclerView it will be used again
* to display the data of a different dataset entry.
*/
public class PersonHolder extends RecyclerView.ViewHolder {
// Declare the Views that this ViewHolder represents that hold data.
private TextView givenName;
private TextView familyName;
public PersonHolder(View itemView) {
super(itemView);
// Initialize our Views using our inflated holder XML from onCreateViewHolder in PersonAdapter.
givenName = (TextView) itemView.findViewById(R.id.given_name);
familyName = (TextView) itemView.findViewById(R.id.family_name);
}
public TextView getGivenName() {
return givenName;
}
public TextView getFamilyName() {
return familyName;
}
}
/**
* This extends RecyclerView.Adapter&lt;&gt; and it represents a binding between the dataset
* provided to it and the underlying layout structure of your ViewHolder (PersonHolder)
*/
public class PersonAdapter extends RecyclerView.Adapter<PersonHolder> {
/**
* Our dataset provided to this Adapter through the constructor
*/
private List<Person> mDataset;
/**
* An OnClickListener that will respond to clicks delivered to our ViewHolder (PersonHolder)
*/
private View.OnClickListener mClickListener;
/**
* We need to set our local dataset and OnClickListener callback in the constructor
* @param dataSet The dataset that this Adapter will work with
* @param clickListener The callback that we will attach to our ViewHolder (PersonHolder)
*/
public PersonAdapter(List<Person> dataSet, View.OnClickListener clickListener) {
this.mDataset = dataSet;
this.mClickListener = clickListener;
}
/**
* Use LayoutInflater to create a layout from our XML. This will be used to create a
* new ViewHolder (PersonHolder). This function is NOT responsible for filling the
* ViewHolder with data. That is done through onBindViewHolder.
*/
@Override
public PersonHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.holder_person, parent, false);
return new PersonHolder(view);
}
/**
* Use a pre-created layout in the form of a ViewHolder (PersonHolder). Fill it with
* data from your dataset.
*/
@Override
public void onBindViewHolder(final PersonHolder holder, final int position) {
// Get our Person from the dataset using the index of position.
final Person person = mDataset.get(position);
// Set the "tag" of our ViewHolder to allow us to determine which Person
// has been clicked outside of this Adapter.
holder.itemView.setTag(person);
// If we passed a valid OnClickListener (not null) then we will attach that
// callback to the ViewHolder.
if(mClickListener != null) {
holder.itemView.setOnClickListener(mClickListener);
}
// Set the ViewHolder (PersonHolder) views with information provided from
// our dataset.
holder.getGivenName().setText(person.getGivenName());
holder.getFamilyName().setText(person.getFamilyName());
}
/**
* Return the total items in your dataset
*/
@Override
public int getItemCount() {
return mDataset.size();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:clickable="true"
android:focusable="true"
android:padding="4dp"
android:layout_margin="4dp"
android:background="#55aaaaaa"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/given_name"
android:layout_width="wrap_content"
android:text="Given"
android:padding="8dp"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/family_name"
android:text="Family"
android:padding="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment