I'm basically trying to display multiple views via the same ListView adapter. However, the adapter ends up generating multiple duplicates and crashes sometimes as well with a NullPointer. My guess is that I have implemented the adapter all wrong. Here's my complete code:
The item could either be a photo or a text.
Adapter:
public class FeedAdapter extends BaseAdapter {
static private Activity activity;
private static LayoutInflater inflater = null;
ArrayList<ActivityTable> actList = new ArrayList<ActivityTable>();
Holder holder;
public FeedAdapter(Activity a, ArrayList<ActivityTable> actList) {
activity = a;
this.actList = actList;
}
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
final ActivityTable act = actList.get(position);
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
if (act.getType().equals("text")) {
convertView = inflater.inflate(R.layout.feed_single_text, null);
holder = new Holder();
//More code that Set the caption to the holder
convertView.setTag(holder);
}
if (act.getType().equals("photo")) {
convertView = inflater.inflate(R.layout.feed_single_picture, parent, false);
holder = new Holder();
holder.media = (ImageView) convertView.findViewById(R.id.postphoto);
//More code that Set the photo to the holder
convertView.setTag(holder);
}
} else {
holder = (Holder) convertView.getTag();
}
return convertView;
}
public static class Holder {
ImageView media;
TextView caption;
}
}
Am I inflating multiple views in the same adapter the wrong way? Can anyone point out the error?
You have 2 diffrent layout for each row so I think you should add
#Override
public int getViewTypeCount() {
return 2;
}
to your listview adapter
In your code, try to initial your LayoutInflater inside the constructor of your adapter
public FeedAdapter(Activity a, ArrayList<ActivityTable> actList) {
...
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
And also you should optimize your ListView performance
Here is my experience
It is good to have these 3 in place.
#Override
public int getCount() {
return actList().size();
}
#Override
public Object getItem(int position) {
return actList().get(position);
}
#Override
public long getItemId(int position) {
return position;
}
Here is the important part,
first you have to tell the adapter how many type,
and then you have to tell the adapter how to determine the type.
Here I tell type View Type = 2
#Override
public int getViewTypeCount() {
return 2;
}
and Here I tell the adapter How I put the type number into the array
I use setType = 0 || set Type = 1
personal preference here: I like to use int instead of String
#Override
public int getItemViewType(int position) {
return act.get(position).getType();
}
and then later at the getView
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
int listViewItemType = getItemViewType(position);
if (v == null) {
..whatevever you doing to make v not null
}
if (listViewItemType == 0) {
//Do something
}else if(listViewItemType == 1){
// Do something different
}
return v;
}
Yes you will get duplicate Item, Because Convertview is reusing. Once convertview is created that view using if you scroll.
So better use single layout and with both Image and text. Based type hide any one.
xml file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="#+id/ImgFeed"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/txtCaption"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
try this you are using ImageView insted of TextView
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
final ActivityTable act = actList.get(position);
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = inflater.inflate(R.layout.feed_layout, null);
holder = new Holder();
holder.caption = (TextView) convertView.findViewById(R.id.txtCaption);
holder.media = (ImageView) convertView.findViewById(R.id.ImgFeed);
if (act.getType().equals("text")) {
holder.media.setVisibility(View.GONE)
}
else if (act.getType().equals("photo")) {
holder.caption.setVisibility(View.GONE)
}
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
return convertView;
}
Related
I am just adapting my custom adapter code with ViewHolder so that i can optimize my list view with a recycler, but i am not sure if i do it right.
My view holder class:
public class ViewHolderTask {
int positionHolder;
TextView nameHolder;
TextView timeHolder;
TextView sessionHolder;
TextView dateHolder;
FloatingActionButton mFabTaskHolder;
public ViewHolderTask(View v, int position) {
this.positionHolder = position;
this.nameHolder = v.findViewById(R.id.taskNameText);
this.timeHolder = v.findViewById(R.id.timeTextView);
this.sessionHolder = v.findViewById(R.id.textViewSession);
this.dateHolder = v.findViewById(R.id.dateTextView);
this.mFabTaskHolder = v.findViewById(R.id.myFabTask);
}
My custom adapter class:
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
ViewHolderTask holder;
if(convertView == null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE );
convertView = inflater.inflate(R.layout.task_row, parent, false);
holder = new ViewHolderTask(convertView, position);
convertView.setTag(holder);
}else{
holder = (ViewHolderTask) convertView.getTag();
}
Task task = taskArrayList.get(position);
//set the configurations
holder.getTimeHolder().setText(getTimeString(task.getTime()));
holder.getNameHolder().setText(task.getName());
holder.getDateHolder().setText(getDateString(task.getDate()));
holder.getSessionHolder().setText(getSessionString(task.getSession()));
//Set the FAB listener
addFabListener(holder.getmFabTaskHolder(), position);
//set the clicked background
if(TaskActivity.getIsClicked() && TaskActivity.getPositionClicked()-1 == position){
convertView.setBackgroundResource(R.color.backgroundSelectedItem);
}
return convertView;
}
Do I use it right?
Seems to be fine for me other than this portion of the code
//set the clicked background
if(TaskActivity.getIsClicked() && TaskActivity.getPositionClicked()-1 == position){
convertView.setBackgroundResource(R.color.backgroundSelectedItem);
}
You might need to reset the background resource back to default for the item which is not clicked. maybe you have to add "else" part to the "if"
I am trying to implement a customlistadapter for a small project of mine. I basically want ask java to use the appropriate class to inflate the view. I have here first:
public class slide {
public class video {
VideoView videoOfTheDay;
//Purpose of this constructor
public video(VideoView videoOfTheDay) {
this.videoOfTheDay = videoOfTheDay;
}
public VideoView getVideoOfTheDay() {
return videoOfTheDay;
}
}
public class blog {
ImageView imageOfTheDay;
TextView messageOfTheDay;
public blog(ImageView imageOfTheDay, TextView messageOfTheDay) {
this.imageOfTheDay = imageOfTheDay;
this.messageOfTheDay = messageOfTheDay;
}
public ImageView getImageOfTheDay() {
return imageOfTheDay;
}
public TextView getMessageOfTheDay() {
return messageOfTheDay;
}
}
public class advertisement {
ImageView ImageViewAd1;
ImageView ImageViewAd2;
public advertisement(ImageView imageViewAd1, ImageView imageViewAd2) {
this.ImageViewAd1 = imageViewAd1;
this.ImageViewAd2 = imageViewAd2;
}
public ImageView getImageViewAd1() {
return ImageViewAd1;
}
public ImageView getImageViewAd2() {
return ImageViewAd2;
}
}
}`
I have listed all the classes within a superclass slide because I wasn't able to accomplish no errors without them being grouped. From there I went to ask Java to look within itself and determine the appropriate class to use to populate the element:
class CustomListAdapter extends BaseAdapter {
private ArrayList<slide> customVariableDisplay;
private LayoutInflater layoutInflater;
public CustomListAdapter(Context context, ArrayList<slide>customVariableDisplay) {
this.customVariableDisplay = customVariableDisplay;
layoutInflater = LayoutInflater.from(context);
}
public int getCount() {
return customVariableDisplay.size();
}
public Object getItem(int position) {
return customVariableDisplay.get(position);
}
public long getItemId(int position) {
return position;
}
// If the element of the slide is a video -- then the getView will return...
if(slide==slide.video){
public View getView ( int position, View convertView, ViewGroup parent){
ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.act_layout, null);
holder = new ViewHolder();
holder.slide.video = (VideoView) convertView.findViewById(R.id.videolayout);
}
else{
holder = (ViewHolder)convertView.getTag();
}
holder.video.setVideoResource(customVariableDisplay.get(position).getVideoOfTheDay());
}
return convertView;
}
// If the element is a 'blog' then --- then the getView will return...
else if(slide==slide.blog){
public View getView ( int position, View convertView, ViewGroup parent){
ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.act_layout, null);
holder = new ViewHolder();
holder.slide.blog.message = (TextView) convertView.findViewById(R.id.messageInLayout);
holder.slide.blog.image = (ImageView) convertView.findViewById(R.id.imageInLayout);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
//Can write to getClass() for either?
//Ex: holder.(setImageResource(cVD) || setText(cVD)).getClass ??
holder.image.setImageResource(customVariableDisplay.get(position).getImageofTheDay());
holder.message.setText(customVariableDisplay.get(position).getMessageOfTheDay());
}
return convertView;
}
//Else if the element of the slide is an 'advertisement' then the getView will return...
else if (slide==slide.advertisement){
public View getView ( int position, View convertView, ViewGroup parent){
ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.act_layout, null);
holder = new ViewHolder();
holder.slide.advertisement.imagead1 = (ImageView)convertView.findViewById(R.id.imageAdOneInAdvertisementLayout);
holder.slide.blog.image = (ImageView)convertView.findViewById(R.id.imageAdTwoInAdvertismentLayout);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
holder.imagead1.setImageResource(customVariableDisplay.get(position).getImageViewAd1());
holder.imagead2.setImageResource(customVariableDisplay.get(position).getImageViewAd2());
}
return convertView;
}
else{
//Throw a final View exception for unprecedented errors!!
}
}
}`
I am stuck with what to a way to ask Java what class is it inside the if statements. // If this slide comprises the class blog... etc. ANY HELP APPRECIATED! THANKS!
You can declare one variable slideType in your adapter and pass it to your adapter constructor and based on that value inflate your layout in onBindViewHolder(), onCreateViewHolder()
int slideType;
public CustomListAdapter(Context context, ArrayList<slide>customVariableDisplay, int slideType) {
this.slideType = slideType;
}
and also define three separate method in your adapter to pass your list so that you can bind your data.
Does this ListView show a mix of video, blog etc.. if yes then you need use below checking.
if(null != slide.video){
// add code for video
}
else if(null != slide.blog){
// add code for blog
}
else if (null != slide.advertisement){
// add code for advertisement
}
Also you need to set null for blog and advertisement in slide object or don't initialize them in case the item you want to show is video
I have a listview item with a label and two buttons. I tried to change the label on button click. But it's changing text on another listview item. Not the label with button. I did this using a custom list adapter. I tried it like following,
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final MenuItem listItem = objects.get(position);
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) this.context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.menu_list_item, null);
btnPlus = (ButtonRectangle) convertView.findViewById(R.id.buttonPlus);
btnPlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int i = 0;
cartQtyTextView.setText("" + ++i);
}
});
}
}
How may I fix this?
You might want to create holder.
I didn't include your menu item code due to i didn't see you got use it.
public class Holder {
ButtonRectangle buttonPlus;
TextView cartQtyTextView;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get Holder
final Holder holder = new Holder();
// Change Layout
LayoutInflater inflater = (LayoutInflater) this.context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.menu_list_item, null);
// Find Control
holder.buttonPlus = (ButtonRectangle)view.findViewById(R.id.buttonPlus);
holder.cartQtyTextView = (TextView)view.findViewById(R.id.cartQtyTextView);
// Check & Set
if (holder.buttonPlus != null) {
holder.buttonPlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int i = 0;
if (holder.cartQtyTextView != null) {
holder.cartQtyTextView.setText("" + ++i);
}
}
});
}
return view;
}
This is because you are setting the listener inside the if(convertView==null).
So the listener is only set once a view is created, but when you scroll, the list view is reusing a hidden item, but it keep the first assigned listener since the convertView is not null.
You need to set your onClickListener outside the if. And it is better if you use a holder for better performances
public class ExampleAdapter extends ArrayAdapter<MenuItem> {
private Activity activity;
private int resource;
private List<MenuItem> objects;
public ExampleAdapter(Activity activity, int resource, List<MenuItem> objects) {
super(activity, resource, objects);
this.activity = activity;
this.resource = resource;
this.objects= objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
MenuItem listItem = objects.get(position);
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
LayoutInflater li = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = li.inflate(resource, parent, false);
holder.labelTextView= (TextView) convertView.findViewById(R.id.labelTextView);
holder.btnPlus= (ButtonRectangle) convertView.findViewById(R.id.buttonPlus);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.btnPlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int i = 0;
holder.labelTextView.setText("" + ++i);
}
});
return convertView;
}
static class ViewHolder {
TextView labelTextView;
ButtonRectangle btnPlus;
}
}
I making a gridview that is made of a text and under it an image and everything is working fine but the problem is that when I scroll, the images gets repeated, so any tips on how I could solve this problem
here is my adapter:
public class CustomGridAdapter extends BaseAdapter {
private Context context;
private final String[] mobileValues;
public CustomGridAdapter(Context context, String[] mobileValues) {
this.context = context;
this.mobileValues = mobileValues;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
if (convertView == null) {
gridView = new View(context);
// get layout from mobile.xml
gridView = inflater.inflate(R.layout.heartless_gridview_design, null);
// set value into textview
TextView textView = (TextView) gridView
.findViewById(R.id.heartless_name);
textView.setText(mobileValues[position]);
// set image based on selected text
ImageView imageView = (ImageView) gridView
.findViewById(R.id.heartless_image);
String mobile = mobileValues[position];
if (mobile.equals("Shadow")) {
imageView.setImageResource(R.drawable.shadow);
} else if (mobile.equals("Soldier")) {
imageView.setImageResource(R.drawable.soldier);
} else if (mobile.equals("Large Body")) {
imageView.setImageResource(R.drawable.large_body);
} else if (mobile.equals("Silver Rock")) {
imageView.setImageResource(R.drawable.silver_rock);
} else if (mobile.equals("Emerald Blues")) {
imageView.setImageResource(R.drawable.emerald_blues);
} else {
}
} else {
gridView = (View) convertView;
}
return gridView;
}
#Override
public int getCount() {
return mobileValues.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
}
please if you know how to fix this please tell me how.
so far I understood you should use viewholder pattern and
every time when getView method calls check if viewholder is null initialize it otherwise do nothing just return view..
http://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder
and good example here - http://java.dzone.com/articles/android-listview-optimizations
I'm attempting to create a Gridview of icons with the Imageview set to one of two images like in my example below:
I'm retrieving two values from the sqlite content provider, one for the number of stamps and the other for the max number of stamps.
I use a custom adapter with a view holder pattern so I attempt to set the model onLoadFinished() in the Fragment. The model I've currently tried is an int[] with either value of 0 or 1 in the values to determine if green stamp is used or gray. This isn't working however as nothing is being displayed in the GridView.
Here's the code snippet from onLoadFinished() from the Fragment:
imageSourceModel = new int[data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_MAX_POINTS))];
int i = data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_POINTS));
for (int j = 0; j < imageSourceModel.length; j++, i--) {
if (i > 0) {
imageSourceModel[j] = 1;
} else {
imageSourceModel[j] = 0;
}
}
stampGridAdapter.setModel(imageSourceModel);
and the code from the custom Adapter:
#Override
public View getView(int i, View view, ViewGroup parent) {
ViewHolder viewHolder;
if (view == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.list_item_discover, parent, false);
viewHolder = new ViewHolder(view);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
if (model[i] > 0) {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp);
} else {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp_disabled);
}
return view;
}
And simple ViewHolder:
private class ViewHolder {
public ImageView stampView;
public ViewHolder(View view) {
stampView = (ImageView) view.findViewById(R.id.grid_stamp_image);
}
}
If I had to guess, I would say that it fails because of the creation order such that getView() is completed before onLoadFinished() can pass the model in. I'm just not sure how to work around this or what other methods I could use. Any help is appreciated.
For what you want to achieve, this seems unnecessary complicated to me, especially the double variable loop. To cut it simple I'd just do this:
in onLoadFinished()
int maxPoints = data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_MAX_POINTS));
int currentPoints = data.getInt(data.getColumnIndex(RewardsEntry.COLUMN_REW_POINTS));
mGridView.setAdapter(new RewardPointAdapter(getActivity(), currentPoints, maxPoints);
your adapter could look like this:
public class RewardPointAdapter extends BaseAdapter {
private LayoutInflater mLayoutInflater;
private int mMaxRewardPoints;
private int mCurrentRewardPoints;
public RewardPointAdapter(Context context, int currentRewardPoints, int maxRewardPoints) {
mLayoutInflater = LayoutInflater.from(context);
mMaxRewardPoints = maxRewardPoints;
mCurrentRewardPoints = currentRewardPoints;
}
#Override
public int getCount() {
return mMaxRewardPoints;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.list_item_discover, parent, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (position < mCurrentRewardPoints) {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp);
} else {
viewHolder.stampView.setImageResource(R.drawable.ic_approved_stamp_disabled);
}
return convertView;
}
private class ViewHolder {
public ImageView stampView;
public ViewHolder(View view) {
stampView = (ImageView) view.findViewById(R.id.grid_stamp_image);
}
}
}