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); } } }