I am developing an application using the RecyclerView & CardView widgets to display the contacts added by the user.
The application allows the user to add a new card, set the appropriate information (name, phone number & image) to be displayed within the card while the card is being added to the list
The number set by the user can be dialed to initiate a phone
call when the card is clicked
I have created the UI, but I don't know how to go about the aforementioned operations using RecyclerView. How to solve this?
In order to bind the contacts to the CardView, you need to create an ArrayList<> to store the contacts in order to bind them. First, create a new class called Contact.java and add the following code:
public class Contact {
public String name;
public int number;
public Contact(String name, int number) {
this.name = name;
this.number = number;
}
}
This class will make sure that each instance within our ArrayList<> has the required values for the name of the contact and phone number. Now, we need to create an adapter class to manage the click events within our CardView and to bind the values from our ArrayList<> to the Views within the CardView. Create a new class called ContactsAdapter.java and add the following code:
public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.ContactsViewHolder> {
public List<Contact> list;
public ContactsAdapter(List<ContactsAdapter> list) {
this.list = list;
}
public static class ContactsViewHolder extends RecyclerView.ViewHolder {
// Update:
RelativeLayout item;
ImageView photo;
TextView name;
TextView number;
public ContactsViewHolder(View view) {
super(view);
// Update:
item = (RelativeLayout) view.findViewById(R.id.id_to_rel_layout); // If not, then add one by using android.id="#+id/whatever"
photo = (ImageView) view.findViewById(R.id.contact_photo);
name = (TextView) view.findViewById(R.id.contact_name);
number = (TextView) view.findViewById(R.id.contact_number);
}
}
#Override
public ContactsViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.contacts_layout, viewGroup, false);
ContactsViewHolder viewHolder = new ContactsViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(ContactsViewHolder viewHolder, final int position) {
// Update:
item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Do onclick stuff here such as dialing someone
}
});
viewHolder.photo.setImageURI(your_uri);
viewHolder.name.setText(list.get(position).name);
viewHolder.number.setText(list.get(position).number);
}
#Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
#Override
public int getItemCount() {
return list.size();
}
}
Now we have to attach the adapter to our RecyclerView. In the activity that contains the appropriate RecyclerView, add the following code:
private List<Contact> list = new ArrayList<>;
public void loadContacts() {
list.add(new Contacts(photoUri, name, number));
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.your_recyclerview);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
ContactsAdapter adapter = new ContactsAdapter(list);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
You have successfully binded your contacts with your CardView. Tell me how you go.
Related
I have a recycleview showing a list of audio files fetched from my audios.json file hosted on my server. i have a model class with a getter method getLanguage() to see the audio language. I would like to show only audio files of users preference in recycle view. Say for example, if user wants only english and russian i would like to show only list of russian and english. How can we achieve this? Right now the entire list is displayed.
public class AudioAdapter extends RecyclerView.Adapter<AudioAdapter.HomeDataHolder> {
int currentPlayingPosition = -1;
Context context;
ItemClickListener itemClickListener;
List<Output> wikiList;
public AudioAdapter(List<Output> wikiList, Context context) {
this.wikiList = wikiList;
this.context = context;
}
#NonNull
#Override
public HomeDataHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate(R.layout.audio_row_layout,viewGroup,false);
HomeDataHolder mh = new HomeDataHolder(view);
return mh;
}
#Override
public void onBindViewHolder(#NonNull final HomeDataHolder homeDataHolder, int i) {
String desc = wikiList.get(i).getLanguage() + " • " + wikiList.get(i).getType();
homeDataHolder.tvTitle.setText(wikiList.get(i).getTitle());
homeDataHolder.tvotherinfo.setText(desc);
homeDataHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (itemClickListener != null)
itemClickListener.onClick(view,homeDataHolder.getAdapterPosition());
}
});
homeDataHolder.rippleLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (itemClickListener != null)
itemClickListener.onClick(view,homeDataHolder.getAdapterPosition());
}
});
}
#Override
public int getItemCount() {
return wikiList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
public void setClickListener(ItemClickListener itemClickListener) { // Method for setting clicklistner interface
this.itemClickListener = itemClickListener;
}
public class HomeDataHolder extends RecyclerView.ViewHolder {
TextView tvTitle,tvotherinfo;
MaterialRippleLayout rippleLayout;
public HomeDataHolder(View v) {
super(v);
this.tvTitle = v.findViewById(R.id.title);
this.tvotherinfo = v.findViewById(R.id.audioDesc);
this.rippleLayout = v.findViewById(R.id.ripple);
}
}
}
The general idea for this should be:
you have one list with all items
you have filter rules selected by the user
You filter items from number 1, to see which ones match the constraints and store this in another list.
Then the recycler view only shows the items of the list from number 3.
This means that recycler view's getItemCount would return the size of the filtered list, not the whole list.
Instead of passing the wikiList as it is, filter it then send it:
Lets say that you filled up the wikiList, before passing it to the adapter, filter it like this:
In the activity that you initialize the adapter in:
public class YourActivity extends ............{
........
........
//your filled list
private List<Output> wikiList;
//filtered list
private List<Output> filteredList= new ArrayList<Output>();
//filters
private List<String> filters = new ArrayList<String>();
//lets say the user chooses the languages "english" and "russian" after a button click or anything (you can add as many as you want)
filters.add("english");
filters.add("russian");
//now filter the original list
for(int i = 0 ; i<wikiList.size() ; i++){
Output item = wikiList.get(i);
if(filters.contains(item.getLanguage())){
filteredList.add(item);
}
}
//now create your adapter and pass the filteredList instead of the wikiList
AudioAdapter adapter = new AudioAdapter(filteredList , this);
//set the adapter to your recyclerview........
......
.....
......
}
I use above "english" and "russian" for language. I don't know how they are set in your response, maybe you use "en" for "english" so be careful.
I am creating an AlertDialog custom class, called ActionDialog, which will contains a RecyclerView containing Buttons. I have a List of Button that I populate in the custom class ActionDialog (for now i just populate with useless Button just to try to use it, except one which I create in another class).
The problem is that when i create the AlertDialog, all buttons are showing empty, they are showed but with no text/no clicklistener (as you can see in the image below).
(I have added a custom ActionListener to a Button in another class and then give it as parameter in ActionDialog class. Will it lose the ActionListener?)
Here is the result.
I will leave here my ActionDialog class code, and the adapter class.
This is ActionDialog class:
public class ActionDialog extends AlertDialog{
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private Button actionButtons;
private List<Button> buttons;
private Activity context;
public ActionDialog(#NonNull Activity context, Button actionButtons) {
super(context);
this.context = context;
this.actionButtons = actionButtons;
buttons = new ArrayList<>();
initButton();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//requestWindowFeature(Window.FEATURE_NO_TITLE);
}
private void initButton(){
initZoneButton();
//TODO init all buttons
Button b1 = new Button(context);
b1.setText("ExampleButton1");
Button b2 = new Button(context);
b2.setText("ExampleButton2");
b1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String a;
}
});
buttons.add(b1);
buttons.add(b2);
}
private void initZoneButton(){
buttons.add(actionButtons); //this button is created in another class and give as parameter in this class
}
public void createDialog(){
Builder mBuilder = new Builder(context);
View view = context.getLayoutInflater().inflate(R.layout.dialog_actionbuttons_layout, null);
mRecyclerView = view.findViewById(R.id.dialog_actionbuttons_rv);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(context);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new ActionButtonsAdapter(buttons);
mRecyclerView.setAdapter(mAdapter);
mBuilder.setView(view);
mBuilder.create().show();
}
}
Here is the RecyclerView adapter class:
public class ActionButtonsAdapter extends RecyclerView.Adapter<ActionButtonsAdapter.ViewHolder>{
private List<Button> dataButtons;
static class ViewHolder extends RecyclerView.ViewHolder {
Button actionButton;
ViewHolder(View v) {
super(v);
actionButton = v.findViewById(R.id.action_button_rv);
}
}
public ActionButtonsAdapter(List<Button> dataButtons){
this.dataButtons = dataButtons;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.actionButton = dataButtons.get(position);
//i think the problem is here, maybe
}
#Override
public ActionButtonsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_actionbutton_layout, parent, false);
return new ViewHolder(v);
}
#Override
public int getItemCount() {
return dataButtons.size();
}
}
I think in the onBindViewHolder method you should do what ever you want to do with your button.
Also there is no need for the list of buttons here. Make a list the data you need to be held in the Buttons RecyclerView.
I have a RecyclerView that will display Genres for restaurants lets say, So I will create a List of strings to hold these genres names (chickens, meats, etc,..)
Setting its text
holder.actionButton.setText(// Make use of position here);
Or Click Listeners.
Update
You can check google samples for recyclerview here
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
Log.d(TAG, "Element " + position + " set.");
// Get element from your dataset at this position and replace the contents of the view
// with that element
viewHolder.getTextView().setText(mDataSet[position]);
}
wheres mDataset is Array of Strings.
I'm using the cardslib library for having cards in my Android app. It has pre-defined card layouts or we have an option to customize the layout.
While creating the adapter for recycler view, only the list of card objects with pre-defined card layouts are accepted. I get an error when I try to pass a list of custom cards to the constructor.
CardArrayRecyclerViewAdapter cardArrayRecyclerViewAdapter = new CardArrayRecyclerViewAdapter(getApplicationContext(), customCards);
The above code shows an error. customCards is the list of custom card objects. How do I make it work ?
In your CardArrayRecyclerViewAdapter change List<Card> to List<CustomCard> in the constructor
you can write Custom card adapter bellow
card adapter call just like
List<CustomCard> cardlist = new ArrayList<>();
recyclerView.setHasFixedSize(true);
// recyclerView
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// create an Object for Adapter
CardDataAdapter mAdapter = new CardDataAdapter(cardlist);
// set the adapter object to the Recyclerview
recyclerView.setAdapter(mAdapter);
custom Adapter
class CardDataAdapter extends RecyclerView.Adapter<CardDataAdapter.ViewHolder> {
private List<CustomCard> dataSet;
public CardDataAdapter(List<CustomCard> list) {
dataSet = list;
}
#Override
public CardDataAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
// create a new view
View itemLayoutView = LayoutInflater.from(viewGroup.getContext()).inflate(
R.layout.row_custom_card, null);
itemLayoutView.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT));
// create ViewHolder
CardDataAdapter.ViewHolder viewHolder = new CardDataAdapter.ViewHolder(itemLayoutView);
return viewHolder;
}
#Override
public void onBindViewHolder(CardDataAdapter.ViewHolder viewHolder, int i) {
CustomCard fp = dataSet.get(i);
viewHolder.tv_title.setText(fp.getName());
viewHolder.feed = fp;
}
#Override
public int getItemCount() {
return dataSet.size();
}
// inner class to hold a reference to each item of RecyclerView
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView tv_title;
public NotificationModel feed;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
tv_title = (TextView) itemLayoutView .findViewById(R.id.tv_title);
itemLayoutView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Write here
}
});
}
}
}
note : CustomCard is model & row_custom_card layout xml where name Textview resde
The second argument to the CardArrayRecyclerViewAdapter constructor must be of type List<Card>. I know it seems like List<CustomCard> should work here, but it will not.
It's impossible to say what the best way to solve this problem is without seeing more code, but you could do this:
List<Card> cards = new ArrayList<>();
cards.addAll(customCards);
// now call constructor with `cards` instead of `customCards`
You can read more about why List<CustomCard> is not accepted for the List<Card> argument here Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?
Here's my problem:
I have an AlbumActivity that lists all the albums name using RecyclerView.
When one item is clicked it will go to ImagesActivity where all of the images inside the Album will be listed. I also used RecyclerView. ImagesActivity has a toolbar menu that can add multiple images in case the user wants to add another images to the album. When the menu is clicked another activity will be opened to add images path to the database.
My problem is that when I go back to the ImagesActivity the images do not appear. The images will only appear when I only go again to AlbumActivity to view again the album's images. How can I notify the change quickly in the ImagesActivity.
Here's my Adapter:
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder> {
static List<GettersSetters> dbList;
static Context context;
ImageAdapter(Context context, List<GettersSetters> dbList) {
this.dbList = new ArrayList<GettersSetters>();
this.context = context;
this.dbList = dbList;
}
#Override
public ImageAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(
R.layout.item_image, null);
ViewHolder viewHolder = new ViewHolder(itemLayoutView);
return viewHolder;
}
#Override
public void onBindViewHolder(ImageAdapter.ViewHolder holder, int position) {
File imageFile = new File(dbList.get(position).getPath());
if(imageFile.exists()){
Bitmap img = decodeBitmapWithSize(dbList.get(position).getPath(),300,150, true);
holder.imageGallery.setImageBitmap(img);
}else{
holder.imageGallery.setImageResource(R.drawable.not_found);
}
}
#Override
public int getItemCount() {
return dbList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public ImageView imageGallery;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
imageGallery = (ImageView) itemLayoutView.findViewById(R.id.img_row);
itemLayoutView.setOnClickListener(this);
itemLayoutView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Toast.makeText(context,"Delete Image",Toast.LENGTH_LONG).show();
return true;
}
});
}
#Override
public void onClick(View v) {
}
}
You will need to refresh the dbList after add images path to the database.
When you go back to the Images Activity, you get new dbList from database in onActivityResult. And you can refresh the dbList as follows:
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder> {
...
public void updateList(List<GettersSetters> dbList) {
this.dbList.clear();
this.dbList.addAll(dbList);
notifyDataSetChanged();
}
}
The most common way of doing it is to instantiate your List and Adapter in your activity, and call notifyDataSetChanged on your adapter whenever you change the data in the list.
For example, in your Activity class...
List<GetterSetter> list;
ImageAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
list = new ArrayList();
adapter = new ImageAdapter(this, list);
// you will also need to attach your adapter to your recyclerview.
// when you are ready to modify/add/delete items from the list, just do it and call notifyDataSetChanged
list.add(new GetterSetter());
adapter.notifyDataSetChanged(); // This will update your recyclerview to show one item, instead of an empty list
}
In short, you obviously pass your Adapter a List. Any time you change any data in the list, make sure to call notifyDataSetChanged() on the adapter object.
when coming back to Images Activity in onResume method call like this
public void onResume{
youradapter.refreshrecyclerview(); // implement this method in adpater or simply call here
adpater.notifiDataSetChanged();
}
In adpater implement this method,
public void refreshrecyclerview(){
notifiDataSetChanged();
}
once you add the images, are you calling notifydatasetchanged() ? https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#notifyDataSetChanged()
this needs to be called in your ImagesActivity after you add your images to the dbList.
Please read the documentation in the above link to understand notifydatasetchanged()
This method basically informs the Adapter that the data in the dbList is updated and hence it has to refresh the UI.
I have a RecyclerView that will contain list of item retrieved from the internet. So at first, the list will be empty. After the data retrieved from the internet, it will update the list and call notifyDataSetChanged().
I can adapt the data into the RecyclerView just fine. But, I have an ImageButton for each of item which has different Image if it's clicked. If I initialize the flags array inside onBindViewHolder, each time I scrolled the RecyclerView, the flag array will be reinitialize to false. If I initialize it in the Adapter constructor, it will be 0 index since the list will be empty at first. Where should I put array initializing in adapter if the data will come at some amount of time later?
Below is my code, but the flag array (isTrue) is always reinitialize each time I scrolled my RecyclerView.
public class SomethingAdapter extends RecyclerView.Adapter<SomethingAdapter.ViewHolder> {
private ArrayList<String> someList;
private boolean[] isTrue;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView someText;
public ImageButton someButton;
public ViewHolder(View v) {
super(v);
someText = (TextView) v.findViewById(R.id.text);
someButton = (ImageButton) v.findViewById(R.id.button);
}
}
public SomethingAdapter(ArrayList<String> someList) {
this.someList = someList;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.some_layout, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
//TODO: This thing will make isTrue always reinitialize if scrolled
this.isTrue = new boolean[someList.getResults().size()];
viewHolder.someText.setText(someList.get(position));
if (isTrue[position]) {
viewHolder.someButton.setImageResource(R.drawable.button_true);
} else {
viewHolder.someButton.setImageResource(R.drawable.button_false);
}
viewHolder.someButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (isTrue[position]) {
//Connect to the internet and if response is positive {
//isTrue[position] = false;
//viewHolder.someButton.setImageResource(R.drawable.button_false);
//}
} else {
//Connect to the internet and if response is positive {
//isTrue[position] = true;
//viewHolder.someButton.setImageResource(R.drawable.button_true);
//}
}
}
});
}
#Override
public int getItemCount() {
return someList.size();
}
Initialize it when you add items to someList.
Also, don't add click listener in your onBind, create it in onCreateViewHolder. You cannot use position in the click callback, instead you should be using ViewHolder#getAdapterPosition.
See docs for details:
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#onBindViewHolder(VH, int)