Created
September 28, 2016 14:17
-
-
Save DavidEdwards/b63388a7e138a56f9f6b46131ec97a27 to your computer and use it in GitHub Desktop.
RecyclerView Grid Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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<> 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(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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