I'm facing a very strange phenomenon. I'm using a ViewPager from the compatibility package to display profiles.
Every profile is just a ListView with custom elements in it. Every element has two states:
There is data - display it
There's no data - display place holder
Every time I swipe between profiles I reset the data within the item objects. When I remove the REST-Calls and just let the profile item empty - which should display the placeholder - all items keep blank.
What could be the problem?
EDIT:/
That's how I set the current item in the listview. The PageViewAdapter seems to be OK, if I use static content everything works perfect.
final TextView lblTitle = (TextView)convertView.findViewById(R.id.lblTitle);
final LinearLayout llContent = (LinearLayout)convertView.findViewById(R.id.llContent);
final ProfileItem item = getItem(userPosition);
String title = item.getTitle();
if(title == null || title.equals("")) {
lblTitle.setVisibility(View.GONE);
} else {
lblTitle.setText(item.getTitle());
}
if(item.getContent().getParent() != null) {
((ViewGroup)item.getContent().getParent()).removeAllViews();
}
llContent.removeAllViews();
llContent.addView(item.getContent());
The Items look all very similar like that:
#Override
public View getContent() {
if(isTextEmpty()) {
return noTextTherePlaceholder;
} else {
return view;
}
}
The effect keeps the same even if I return just view or noTextTherePlaceholder.
If I would instate new views like a TextView in the getView method everything works as expected.
With view pager you have to get all your data in your Activity.onCreate and then use that data in your ViewPagers adapter's onInstantiateItem.
Related
I've four tabs and four fragments (each one for each tab).
Each fragment has a vertical recycler view. Since all fragments view look similar I'm re-using the same layout file, same recycler view items and same adapter.
The issue is that only one item is loaded under the first tab and third tab and fourth tab, While the second tab successfully loads the entire data.
I hope image added below gives better understanding regarding the issue.
Here is my adapter code
public class OthersAdapter extends RecyclerView.Adapter<OthersAdapter.OthersViewHolder> {
private final Context context;
private final ArrayList<LocalDealsDataFields> othersDataArray;
private LayoutInflater layoutInflater;
public OthersAdapter(Context context, ArrayList<LocalDealsDataFields> othersDataArray) {
this.context = context;
this.othersDataArray = othersDataArray;
if (this.context != null) {
layoutInflater = LayoutInflater.from(this.context);
}
}
class OthersViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView othersSmallTitleTextView;
ImageView othersImageView;
OthersViewHolder(View itemView) {
super(itemView);
othersSmallTitleTextView = (TextView) itemView.findViewById(R.id.others_small_title);
othersImageView = (ImageView) itemView.findViewById(R.id.others_image);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
Intent couponDetailsItem = new Intent(context, LocalDealsActivity.class);
Bundle extras = new Bundle();
extras.putString(Constants.SECTION_NAME, context.getString(R.string.local_deals_section_title));
// Add the offer id to the extras. This will be used to retrieve the coupon details
// in the next activity
extras.putInt(Constants.COUPONS_OFFER_ID, othersDataArray.get(
getAdapterPosition()).getLocalDealId());
couponDetailsItem.putExtras(extras);
context.startActivity(couponDetailsItem);
}
}
#Override
public OthersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.others_items, parent, false);
return new OthersViewHolder(view);
}
#Override
public void onBindViewHolder(OthersViewHolder holder, int position) {
String lfImage = othersDataArray.get(position).getLocalDealImage();
String lfCategoryName = othersDataArray.get(position).getLocalDealSecondTitle();
if (lfCategoryName != null) {
// Set the second title
holder.othersSmallTitleTextView.setText(lfCategoryName);
}
if (lfImage != null) {
if (!lfImage.isEmpty()) {
// Get the Uri
Uri lfUriImage = Uri.parse(lfImage);
// Load the Image
Picasso.with(context).load(lfUriImage).into(holder.othersImageView);
}
}
}
#Override
public int getItemCount() {
return othersDataArray.size();
}
}
I like to point out couple of things -
I've checked other answers on Stack Overflow. They talk about setting the recycler view layout_height to wrap_content. This isn't the issue as the layout_height is already wrap_content and also the second tab loads all the data as expected.
And some others answers mentioned to used same versions for all support libraries and I'm already using 25.1.0 version for all the support libraries.
Size of the data array is 20 and returning 20 from the adapter's getItemCount() method.
The data array has the expected number of items in it and they are not null or empty.
Clean build, invalidate/caches doesn't work either.
Finally, I'm using FragmentStatePagerAdapter to load the fragments when the tabs are in focus.
EDIT:
This is how I'm parsing the JSON data received
private void parseLocalDeals(String stringResponse) throws JSONException {
JSONArray localJSONArray = new JSONArray(stringResponse);
// If the array length is less than 10 then display to the end of the JSON data or else
// display 10 items.
int localArrayLength = localJSONArray.length() <= 20 ? localJSONArray.length() : 20;
for (int i = 0; i < localArrayLength; i++) {
// Initialize Temporary variables
int localProductId = 0;
String localSecondTitle = null;
String localImageUrlString = null;
JSONObject localJSONObject = localJSONArray.getJSONObject(i);
if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_ID)) {
localProductId = localJSONObject.getInt(JSONKeys.KEY_LOCAL_DEAL_ID);
}
if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_CATEGORY)) {
localSecondTitle = localJSONObject.getString(JSONKeys.KEY_LOCAL_DEAL_CATEGORY);
}
if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_IMAGE)) {
localImageUrlString = localJSONObject.getString(JSONKeys.KEY_LOCAL_DEAL_IMAGE);
}
if (localImageUrlString != null) {
if (!localImageUrlString.isEmpty()) {
// Remove the dots at the start of the Product Image String
while (localImageUrlString.charAt(0) == '.') {
localImageUrlString = localImageUrlString.replaceFirst(".", "");
}
// Replace the spaces in the url with %20 (useful if there is any)
localImageUrlString = localImageUrlString.replaceAll(" ", "%20");
}
}
LocalDealsDataFields localDealsData = new LocalDealsDataFields();
localDealsData.setLocalDealId(localProductId);
localDealsData.setLocalDealSecondTitle(localSecondTitle);
localDealsData.setLocalDealImage(localImageUrlString);
localDealsDataArray.add(localDealsData);
}
// Initialize the Local Deals List only once and notify the adapter that data set has changed
// from second time. If you initializeRV the localDealsRVAdapter at an early instance and only
// use the notifyDataSetChanged method here then the adapter doesn't update the data. This is
// because the adapter won't update items if the number of previously populated items is zero.
if (localDealsCount == 0) {
if (localArrayLength != 0) {
// Populate the Local Deals list
// Specify an adapter
localDealsRVAdapter = new OthersAdapter(context, localDealsDataArray);
localDealsRecyclerView.setAdapter(localDealsRVAdapter);
} else {
// localArrayLength is 0; which means there are no rv elements to show.
// So, remove the layout
contentMain.setVisibility(View.GONE);
// Show no results layout
showNoResultsIfNoData(localArrayLength);
}
} else {
// Notify the adapter that data set has changed
localDealsRVAdapter.notifyDataSetChanged();
}
// Increase the count since parsing the first set of results are returned
localDealsCount = localDealsCount + 20;
// Remove the progress bar and show the content
prcVisibility.success();
}
parseLocalDeals method is inside a helper class and it is called by using initializeHotels.initializeRV();
initializeRV() initializes the Recycler view, makes a network call to the server and the received data is passed to the parseLocalDeals method. initializeHotels being an instance variable of the Helper class.
EDIT 2:
For those who wants to explore the code in detail, I've moved the part of the code to another project and shared it on Github. Here is the link https://github.com/gSrikar/TabLayout and to understand the hierarchy check out the README file.
Can anyone tell me what I'm missing?
Not much of an answer but too long for a comment.
I have duplicated (almost) your adapter code and it fully works for me. I believe I have done the same as you. I'm using the same layout file, the same item & same adapter for all tabs. I think there are no problems with your adapter code.
I say 'almost' because I had to change a couple of things since I don't have access to your data. I changed your LocalDealsDataField model to include a BitmapDrawable & I changed onBindViewHolder() to handle it.
BitmapDrawable lfImage = othersDataArray.get(position).getLocalDealImage();
holder.othersImageView.setBackground(lfImage);
Since there seems to be no problem with your adapter, I would focus on getting the data or setting up the adapter as your problem. Sorry I can't be of help beyond that.
FYI, here's how I setup the adapter in onCreateView()
rootView = inflater.inflate(R.layout.recycler_view, container, false);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mAdapter = new OthersAdapter(this.getContext(), list);
mRecyclerView.setAdapter(mAdapter);
Summary
Solved the layout issue at point 1 replacing a LinearLayout by a RelativeLayout, inverting visibility logic to avoid ghost effect and catching exceptions and preventing them when the related view is not found.
Added point 2 to demonstrate that the visual defect is only present on Marshmallow and Nougat devices.
Finally FragmentStatePagerAdapter loads pages before getting focus so a fix is proposed at point 3 (load all pages and update them when are selected).
Further information in the comments below and #d4h answer.
The fourth page is not using the same layout, only the same RecyclerView and id, perhaps a work in progress. The layout issue can be solved using the same layout that previous pages but I consider this change out of scope.
1. Partially fixed for Marshmallow and Nougat devices. Work in progress.
Update2 Changing LinearLayout by RelativeLayout and inverting visibility logic solves layout issue:
Update: Commenting initializeTrending in all the fragment initializations also works onApi23+
I'll check it later, seems as deals are correctly loaded but then trending is loaded and deals are lost. WIP here.
If trending array empty and trending view gone, deals are not shown, but using invisible are shown
2. You are loading a wrong page on Marshmallow and Nougat devices
FragmentStatePagerAdapter first call to getItem() wrong on Nougat devices
This ended up having nothing to do with the FragmentStatePagerAdapter
code. Rather, in my fragment, I grabbed a stored object from an array
using the string ("id") that I passed to the fragment in init. If I
grabbed that stored object by passing in the position of the object in
the array, there was no problem. Only occurs in devices with Android 7.
FragmentStatePagerAdapter - getItem
A FragmentStatePager adapter will load the current page, and one page
either side. That is why it logs 0 and 1 at the same time. When you
switch to page 2, it will load page 3 and keep page 1 in memory. Then
when you get to page 4 it will not load anything, as 4 was loaded when
you scrolled to 3 and there is nothing beyond that. So the int that
you're being given in getItem() is NOT the page that is currently
being viewed, is the one being loaded into memory. Hope that clears
things up for you
These comments are confirmed in this branch and commit
All pages load correctly on Lollipop emulator, the last page has an extra issue, see OthersFragment:
3. Initialize all pages at creation and update them on selection.
Increase OffScreenPageLimit so all pages are initialised
Add on page selected/unselected/reselected listener
These changes solve the issue commented below:
/**
* Implement the tab layout and view pager
*/
private void useSlidingTabViewPager() {
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
BottomSectionsPagerAdapter mBottomSectionsPagerAdapter = new BottomSectionsPagerAdapter(getChildFragmentManager());
// Set up the ViewPager with the sections adapter.
ViewPager mBottomViewPager = (ViewPager) rootView.findViewById(R.id.local_bottom_pager);
mBottomViewPager.setOffscreenPageLimit(mBottomSectionsPagerAdapter.getCount());
mBottomViewPager.setAdapter(mBottomSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) rootView.findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(mBottomViewPager);
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
/**
* Called when a tab enters the selected state.
*
* #param tab The tab that was selected
*/
#Override
public void onTabSelected(TabLayout.Tab tab) {
// TODO: update the selected page here
Log.i(LOG_TAG, "page " + tab.getPosition() + " selected.");
}
/**
* Called when a tab exits the selected state.
*
* #param tab The tab that was unselected
*/
#Override
public void onTabUnselected(TabLayout.Tab tab) {
// Do nothing
Log.i(LOG_TAG, "Page " + tab.getPosition() + " unselected and ");
}
/**
* Called when a tab that is already selected is chosen again by the user. Some applications
* may use this action to return to the top level of a category.
*
* #param tab The tab that was reselected.
*/
#Override
public void onTabReselected(TabLayout.Tab tab) {
// Do nothing
Log.i(LOG_TAG, "Page " + tab.getPosition() + " reselected.");
}
});
}
Previous Comments:
Check your LocalFragment getItem() method using breakpoints.
If you select one page, next page is also initialized, and you are sharing the recyclerView, etc.
I would move the initialization outside of getItem() as suggested here:
ViewPager is default to load the next page(Fragment) which you can't
change by setOffscreenPageLimit(0). But you can do something to hack.
You can implement onPageSelected function in Activity containing the
ViewPager. In the next Fragment(which you don't want to load), you
write a function let's say showViewContent() where you put in all
resource consuming init code and do nothing before onResume() method.
Then call showViewContent() function inside onPageSelected. Hope this
will help
Read these related questions (the first has possible workarounds to hack the limit to zero):
ViewPager.setOffscreenPageLimit(0) doesn't work as expected
Does ViewPager require a minimum of 1 offscreen pages?
Yes. If I am
reading the source code correctly, you should be getting a warning
about this in LogCat, something like:
Requested offscreen page limit 0 too small; defaulting to 1
viewPager.setOffscreenPageLimit(couponsPagerAdapter.getCount());
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
+ DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
I have looked at your code, problem is same as explained by #ardock
Solution i would like to propose,
You have to change your code at 3 place ::
Inside all Fragment You are using in ViewPager Don't call initializeRESPECTIVEView() from onCreateView method.
Inside LocalFragment make a list of Fragments you are going to use with ViewPager and pass it to BottomSectionsPagerAdapter. and return Fragment from that list from getItem(int position) of BottomSectionsPagerAdapter.
Add Following code to LocalFragment inside useSlidingTabViewPager().
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
//Call Respective fragment initializeRESPECTIVEView() method from onTabSelected , you can get fragment instance from list you passed to BottomSectionsPagerAdapter
In my app I am using recycler view.I want to show and hide view on particular condition.But when I scroll recycler views I am not getting expected behaviour.When I Visible a view it gets visible for other rows as well randomly.
What I understand is when it recycles it reuses view and when previous view when it gets recycled it finds the visibility of that view.How can I hide view on particular condition? here is my adapter code
#Override
public void onBindViewHolder(UrduRhymesViewHolder holder, int position) {
RhymesModel current = mUrduRhymesList.get(position);
AppUtility.setCustomFont(mContext, holder.tvUrduRhymesName, Constants.HANDLEE_REGULAR);
holder.tvUrduRhymesName.setText(current.getRhymeName());
holder.ivUrduRhymesLogo.setImageUrl(current.getThumbnailUrl(), mImageRequest);
int status = AppUtility.getFavouriteStatus(mContext, current.getRhymeName(), new UrduRhymesDb(mContext));
if (status == 0)
holder.btnFavourite.setBackgroundResource(R.mipmap.btn_star_unactive);
else
holder.btnFavourite.setBackgroundResource(R.mipmap.btn_star);
ProgressbarDetails progressbarDetails = ProgressbarDetails.getProgressDetail(current.getRhymeName());
if (progressbarDetails == null) {
progressbarDetails = new ProgressbarDetails();
progressbarDetails.prgProgressBar = holder.pbRhymeDownload;
progressbarDetails.download_btn_settings = holder.downloadButtonLayout;
} else {
progressbarDetails.prgProgressBar = holder.pbRhymeDownload;
progressbarDetails.download_btn_settings = holder.downloadButtonLayout;
holder.pbRhymeDownload.setProgress(progressbarDetails.progress);
}
ProgressbarDetails.addUpdateProgressDetail(current.getRhymeName(), progressbarDetails);
if (progressbarDetails != null && progressbarDetails.isDownloading) {
Log.e("test","downloading foe position "+position );
holder.downloadButtonLayout.setBackgroundResource(R.mipmap.btn_download);
holder.pbRhymeDownload.setVisibility(View.VISIBLE);
holder.pbRhymeDownload.setProgress(progressbarDetails.progress);
} else {
Log.e("test","should not be visible for position "+position);
holder.pbRhymeDownload.setVisibility(View.GONE);
}
}
Here progressbarDetails.isDownloading (having value true) is the criteria when I want to show my view but is else clause it is not hiding my view
Edit: Here ProgressbarDetails (Singleton )is a class keeping reference of every row of adapter's progress bar.
No direct way of hiding and unhiding recylerview childitems.
Solution:
Let us assume that the recyclerview adapter is ArrayList
Now make another arraylist (temp_list)
Scenarios:
Hide: iterate through your adapter items and remove the ones that you want to hide. Put each of these into temp_list. After iteration is over, call notifyDataSetChanged()
Show: iterate through your temp_list items and remove the ones that you want to show. Put each of these into adapter. After iteration is over, call notifyDataSetChanged()
You should add a flag in your viewHolder that indicates if this view should be displayed or not . and check this flag every time in the onBindViewHolder.
because the recyclerView reuses the same views you should make a decision depending on something special for every view in you viewHolder.
do u mean when your data has been changed?
and your layout want to change ?
adapter.notifyDataSetChanged();
This is my first time to ask a question here. I will to explain clearly what problem is.
I am creating an Android app that displays questions with choices/options in a feed. I used RecyclerView and CardView to display them. I was able to display the questions and other stuff but I am having difficulties display the options. I am trying to loop the array of options/choices inside the onBindViewHolder(). I can see in the log that the data is there but I can't/ I do not know how to add the views for the choices dynamically.
I already have the loop but I am not certain how I can I inflate or display the textviews. This is my onBindViewHolder() code:
public void onBindViewHolder(PublicQuestionViewHolder publicQuestionViewHolder, int i) {
publicQuestionViewHolder.questionAuthor.setText(questions.get(i).getQuestionAuthor());
publicQuestionViewHolder.questionText.setText(questions.get(i).getQuestionText());
ParseUser currentUser = ParseUser.getCurrentUser();
if (currentUser == questions.get(i).getQuestionParseUser()) {
publicQuestionViewHolder.questionEditIconImageView.setVisibility(View.VISIBLE);
} else {
publicQuestionViewHolder.questionEditIconImageView.setVisibility(View.INVISIBLE);
}
//Log.d("Options", String.valueOf(questions.get(i).getQuestionOptions().size()));
if (questions.get(i).getQuestionOptions().size() != 0) {
for (QuestionOption option : questions.get(i).getQuestionOptions()) {
if (option.getQuestionOptionType().equals("T")) {
Log.d("Option", option.getQuestionOptionText());
} else if (option.getQuestionOptionType().equals("I")) {
Log.d("Option", option.getQuestionOptionImage().getName());
}
}
}
}
First get the activity context in your adapter constructor and save it.
Then just inflate that view inside onBindViewHolder like this -
View newView = LayoutInflater.from(context).inflate(R.layout.view_name, null);
Add that view to whichever parent you like, for eg. -
publicQuestionViewHolder.parentName.addView(newView);
Then get reference to its children like this -
TextView name = (TextView) newView.findViewById(R.id.name);
I have searched these forums for nearly 3 hours and seen several similar questions but none of the answers works for me.
I have a single Activity, with several card views. One of the card views has a Spinner with string values and a very simple ListView. The user selects a value from the Spinner, between 1 and 12. The ListView should then display a number of strings equal to the value selected, based on the position in the spinner list. For example, user selects 2nd item in spinner list and the ListView displays 2 strings. I have a custom adapter on the listview. The ListView itself initially displays a single row, which is correct. However, after the user selects a value from the spinner, the listview is not displaying the extra rows, it still only displays one row. The data for the ListView comes from an ArrayList. I have checked the data model of the adapter after the user selects a value and it has the correct number of entries, as does the ArrayList itself, yet no matter what I try the ListView itself still only display the first row. I have tried NotifyDataSetChanged and every variation of Invalidate without success.
The various code samples:
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (parent == spDoseFrequency){
Toast.makeText(this,String.valueOf(position),Toast.LENGTH_SHORT).show();
rebuildReminderTimesList(position + 1);
}
}
private void rebuildReminderTimesList(int numberOfTimes){
Toast.makeText(this,"yup",Toast.LENGTH_SHORT).show();
//reset selected item to position 1
myApp.iSelectedReminderTimeIndex = 0;
//clear array and list, then rebuild with hourly timeslots
iarrTimes = new int[numberOfTimes][2];
liReminderTimes.clear();
int startTime = 8;
for (int i = 0; i < numberOfTimes; i++){
iarrTimes[i][0] = startTime + i;
iarrTimes[i][1] = 0;
liReminderTimes.add(pad(startTime + i) + ":00");
}
//refresh the listview
myAdapter.notifyDataSetChanged();
}
public class ReminderListAdapter extends ArrayAdapter {
List<String> liTimes;
Context ctx;
LayoutInflater inf;
public ReminderListAdapter(Context ctx, List<String> liTimes) {
super(ctx, R.layout.reminder_time_listview, liTimes);
this.liTimes = liTimes;
this.ctx = ctx;
inf = LayoutInflater.from(ctx);
}
public void setLiTimes(List<String> liTimes){
this.liTimes = liTimes;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder viewHolder;
if (view == null){
view = inf.inflate(R.layout.reminder_time_listview,parent,false);
viewHolder = new ViewHolder();
viewHolder.sTime = (TextView) view.findViewById(R.id.tvTime);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.sTime.setText(liTimes.get(position));
return view;
}
private static class ViewHolder{
TextView sTime;
}
}
Any help would be appreciated as this is driving me crazy.
Quick update to this question: I have just tested supplying the initial list more than one value but even then it only displays the first item. Is there perhaps a problem with using ListView inside a CardView object? All my other cards work fine, only the ListView one fails to display properly.
Also, I have tried amending the code so that instead of changing the number of elements in the list, it just changes the text in the string of the first element and this works fine. So the notifyDataSetChanged appears to be working, but it just won't display more than one item. A quick check of the Adapter.getCount() method also gives the correct number of elements back, but won't display them.
A lot of folks forget to do the notifyDataSetChanged() call, but you've got that. Are you using a custom adapter? If so, that makes this sound like an issue with one or more of the adapter's methods. In particular, it sounds like getCount or getView might not be returning what they should be. That could either be because of a flawed logic issue, the underlying data source isn't being updated correctly, or the underlying data source isn't the same object you think it is. Without seeing your adapter though, it's hard to diagnose.
I found the problem. I had several CardView objects inside a LinearLayout, which itself was inside a ScrollView. As soon as I removed the ScrollView, the List inside the Card displayed properly. This has unfortunately created another problem in that I can no longer scroll the page to see later cards, which I have not yet found a solution for, but that is a different topic.
I create a dialog and populate it with a listview that uses a custom list adapter. It works fine, but I've noticed that when the list is long enough to scroll, doing so back and forth will cause some of my list items to randomly lose some of their data. I've noticed it is always the same list items too. For instance, each list item will have a title, image, and date on it. The dates seem to vanish on some when I scroll. They are always there when I start the dialog, and they always vanish once I scroll.
The weird thing is that my list row consists of a few TextViews in 2 rows and its only the bottom row TextViews that dissapear...Any ideas?
Code for my dialog
itemSendPickerDialog = new Dialog(this);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Select Item to Send");
ListView lv = new ListView(this);
Cursor c = mDbHelper.fetchItemsByDate(id);
c.moveToFirst();
int i = R.layout.send_item_menu_row;
MyListAdapter ia = new MyListAdapter(this, mainListBackground, c, true);
lv.setAdapter(ia);
builder.setView(lv);
itemSendPickerDialog = builder.create();
itemSendPickerDialog.show();
And my custom list adapter class:
class MyListAdapter extends ResourceCursorAdapter {
public MyListAdapter(Context context, int i, Cursor cursor, boolean...sending) {
super(context, i, cursor);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView title = (TextView) view.findViewById(R.id.item_name);
title.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_TITLE)));
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getWidth();
width = width - 150;
ViewGroup.LayoutParams params = title.getLayoutParams();
params.width = width;
title.setLayoutParams(params);
String cat = cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_CATEGORY));
if (cat.equalsIgnoreCase("trip notes")) {
LinearLayout ll = (LinearLayout) view.findViewById(R.id.item_datetime_holder);
ll.setVisibility(View.INVISIBLE);
}
TextView date = (TextView) view.findViewById(R.id.item_date);
date.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_DEP_DATE)));
TextView time = (TextView) view.findViewById(R.id.item_time);
time.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_DEP_TIME)));
ImageView iv = (ImageView) view.findViewById(R.id.image_icon);
if (iv != null) {
int index = cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_TYPE);
String type = cursor.getString(index);
if (type != null) {
} else {
type = "notes";
}
iv.setImageResource(getTypeResource(type));
}
}
}
I have faced this problem too...
The problem you are facing is due to the recycling of views by the LIstView when you scroll up/down. In your case, the ListView is giving you those recycled views , whose properties you have changed by making them invisible. Some possible solutions could be:
1) When cat.equalsIgnoreCase("trip notes")) is becoming true, you are making some views invisible. This invisible view is then recycled and given back. The recycled view is still invisible (when it is given back to you), so what you can do is make this invisible view visible in the beginning of your ListAdapter every time .
You can put this code at the beginning of bindView method where you make the layout visible first and then proceed with rest of the logic.( In short, the dates from your display are not vanishing but are just invisible).
2) Override getViewTypeCount() in you adapter . From your codesnippet, it looks like you have two types of rows( one in which R.id.item_datetime_holder is invisible and other in which it is visible) , so return 2 from this method( please do some trial and error ) . This should take care of the problem.
public int getViewTypeCount() {
return 2;
}
You will find an excellent explanation at this link http://logc.at/2011/10/10/handling-listviews-with-multiple-row-types/
3) You can inflate completely different layouts depending on your if condition. But the effeciency would be a little less.
I had a similar problem, when scrolling a list, only the items after the window height decided to get their data repeating from index 0 - so if the last visible item was 8, the next would be 0 again.
So you could try to check if the index of the view is correct, maybe this method of ListView would help
lv.getPositionForView(view);
I've figured out that the code that is doing this is in my custom list adapter class
String cat = cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_CATEGORY));
if (cat.equalsIgnoreCase("trip notes")) {
LinearLayout ll = (LinearLayout) view.findViewById(R.id.item_datetime_holder);
ll.setVisibility(View.INVISIBLE);
}
I hide some of the layout items depending on what the category is, and for some reason when putting a list view in an AlertDialog builder it appears to mix up the items. I don't know exactly how I am going to fix it, but for now if I just remove the invisibility functionality, my data won't go away.
I would use two different layouts, depending on the "type" of list item. It looks like you are switching the "type" based on the cat string containing "trip notes". If it contains it, then you would have a layout that is the same as you have now, but without the item_datetime_holder view. If it doesn't contain it, then you would use the same layout as you're using now (send_item_menu_row).
Here is a good tutorial on how to use multiple layouts in a ListView: http://android.amberfog.com/?p=296
By the way, I think that the reason why some of your rows are not drawing correctly is due to view reuse by the ListView. Utilizing the multiple layouts like I've mentioned above should fix the problem since you won't be changing the visibility of views, but just utilizing two different views to render, depending on what type of list item you're rendering.