Reuse the RecyclerView adapter - java

I have very small question here, in my recycle view adapter class i'm using List<FeaturedTags> and its working fine.
Now we have newly introduced class called 'FeaturedLangTags, the only difference between FeaturedTags & FeaturedLangTagsis just an addition of Lang field. But we are not using this Lang field anyway to show on screen.
The output of the recycle view looks exactly similar to existing FeaturedTags adapter. Here i want to know how i can re-use the existing adapter class to display List<FeaturedLangTags> items?
One simple way is to duplicate the existing adapter and pass the FeaturedLangTagslist, but here so much of code is getting duplicated. I would like to know how i can tweek the existing class?

Create Adapter of generic List<T> which can be used in both condition.
public abstract class GenericAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private ArrayList<T> items;
public abstract RecyclerView.ViewHolder setViewHolder(ViewGroup parent);
public abstract void onBindData(RecyclerView.ViewHolder holder, T val);
public GenericAdapter(Context context, ArrayList<T> items){
this.context = context;
this.items = items;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder holder = setViewHolder(parent);
return holder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
onBindData(holder,items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
public void addItems( ArrayList<T> savedCardItemz){
items = savedCardItemz;
this.notifyDataSetChanged();
}
public T getItem(int position){
return items.get(position);
}
}
adapter = new GenericAdapter<DataModel>(context,modelList) {
#Override
public RecyclerView.ViewHolder setViewHolder(ViewGroup parent) {
final View view = LayoutInflater.from(context).inflate(R.layout.item_view_holder, parent, false);
ItemViewHolder viewHolder = new ItemViewHolder(context, view);
return viewHolder;
}
#Override
public void onBindData(RecyclerView.ViewHolder holder1, DataModel val) {
DataModel userModel = val;
ItemViewHolder holder = (ItemViewHolder)holder1;
holder.name.setText(userModel.getName());
holder.fatherName.setText(userModel.getFatherName());
}
};
mRecyclerView.setAdapter(adapter);

Create a common interface or superclass to FeaturedTags and FeaturedLangTags, then use the same adapter.
Or use a more generic approach to your adapter like AdapterDelegates.

You can create two different constructors for the adapter and achieve it.

You can have both the classes implement one common interface say CommonInterface
Make your list of type
YourList<CommonInterface>
In your adapter, override the
getItemViewType
Method. Here use the instance of operator to check what type of class it is and return a view type integer. Say for without lang, it will be an integer 0 and with it, 1
Now in your onCreateViewHolder, you get passed the viewType as the second parameter. Using that inflate your layout as your choice for the view.
In your onBindViewHolder, you can check
getItemViewType
On the holder passed in the parameter and cast your interface explicitly to the object you want. Then use it as usual.
Reference for this multiple view types is
Recycler view multiple view types

You can use ConcatAdapter of RecyclerView to concatenate your adapters and reuse them.
MyAdapter adapter1 = ...;
AnotherAdapter adapter2 = ...;
ConcatAdapter concatenated = new ConcatAdapter(adapter1, adapter2);
recyclerView.setAdapter(concatenated);
"Favor composition over inheritance"

Related

Bug with the recycle view and the viewholder adapater in Android

I am getting a very strange bug in my application. To make it more clear I will create a similar example with minus code.
I am reading objects from Firebase Realtime Database with an addListenerForSingleValueEvent. While I am reading the objects, I am stored them in an Array that I passed to an Adapter in a Recycleview. At this point, I can say, after debugging, that all seems to work correctly.
Then in the Adapter, I have a code similar to this:
public class AdapterObject extends RecyclerView.Adapter<AdapterObject.ViewHolder> {
ArrayList<Object> objectList;
Context mContext;
public AdapterObject (Context context, ArrayList<Object> objectList){
this.mContext = context;
this.objectList = objectList;
}
#NonNull
#Override
public AdapterObject.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.object_grid_layout, parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull AdapterObject.ViewHolder holder, int position) {
Object o = objectList.get(position);
Log.d("TAG", o.getAtribute());
if (o.getAtribute().equals("A")){
holder.atribute.setVisibility(View.VISIBLE);
}
#Override
public int getItemCount() {
return objectList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView atribute;
boolean favorite;
String descuento, precioOriginal;
public ViewHolder(#NonNull View itemView) {
super(itemView);
atribute = itemView.findViewById(R.id.atribute);
}
}
}
As you can see in the code if the current object has the attribute value == "A", then his Textview is displayed, otherwise, the Textview remains hidden.
All seems correct when I debug it because the objects and their attribute corresponds to the Database, but when I deploy the application in the Android simulator and I start going up and down on the Recycleview, the holders start to display the Textviews although the console debugs seems correct...
Is this normal in RecycleViews? How can I fix that? I have found this, do you think it has any relation?
This is an extract from the RecyclerView documentation
As the name implies, RecyclerView recycles those individual elements.
When an item scrolls off the screen, RecyclerView doesn't destroy its
view. Instead, RecyclerView reuses the view for new items that have
scrolled onscreen. This reuse vastly improves performance, improving
your app's responsiveness and reducing power consumption.
That means that when the view gets reused it will keep the current properties. It's up to you to change them when onBindViewHolder gets called.
In your specific case
#Override
public void onBindViewHolder(#NonNull AdapterObject.ViewHolder holder, int position) {
Object o = objectList.get(position);
Log.d("TAG", o.getAtribute());
if (o.getAtribute().equals("A")){
holder.atribute.setVisibility(View.VISIBLE);
} else. {
holder.atribute.setVisibility(View.GONE);
}
}

How to call bindViewHolder when RecyclerView data changes [duplicate]

I have a RecyclerView with an TextView text box and a cross button ImageView. I have a button outside of the recyclerview that makes the cross button ImageView visible / gone.
I'm looking to remove an item from the recylerview, when that items cross button ImageView is pressed.
My adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {
private ArrayList<String> mDataset;
private static Context sContext;
public MyAdapter(Context context, ArrayList<String> myDataset) {
mDataset = myDataset;
sContext = context;
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_text_view, parent, false);
ViewHolder holder = new ViewHolder(v);
holder.mNameTextView.setOnClickListener(MyAdapter.this);
holder.mNameTextView.setOnLongClickListener(MyAdapter.this);
holder.mNameTextView.setTag(holder);
return holder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mNameTextView.setText(mDataset.get(position));
}
#Override
public int getItemCount() {
return mDataset.size();
}
#Override
public void onClick(View view) {
ViewHolder holder = (ViewHolder) view.getTag();
if (view.getId() == holder.mNameTextView.getId()) {
Toast.makeText(sContext, holder.mNameTextView.getText(), Toast.LENGTH_SHORT).show();
}
}
#Override
public boolean onLongClick(View view) {
ViewHolder holder = (ViewHolder) view.getTag();
if (view.getId() == holder.mNameTextView.getId()) {
mDataset.remove(holder.getPosition());
notifyDataSetChanged();
Toast.makeText(sContext, "Item " + holder.mNameTextView.getText() + " has been removed from list",
Toast.LENGTH_SHORT).show();
}
return false;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mNumberRowTextView;
public TextView mNameTextView;
public ViewHolder(View v) {
super(v);
mNameTextView = (TextView) v.findViewById(R.id.nameTextView);
}
}
}
My layout is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:id="#+id/layout">
<TextView
android:id="#+id/nameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:padding="5dp"
android:background="#drawable/greyline"/>
<ImageView
android:id="#+id/crossButton"
android:layout_width="16dp"
android:layout_height="16dp"
android:visibility="gone"
android:layout_marginLeft="50dp"
android:src="#drawable/cross" />
</LinearLayout>
How can I get something like an onClick working for my crossButton ImageView? Is there a better way? Maybe changing the whole item onclick into a remove the item? The recyclerview shows a list of locations that need to be edited. Any technical advice or comments / suggestions on best implementation would be hugely appreciated.
I have done something similar.
In your MyAdapter:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public CardView mCardView;
public TextView mTextViewTitle;
public TextView mTextViewContent;
public ImageView mImageViewContentPic;
public ImageView imgViewRemoveIcon;
public ViewHolder(View v) {
super(v);
mCardView = (CardView) v.findViewById(R.id.card_view);
mTextViewTitle = (TextView) v.findViewById(R.id.item_title);
mTextViewContent = (TextView) v.findViewById(R.id.item_content);
mImageViewContentPic = (ImageView) v.findViewById(R.id.item_content_pic);
//......
imgViewRemoveIcon = (ImageView) v.findViewById(R.id.remove_icon);
mTextViewContent.setOnClickListener(this);
imgViewRemoveIcon.setOnClickListener(this);
v.setOnClickListener(this);
mTextViewContent.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
if (mItemClickListener != null) {
mItemClickListener.onItemClick(view, getPosition());
}
return false;
}
});
}
#Override
public void onClick(View v) {
//Log.d("View: ", v.toString());
//Toast.makeText(v.getContext(), mTextViewTitle.getText() + " position = " + getPosition(), Toast.LENGTH_SHORT).show();
if(v.equals(imgViewRemoveIcon)){
removeAt(getPosition());
}else if (mItemClickListener != null) {
mItemClickListener.onItemClick(v, getPosition());
}
}
}
public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
public void removeAt(int position) {
mDataset.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mDataSet.size());
}
Edit:
getPosition() is deprecated now, use getAdapterPosition() instead.
first of all, item should be removed from the list!
mDataSet.remove(getAdapterPosition());
then:
notifyItemRemoved(getAdapterPosition());
notifyItemRangeChanged(getAdapterPosition(), mDataSet.size()-getAdapterPosition());
if still item not removed use this magic method :)
private void deleteItem(int position) {
mDataSet.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mDataSet.size());
holder.itemView.setVisibility(View.GONE);
}
Kotlin version
private fun deleteItem(position: Int) {
mDataSet.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, mDataSet.size)
holder.itemView.visibility = View.GONE
}
The Problem
RecyclerView was built to display data in an efficient and responsive manner.
Usually you have a dataset which is passed to your adapter and is looped through to display your data.
Here your dataset is:
private ArrayList<String> mDataset;
The point is that RecyclerView is not connected to your dataset, and therefore is unaware of your dataset changes.
It just reads data once and displays it through your ViewHolder, but a change to your dataset will not propagate to your UI.
This means that whenever you make a deletion/addition on your data list, those changes won't be reflected to your RecyclerView directly. (i.e. you remove the item at index 5, but the 6th element remains in your recycler view).
A (old school) solution
RecyclerView exposes some methods for you to communicate your dataset changes, reflecting those changes directly on your list items.
The standard Android APIs allow you to bind the process of data removal (for the purpose of the question) with the process of View removal.
The methods we are talking about are:
notifyItemChanged(index: Int)
notifyItemInserted(index: Int)
notifyItemRemoved(index: Int)
notifyItemRangeChanged(startPosition: Int, itemCount: Int)
notifyItemRangeInserted(startPosition: Int, itemCount: Int)
notifyItemRangeRemoved(startPosition: Int, itemCount: Int)
A Complete (old school) Solution
If you don't properly specify what happens on each addition, change or removal of items, RecyclerView list items are animated unresponsively because of a lack of information about how to move the different views around the list.
The following code will allow RecyclerView to precisely play the animation with regards to the view that is being removed (And as a side note, it fixes any IndexOutOfBoundExceptions, marked by the stacktrace as "data inconsistency").
void remove(position: Int) {
dataset.removeAt(position)
notifyItemChanged(position)
notifyItemRangeRemoved(position, 1)
}
Under the hood, if we look into RecyclerView we can find documentation explaining that the second parameter we pass to notifyItemRangeRemoved is the number of items that are removed from the dataset, not the total number of items (As wrongly reported in some others information sources).
/**
* Notify any registered observers that the <code>itemCount</code> items previously
* located at <code>positionStart</code> have been removed from the data set. The items
* previously located at and after <code>positionStart + itemCount</code> may now be found
* at <code>oldPosition - itemCount</code>.
*
* <p>This is a structural change event. Representations of other existing items in the data
* set are still considered up to date and will not be rebound, though their positions
* may be altered.</p>
*
* #param positionStart Previous position of the first item that was removed
* #param itemCount Number of items removed from the data set
*/
public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
mObservable.notifyItemRangeRemoved(positionStart, itemCount);
}
Open source solutions
You can let a library like FastAdapter, Epoxy or Groupie take care of the business, and even use an observable recycler view with data binding.
New ListAdapter
Google recently introduced a new way of writing the recycler view adapter, which works really well and supports reactive data.
It is a new approach and requires a bit of refactoring, but it is 100% worth switching to it, as it makes everything smoother.
here is the documentation, and here a medium article explaining it
Here are some visual supplemental examples. See my fuller answer for examples of adding and removing a range.
Add single item
Add "Pig" at index 2.
String item = "Pig";
int insertIndex = 2;
data.add(insertIndex, item);
adapter.notifyItemInserted(insertIndex);
Remove single item
Remove "Pig" from the list.
int removeIndex = 2;
data.remove(removeIndex);
adapter.notifyItemRemoved(removeIndex);
Possibly a duplicate answer but quite useful for me. You can implement the method given below in RecyclerView.Adapter<RecyclerView.ViewHolder>
and can use this method as per your requirements, I hope it will work for you
public void removeItem(#NonNull Object object) {
mDataSetList.remove(object);
notifyDataSetChanged();
}
I tried all the above answers, but inserting or removing items to recyclerview causes problem with the position in the dataSet. Ended up using delete(getAdapterPosition()); inside the viewHolder which worked great at finding the position of items.
The problem I had was I was removing an item from the list that was no longer associated with the adapter to make sure you are modifying the correct adapter you can implement a method like this in your adapter:
public void removeItemAtPosition(int position) {
items.remove(position);
}
And call it in your fragment or activity like this:
adapter.removeItemAtPosition(position);
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private List<cardview_widgets> list;
public MyAdapter(Context context, List<cardview_widgets> list) {
this.context = context;
this.list = list;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(this.context).inflate(R.layout.fragment1_one_item,
viewGroup, false);
return new MyViewHolder(view);
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView txtValue;
TextView txtCategory;
ImageView imgInorEx;
ImageView imgCategory;
TextView txtDate;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
txtValue= itemView.findViewById(R.id.id_values);
txtCategory= itemView.findViewById(R.id.id_category);
imgInorEx= itemView.findViewById(R.id.id_inorex);
imgCategory= itemView.findViewById(R.id.id_imgcategory);
txtDate= itemView.findViewById(R.id.id_date);
}
}
#NonNull
#Override
public void onBindViewHolder(#NonNull final MyViewHolder myViewHolder, int i) {
myViewHolder.txtValue.setText(String.valueOf(list.get(i).getValuee()));
myViewHolder.txtCategory.setText(list.get(i).getCategory());
myViewHolder.imgInorEx.setBackgroundColor(list.get(i).getImg_inorex());
myViewHolder.imgCategory.setImageResource(list.get(i).getImg_category());
myViewHolder.txtDate.setText(list.get(i).getDate());
myViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
list.remove(myViewHolder.getAdapterPosition());
notifyDataSetChanged();
return false;
}
});
}
#Override
public int getItemCount() {
return list.size();
}}
i hope this help you.
if you want to remove item you should do this:
first remove item:
phones.remove(position);
in next step you should notify your recycler adapter that you remove an item by this code:
notifyItemRemoved(position);
notifyItemRangeChanged(position, phones.size());
but if you change an item do this:
first change a parameter of your object like this:
Service s = services.get(position);
s.done = "Cancel service";
services.set(position,s);
or new it like this :
Service s = new Service();
services.set(position,s);
then notify your recycler adapter that you modify an item by this code:
notifyItemChanged(position);
notifyItemRangeChanged(position, services.size());
hope helps you.
String str = arrayList.get(position);
arrayList.remove(str);
MyAdapter.this.notifyDataSetChanged();
To Method onBindViewHolder Write This Code
holder.remove.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Cursor del=dbAdapter.ExecuteQ("delete from TblItem where Id="+values.get(position).getId());
values.remove(position);
notifyDataSetChanged();
}
});
Incase Anyone wants to implement something like this in Main class instead of Adapter class, you can use:
public void removeAt(int position) {
peopleListUser.remove(position);
friendsListRecycler.getAdapter().notifyItemRemoved(position);
friendsListRecycler.getAdapter().notifyItemRangeChanged(position, peopleListUser.size());
}
where friendsListRecycler is the Adapter name
you must to remove this item from arrayList of data
myDataset.remove(holder.getAdapterPosition());
notifyItemRemoved(holder.getAdapterPosition());
notifyItemRangeChanged(holder.getAdapterPosition(), getItemCount());
//////// set the position
holder.cancel.setTag(position);
///// click to remove an item from recycler view and an array list
holder.cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int positionToRemove = (int)view.getTag(); //get the position of the view to delete stored in the tag
mDataset.remove(positionToRemove);
notifyDataSetChanged();
}
});
make interface into custom adapter class and handling click event on recycler view..
onItemClickListner onItemClickListner;
public void setOnItemClickListner(CommentsAdapter.onItemClickListner onItemClickListner) {
this.onItemClickListner = onItemClickListner;
}
public interface onItemClickListner {
void onClick(Contact contact);//pass your object types.
}
#Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
// below code handle click event on recycler view item.
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onItemClickListner.onClick(mContectList.get(position));
}
});
}
after define adapter and bind into recycler view called below code..
adapter.setOnItemClickListner(new CommentsAdapter.onItemClickListner() {
#Override
public void onClick(Contact contact) {
contectList.remove(contectList.get(contectList.indexOf(contact)));
adapter.notifyDataSetChanged();
}
});
}
In case you are wondering like I did where can we get the adapter position in the method getadapterposition(); its in viewholder object.so you have to put your code like this
mdataset.remove(holder.getadapterposition());
In the activity:
mAdapter.updateAt(pos, text, completed);
mAdapter.removeAt(pos);
In the your adapter:
void removeAt(int position) {
list.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, list.size());
}
void updateAt(int position, String text, Boolean completed) {
TodoEntity todoEntity = list.get(position);
todoEntity.setText(text);
todoEntity.setCompleted(completed);
notifyItemChanged(position);
}
in 2022, after trying everything the whole internet given below is the answer
In MyViewHolder class
private myAdapter adapter;
inside MyViewHolder function initalise adapter
adapter = myAdapter.this
inside onclick
int position = getAdapterPosition()
list.remove(position);
adapter.notifyItemRemoved(position);

