To create complex lists and cards with material design styles in your apps, you can use the RecyclerView and CardView widgets. In this post, I provide solution to combined both of them. Futher, I also include a FloatingActionButton (a Design Support Libary widget, too) and adding data to RecyclerView by it action!
Please watch project output by this demo video:
Starting project
compile 'com.android.support:recyclerview-v7:22.2.1'
compile 'com.android.support:cardview-v7:22.2.1'
compile 'com.android.support:design:22.2.1'
In order to combine FloatingActionButton with RecyclerView, we use FrameLayout as root. Main activity layout simple like this:Okey, in programmatically code, we need "config" RecyclerView features. If you know that changes in content do not change the layout size of the RecyclerView, please use this setting to improve performance:
recyclerView.setHasFixedSize(true);We also set LayoutManager (I use LinearLayout) for RecyclerView and set adapter like ListView:
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
setRecyclerViewData(); // call adding data to array list method
adapter = new RecyclerAdapter(this, friendArrayList);
recyclerView.setAdapter(adapter);
Cutomizing a RecyclerView Adapter
/**
* View holder to display each RecylerView item
*/
protected class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView name;
private TextView job;
private View container;
public ViewHolder(View view) {
super(view);
imageView = (ImageView) view.findViewById(R.id.image);
name = (TextView) view.findViewById(R.id.name);
job = (TextView) view.findViewById(R.id.job);
container = view.findViewById(R.id.card_view);
}
}
Note: this inner class must in public or protected type to use!Overriding onCreateViewHolder() and onBindViewHolder() to organize the code, through these methods, data views and size were created:
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
//inflate your layout and pass it to view holder
LayoutInflater inflater = activity.getLayoutInflater();
View view = inflater.inflate(R.layout.item_recycler, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(RecyclerAdapter.ViewHolder viewHolder, int position) {
//setting data to view holder elements
viewHolder.name.setText(friends.get(position).getName());
viewHolder.job.setText(friends.get(position).getJob());
if (friends.get(position).isGender()) {
viewHolder.imageView.setImageResource(R.mipmap.male);
} else {
viewHolder.imageView.setImageResource(R.mipmap.female);
}
//set on click listener for each element
viewHolder.container.setOnClickListener(onClickListener(position));
}
As note above, each RecyclerView item is a CardView, this was declared in xml layout:When app running, we'll have this output screen:
Handle RecyclerView Item Click Event
private View.OnClickListener onClickListener(final int position) {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
final Dialog dialog = new Dialog(activity);
dialog.setContentView(R.layout.item_recycler);
dialog.setTitle("Position " + position);
dialog.setCancelable(true); // dismiss when touching outside Dialog
// set the custom dialog components - texts and image
TextView name = (TextView) dialog.findViewById(R.id.name);
TextView job = (TextView) dialog.findViewById(R.id.job);
ImageView icon = (ImageView) dialog.findViewById(R.id.image);
setDataToView(name, job, icon, position);
dialog.show();
}
};
}
Ofcourse, from now, when each item clicked, a Dialog appeared and show a single CardView with it's information:Floating Action Button implements
private View.OnClickListener onAddingListener() {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
final Dialog dialog = new Dialog(MainActivity.this);
dialog.setContentView(R.layout.dialog_add); //layout for dialog
dialog.setTitle("Add a new friend");
dialog.setCancelable(false); //none-dismiss when touching outside Dialog
// set the custom dialog components - texts and image
EditText name = (EditText) dialog.findViewById(R.id.name);
EditText job = (EditText) dialog.findViewById(R.id.job);
Spinner spnGender = (Spinner) dialog.findViewById(R.id.gender);
View btnAdd = dialog.findViewById(R.id.btn_ok);
View btnCancel = dialog.findViewById(R.id.btn_cancel);
//set spinner adapter
ArrayList<String> gendersList = new ArrayList<>();
gendersList.add("Male");
gendersList.add("Female");
ArrayAdapter<String> spnAdapter = new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_dropdown_item_1line, gendersList);
spnGender.setAdapter(spnAdapter);
//set handling event for 2 buttons and spinner
spnGender.setOnItemSelectedListener(onItemSelectedListener());
btnAdd.setOnClickListener(onConfirmListener(name, job, dialog));
btnCancel.setOnClickListener(onCancelListener(dialog));
dialog.show();
}
};
}
private AdapterView.OnItemSelectedListener onItemSelectedListener() {
return new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView parent, View view, int position, long id) {
if (position == 0) {
gender = true;
} else {
gender = false;
}
}
@Override
public void onNothingSelected(AdapterView parent) {
}
};
}
private View.OnClickListener onConfirmListener(final EditText name, final EditText job, final Dialog dialog) {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
Friend friend = new Friend(name.getText().toString().trim(), gender, job.getText().toString().trim());
//adding new object to arraylist
friendArrayList.add(friend);
//notify data set changed in RecyclerView adapter
adapter.notifyDataSetChanged();
//close dialog after all
dialog.dismiss();
}
};
}
private View.OnClickListener onCancelListener(final Dialog dialog) {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
};
}
Customized layout for this adding dialog:Output running screen:
Final Code
POJO of project, data object:
package devexchanges.info.recycleviewandcardview;
public class Friend {
private String name;
private String job;
private boolean gender;
public Friend(String name, boolean gender, String job) {
this.name = name;
this.gender = gender;
this.job = job;
}
public String getName() {
return name;
}
public String getJob() {
return job;
}
public boolean isGender() {
return gender;
}
}
Running (main) activity full code:package devexchanges.info.recycleviewandcardview;
import android.app.Dialog;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private RecyclerAdapter adapter;
private ArrayList<Friend> friendArrayList;
private FloatingActionButton fab;
private boolean gender;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
friendArrayList = new ArrayList<>();
recyclerView = (RecyclerView) findViewById(R.id.recyle_view);
fab = (FloatingActionButton) findViewById(R.id.fab);
recyclerView.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
setRecyclerViewData(); //adding data to array list
adapter = new RecyclerAdapter(this, friendArrayList);
recyclerView.setAdapter(adapter);
fab.setOnClickListener(onAddingListener());
}
private void setRecyclerViewData() {
friendArrayList.add(new Friend("Phan Thanh", false, "Cashier"));
friendArrayList.add(new Friend("Nguyen Tuan", true, "Developer"));
friendArrayList.add(new Friend("Tran Van Minh", true, "Designer"));
friendArrayList.add(new Friend("Pham Mai Anh", true, "architect"));
friendArrayList.add(new Friend("Nguyen Quynh Trang", false, "Doctor"));
friendArrayList.add(new Friend("Hoang Dinh Cuong", false, "artist"));
friendArrayList.add(new Friend("Tran Cong Bach", true, "Student"));
friendArrayList.add(new Friend("Vu Van Duong", false, "Teacher"));
}
private View.OnClickListener onAddingListener() {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
final Dialog dialog = new Dialog(MainActivity.this);
dialog.setContentView(R.layout.dialog_add); //layout for dialog
dialog.setTitle("Add a new friend");
dialog.setCancelable(false); //none-dismiss when touching outside Dialog
// set the custom dialog components - texts and image
EditText name = (EditText) dialog.findViewById(R.id.name);
EditText job = (EditText) dialog.findViewById(R.id.job);
Spinner spnGender = (Spinner) dialog.findViewById(R.id.gender);
View btnAdd = dialog.findViewById(R.id.btn_ok);
View btnCancel = dialog.findViewById(R.id.btn_cancel);
//set spinner adapter
ArrayList<String> gendersList = new ArrayList<>();
gendersList.add("Male");
gendersList.add("Female");
ArrayAdapter<String> spnAdapter = new ArrayAdapter<String>(MainActivity.this,
android.R.layout.simple_dropdown_item_1line, gendersList);
spnGender.setAdapter(spnAdapter);
//set handling event for 2 buttons and spinner
spnGender.setOnItemSelectedListener(onItemSelectedListener());
btnAdd.setOnClickListener(onConfirmListener(name, job, dialog));
btnCancel.setOnClickListener(onCancelListener(dialog));
dialog.show();
}
};
}
private AdapterView.OnItemSelectedListener onItemSelectedListener() {
return new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView parent, View view, int position, long id) {
if (position == 0) {
gender = true;
} else {
gender = false;
}
}
@Override
public void onNothingSelected(AdapterView parent) {
}
};
}
private View.OnClickListener onConfirmListener(final EditText name, final EditText job, final Dialog dialog) {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
Friend friend = new Friend(name.getText().toString().trim(), gender, job.getText().toString().trim());
//adding new object to arraylist
friendArrayList.add(friend);
//notify data set changed in RecyclerView adapter
adapter.notifyDataSetChanged();
//close dialog after all
dialog.dismiss();
}
};
}
private View.OnClickListener onCancelListener(final Dialog dialog) {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
};
}
}
Recycler adapter class, most important file:package devexchanges.info.recycleviewandcardview;
import android.app.Activity;
import android.app.Dialog;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
private List<Friend> friends;
private Activity activity;
public RecyclerAdapter(Activity activity, List<Friend> friends) {
this.friends = friends;
this.activity = activity;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
//inflate your layout and pass it to view holder
LayoutInflater inflater = activity.getLayoutInflater();
View view = inflater.inflate(R.layout.item_recycler, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(RecyclerAdapter.ViewHolder viewHolder, int position) {
//setting data to view holder elements
viewHolder.name.setText(friends.get(position).getName());
viewHolder.job.setText(friends.get(position).getJob());
if (friends.get(position).isGender()) {
viewHolder.imageView.setImageResource(R.mipmap.male);
} else {
viewHolder.imageView.setImageResource(R.mipmap.female);
}
//set on click listener for each element
viewHolder.container.setOnClickListener(onClickListener(position));
}
private void setDataToView(TextView name, TextView job, ImageView genderIcon, int position) {
name.setText(friends.get(position).getName());
job.setText(friends.get(position).getJob());
if (friends.get(position).isGender()) {
genderIcon.setImageResource(R.mipmap.male);
} else {
genderIcon.setImageResource(R.mipmap.female);
}
}
@Override
public int getItemCount() {
return (null != friends ? friends.size() : 0);
}
private View.OnClickListener onClickListener(final int position) {
return new View.OnClickListener() {
@Override
public void onClick(View v) {
final Dialog dialog = new Dialog(activity);
dialog.setContentView(R.layout.item_recycler);
dialog.setTitle("Position " + position);
dialog.setCancelable(true); // dismiss when touching outside Dialog
// set the custom dialog components - texts and image
TextView name = (TextView) dialog.findViewById(R.id.name);
TextView job = (TextView) dialog.findViewById(R.id.job);
ImageView icon = (ImageView) dialog.findViewById(R.id.image);
setDataToView(name, job, icon, position);
dialog.show();
}
};
}
/**
* View holder to display each RecylerView item
*/
protected class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView name;
private TextView job;
private View container;
public ViewHolder(View view) {
super(view);
imageView = (ImageView) view.findViewById(R.id.image);
name = (TextView) view.findViewById(R.id.name);
job = (TextView) view.findViewById(R.id.job);
container = view.findViewById(R.id.card_view);
}
}
}