Using enums to pass data to listviews

I'm attempting to use multiple enums that I've set as subclasses to a master class as the data source for listviews on separate fragments.
Each fragment will have two listviews, but each listview will only need to use certain parts of the data. Each enum has two strings "name" and "abbr" and a double "value".
I would like to set both strings as the ListView titles, and use the value in a calculation.
Listview1 will hold titles, abbrs, and one EditText in the center row. Listview2 will hold titles, abbrs, and one more TextView that will update based on the EditText input and the value from the enum. I realize I will need two custom adapters for this, one for the heterogeneous Listview1, and one for Listview2.
I am a little lost on implementing, and have only attempted doing the custom adapter for Listview2.
I have tried looking at multiple SO questions, and listview tutorials that use database models and then have tried to use that but with my static enum lists, but am just a bit lost. Any help from a high level approach, specifics, or a nice tutorial would be much appreciated. I am probably not even close on the right path as I am new at Android and relatively new at OOP, thanks for bearing with the poor code!
What I have so far (I set up a test project, which is why I have this listview inflated in main as opposed to in a fragment - if there are any issues with this besides switching the context let me know):
Enum class holding all enums
public class Enums {
public enum Pressures{
ITEM1 ("name", "abbr", 1.0),
etc...;
private final String name;
private final String abbr;
private final double value;
Pressures(String name, String abbr, double value) {
this.name = name;
this.abbr = abbr;
this.intermediary = intermediary;
}
public String getNames() {
return name;
}
public String getAbbr() {
return abbr;
}
public double getIntermediary() {
return intermediary;
}
}
public enum Enum2 {
...
}
}
Custom Adapter for Listview2:
public class CustomAdapter extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<Enums.Pressures> pressureEnum;
public CustomAdapter(Activity activity, List<Enums.Pressures> units) {
this.activity = activity;
this.pressureEnum = units;
}
#Override
public int getCount() {
return pressureEnum.size();
}
#Override
public Object getItem(int location) {
return pressureEnum.get(location);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (inflater == null)
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null)
convertView = inflater.inflate(R.layout.list_row, null);
TextView name = (TextView) convertView.findViewById(R.id.name);
TextView abbr = (TextView) convertView.findViewById(R.id.abbr);
Enums.Pressures p = pressureEnum.get(position);
name.setText(p.getNames());
abbr.setText(p.getAbbr());
return convertView;
}
}
Main:
public class Main extends Activity {
//This List was a poor attempt at setting the list from the enum
//I don't believe ArrayList is the proper choice as I have an enum object
//but I'm not quite sure what to use
private List<Enums.Pressures> pressureUnits = new ArrayList<Enums.Pressures>();
private ListView listView;
private CustomAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.list);
adapter = new CustomAdapter(this, pressureUnits);
listView.setAdapter(adapter);
}
}
Your List "pressureUnits" is empty. You've specified that it is a list of Pressures but not added anything to it. Usually this is done with the "add" method, i.e. pressureUnits.add(...).
However what I think you want to use is Enums.Pressures.values(). This will return an array containing each of your enum elements. Then you will be able to create an adapter using that array. If you can't do it with the BaseAdapter you are using now, have a look at using the ArrayAdapter class rather than the BaseAdapter.

getItem(int) clashes with getItem(int)

I'm new to Java, so please excuse me if this is a dumb question.
I got this class:
public class CustomDrawerAdapter extends ArrayAdapter<DrawerItem> {
Context context;
List<DrawerItem> drawerItemList;
int layoutResID;
private FriendInfo[] friends = null;
public void setFriendList(FriendInfo[] friends)
{
this.friends = friends;
}
public int getCount() {
return friends.length;
}
public FriendInfo getItem(int position) {
return friends[position];
}
public long getItemId(int position) {
return 0;
}
public CustomDrawerAdapter(Context context, int layoutResourceID,
List<DrawerItem> listItems) {
super(context, layoutResourceID, listItems);
this.context = context;
this.drawerItemList = listItems;
this.layoutResID = layoutResourceID;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
DrawerItemHolder drawerHolder;
View view = convertView;
if (view == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
drawerHolder = new DrawerItemHolder();
view = inflater.inflate(layoutResID, parent, false);
drawerHolder.ItemName = (TextView) view
.findViewById(R.id.drawer_itemName);
drawerHolder.icon = (ImageView) view.findViewById(R.id.drawer_icon);
view.setTag(drawerHolder);
} else {
drawerHolder = (DrawerItemHolder) view.getTag();
}
DrawerItem dItem = (DrawerItem) this.drawerItemList.get(position);
drawerHolder.icon.setImageDrawable(view.getResources().getDrawable(
dItem.getImgResID()));
drawerHolder.ItemName.setText(friends[position].userName);
return view;
}
private static class DrawerItemHolder {
TextView ItemName;
ImageView icon;
}
At FriendInfo I get the following error:" 'getItem(int)' in '(My Package name).CustomDrawerAdapter' clashes with 'getItem(int)' in 'android.widget.ArrayAdapter'; attempting to use incompatible return type"
I'm new to Java and I don't know how to fix this, could somebody help me?
Your CustomDrawerAdapter extends ArrayAdapter<T> with T being a DrawerItem. T is a type parameter which is replaced by a type argument when you define your CustomDrawerAdapter class, the type argument being DrawerItem in your case.
So CustomDrawerAdapter is basically an adapter pulling the items to show from an array of DrawerItems.
The ArrayAdapter defines a method
public T getItem (int position)
which will be
public DrawerItem getItem (int position)
for your class CustomDrawerAdapter.
You might want to read this tutorial about generics: http://docs.oracle.com/javase/tutorial/java/generics. Note that generics are not easy to understand but you absolutely need a basic understanding when doing Android development.
If you decide to override that method then you can't change the return value unless the method has a different signature (name, plus the number and the type of its parameters).
You have to ask yourself: is CustomDrawerAdapter an adapter pulling its data from an array (or list) of FriendInfo or from an array of DrawerItem? Depending on the answer your class definition would be
public class CustomDrawerAdapter extends ArrayAdapter<DrawerItem>
or
public class CustomDrawerAdapter extends ArrayAdapter<FriendInfo>
Using both classes to back the ArrayAdapter doesn't make sense unless FriendInfo is extending DrawerItem and that seems unlikely.
If you really need a second array of Objects (FriendInfo) for whatever purposes, then don't modify the ArrayAdapter methods (like getCount(), getItemId(int) or getItem(int)) unless you know exactly what you're doing.
E.g. your getCount() is doomed to fail because it returns the size of FriendInfo[] instead of the number of DrawerItems and FriendInfo[] might not have the same number of elements (it certainly doesn't have before you call the setFriendList and who guarantees that the passed array has the same number of elements as the List you pass in the constructor?).
Because it extends ArrayAdapter<DrawerItem>, getItem needs to return a DrawerItem. If you can't find a clean way to fix this while extending ArrayAdapter, it's pretty easy to make your own adapter implementation by extending BaseAdapter.

Help on a simple Android ListView problem

I am new to Android, so this may seem like a basic question. But what I got is a very simple class called SingleItem, which has an integer and a String and a getter and setter for each. In my application, I got an ArrayList which holds a collection of SingleItem objects. I also have a layout with a ListView widget.
What I am trying to do is populate the ListView with my String value in SingleItem, but when a user selects an item from the ListView, I need the integer ID from that SingleItem value. How do I do this in Android development?
If you are using your own adapter to populate the list then in the getView() function when building the view to return you can call setTag() on the view you are returning and store the entire "SingleItem" object. Then in the onClickListener of the views you return you can retrieve your info using the getTag() method of the view that has been clicked.
EDIT:
Specified which onClickListener I am referring to
here is a bunch of pseudo code: create your own adapter. This will give the flexibility to do all kinds of things but important to you here is displaying only the relevant fields from your custom class and make more complicated listviews. a decent tutorial is here: http://developerlife.com/tutorials/?p=327
You will have to handle the other overrides of baseadapter but the key is assigning the value of singleItem.getString()
public class SingleItemAdapter extends BaseAdapter{
private ArrayList<SingleItem> m_items= new ArrayList<SingleItem>();
private Context mContext;
public SingleItemAdapter (Context c,ArrayList<SingleItem> items) {
mContext = c;
m_items= items;
}
.
.
.
#Override
public Object getItem(int arg0) {
return m_items.get(arg0);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.singleitemview, null);
}
SingleItem i=(SingleItem) getITem(position)
if(v!=null){
TextView tv=(TextView) v.findViewById(R.id.yourListItemView);
tv.setText(i.getStringValue());
}
}
}
After defining your custom adapter, you can then assign it to the listview and assign a listener to the OnItemSelectedListener. since this returns the position, you can tie that back to the position in your ArrayList of SingleItems.
.
.
.
SingleItemAdapter sia=new SingleItemAdapter(this,yourArray);
yourArrayList.setAdapter(sia);
yourArrayList.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View v, int position, long row) {
SingleItem si= yourArray.getItem(position);
//do something with si.getValue();
}
.
.
.
});

Categories