Listview custom ListDivider - android

I'm actually creating a ListView by populating items from the database.
When the end of the listview is reached I populate a few more items. Now, I want the dividers of the ListView based on the value returned from the database. If two consecutive values of the database are same I want them to be seperated by a thinline and if not a thick line.
I tried setting them through the adapter like this
if (convertView == null) {
holder = new ViewHolder();
if (eventSource.get(position).equalsIgnoreCase("asdsadas")
&& eventSource.get(position + 1).equalsIgnoreCase(
"fdgdfgfd")
|| eventSource.get(position).equalsIgnoreCase(
"dfgdfgdfg")
&& eventSource.get(position + 1).equalsIgnoreCase(
"jgghjhhgg")) {
convertView = mInflater.inflate(R.layout.list_adapter, null);
} else {
convertView = mInflater.inflate(R.layout.list_adapterthinline,
null);
}
I'm inflating a new layout based on the condition. It works for the first time, but after I scroll down and come up the view changes. It gets all mixed up.
I tried setting the divider height in the Activity too, like this and I call the 'setdivider' method in onCreate and in onScroll listener too.
public void setdivider() {
// TODO Auto-generated method stub
for (int i = 0; i < listSource.size() - 1; i++) {
if (!listSource.get(i).equalsIgnoreCase(
listSource.get(i + 1))) {
Log.v("inside not equals", "become smalllllllllllllllll");
list.setDivider(red);
list.setDividerHeight(5);
} else if (listSource.get(i).equalsIgnoreCase(
listSource.get(i + 1))) {
Log.v("inside equals", "become bigggggggggggg");
list.setDivider(blue);
list.setDividerHeight(10);
}
}
}
But here even though both the log comments are shown on the LogCat, only one divider is set in the list.
Please tell me where I'm going wrong, or do suggest some other approaches, if any.

ListView caches views, so when you scroll around, view are reused. This is why convertView isn't always null. However, since you have two different kinds of views, you need to tell that to the ListView so that the convertView you get back is the kind you want. You do that by implementing Adapter.getItemViewType() and Adapter.getViewTypeCount().
In your example, you would let getViewTypeCount return 2, and let getItemViewType return 1 if it's a divider, and 0 if it's not.

merge the layout R.layout.list_adapter and R.layout.list_adapterthinline
with one single layout.
and set the thinline gone by default. and set it visible when you needs
..... Your layout here.
when you needs the line.
convertView.findViewById(R.id.thin_line).setVisible(View.Visible)
When you don't need it.
convertView.findViewById(R.id.thin_line).setVisible(View.Gone)

Related

Change background color of ListView item after 3 seconds

I have a list of Items that are "seen" or "not seen" in ArrayList<Item>. If they're not seen I change the background color of the ListView item in my CustomArrayAdapter like this :
if(item.is_seen == null || item.is_seen == 0) {
row.setBackgroundResource(R.color.yellow);
} else {
row.setBackgroundResource(R.color.transparent);
}
Now what I want to do is set all items background to transparent after 3 seconds spent on the page.
I already tried to do something like this:
mScheduledExecutor.schedule(new Runnable() {
#Override
public void run() {
for(int i=0; i<mItems.size(); i++) {
final Item n = mItems.get(i);
if(n.is_seen == null || n.is_seen == 0) {
// update value in db
int isSeen = 1;
updateItem(n._id, isSeen);
// change the color of backgrounds
View view = listViewItem.getChildAt(i);
if(view != null) {
view.setBackgroundResource(R.color.red);
}
}
}
Updating the value in the DB works, but the rest doesn't. But I'd like to avoid to reload the data. I just need the color to change.
I don't have errors, it just does nothing.
I look everywhere for an answer and didn't find one.
Am I wrong since the beginning? Is what I want to achieve even possible?
I thank you in advance for all the help you could give me.
Instead of changing the color of the view like youre doing,
// change the color of backgrounds
View view = listViewItem.getChildAt(i);
if(view != null) {
view.setBackgroundResource(R.color.red);
}
(code above will not work, because the views are recycled in the ListAdapter) update the DATA off which you build your list - add a property to the class you are passing into your ListAdapter, then grab that instance from the list and update that property, you have the position at which it needs to be updated already, so that's easy. Then, call notifyDataSetChanged() on the list. It will not redraw the list if you didn't ADD/REMOVE items from list, but it will update correct view for you. This is the only way to do it - absolutely NO WAY to get to a view corresponding to a specific element in a list after list has been drawn already. Only way is to refresh/redraw the list with notifyDataSetChanged(), followed by refreshDrawableState().

How to add e.g. 5 elements to a 10 rows listview in android?

I am previously working on PHP and js , and recently I am working on android listview
However, I encountered a problem in creating a custom adapter for listview
public View getView(int arg0, View arg1, ViewGroup arg2) {
// TODO
if (arg1 == null) {
arg1 = myInflater.inflate(R.layout.grid, arg2, false);
}
TextView name = (TextView) arg1.findViewById(R.id.text1);
TextView desc = (TextView) arg1.findViewById(R.id.text2);
ImageView image = (ImageView) arg1.findViewById(R.id.image1);
if (arg0 < images.length) {
image.setImageResource(images[arg0]);
}
name.setText(names[arg0]);
desc.setText(description[arg0]);
return arg1;
}
The problem is I have 3 array of content to pass to the listview grid, for the first two array, there are 10 element and the last one have 5 only. So , it is out of boundries for the last one. I added a condition to check whether it exceed 5 , but args0 seems not increased according to the row?
if (arg0 < images.length) {
image.setImageResource(images[arg0]);
}
The first five row and some other rows also has image setted, why is that and how to fix this? thanks
In General
since you want to display Data to your list, plx create an Object that represents Data.
like you named in your comment above:
public class ListEntry {
String name = "";
String gender = "";//use enum here perhaps -.-
String photoUrl = null; //or use byte[] photo or whatever you've stored in your array before
// write getters/setters for your members
}
then you can use one array ListEntry[] (or List<ListEntry>) to access all data. this way you get around your indexOutOfBoundsException.
lookup any listadapter tutorials online, e.g. the one from Vogella
Why do more than the first five entries have an image?
Androids Adapters for Listviews implement a caching mechanism to reduce the inflating (performance/memory cost intensive) of new list-items (e.g. rows) to a minimum. therefore there are only as many rows (or little more) created as displayed by the list. since you only set images if there are any, but never remove already set images from rows, you result in some rows that replay images they shouldn't. these rows are cached from previously outscrolling rows.
therefore add something like
if (listItem.photo != null) {
image.setImageResource(images[arg0]);
} else {
image.setVisibility(View.GONE);
}
as reference for listviews and their caching mechanism see Romain Guy on ListViews
Edit Regarding usage of Listadapter
The getView(..) you posted above is inside your ListAdapter implementation, prefarrably you've extended an ArrayAdapter<T>. if so, your T should now state ListEntry and you have any line of code that states
MyArrayAdapter myAdapter = new MyArrayAdapter() or something like that.
now you have an array or List of ListEntry like List<ListEntry> myCollection = new ArrayList<ListEntry>() or ListEntry[] listEntries = new ListEntry[10] and use
myAdapter.addAll(listEntries);
to get an item of your list inside your getView(..) you can use:
ListEntry currentEntry = getItem(arg0);
and refer the single members of currentEntry to set them ;-)
What about
if (images[arg0] != null) image.setImageResource(images[arg0]);
?

notifyDataSetChanged() without refreshing the UI?

Is there a way to call notifyDataSetChanged() on a custom adapter without refreshing the list or disturbing the UI?
I have a ListView with a custom Adapter behind it, using a List of Guest objects as its dataset. When a Guest marks his attendance by tapping on his name, a tick is supposed to appear next to the guest's name in the UI. This I can do, but when I call notifyDataSetChanged(), the list of names is pushed all the way to the top, presumably because the list "refreshes".
If I don't call notifyDataSetChanged(), however, the tick disappears when I scroll past the updated entry and scroll back again. This is due to the ListView's "recycling" of Views as I understand, but it sure doesn't make my job any easier.
How would one call notifyDataSetChanged() without making the entire ListView refresh itself?
Better to have one boolean field in your Guest Class :isPresent.
whenever user taps on list item you can get the selected item using adapter.getItemAtPosition().
update the value isPresent to true. and make show the tick mark.
In your adapter class. check for isPresent value. If it is marked to true then show the tick mark else hide it.
This is how you can achieve the both. Show Tick Mark on ListItem click and if you scroll the listview and come back to the same item you tickmark show/hide will be taken care by Adapter.
you could retain the position like this
// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
// ...
// restore
mList.setSelectionFromTop(index, top);
Actually possible if you don't want to make a "selected item" here is the code
public void updateItem(ListView listView, Activity activity) {
if (mData == null) return;
DebugLog.i("A", "firstCell: " + listView.getFirstVisiblePosition() + " lastCell: " + listView.getLastVisiblePosition());
for (int firstCell = listView.getFirstVisiblePosition(); firstCell <= listView.getLastVisiblePosition(); firstCell++) {
final DataItem item = (DataItem) getItem(firstCell); // in this case I put the this method in the Adapter and call it from Activity where the adapter is global varialbe
View convertView = listView.getChildAt(firstCell);
if (convertView != null) {
final TextView titleTextView = (TextView) convertView.findViewById(R.id.item_title);
// here is the most important to do; you have to use Main UI thread to update the view that is why you need activity parameter in the method
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
titleTextView.setText( item + " updated");
}
});
}
}
}

ListView inside ViewPager - Items in ListView keep blank

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.

ListView getChildAt returning null for visible children

I'm getting some strange behavior from a listview/the getChildAt method.
I have a HashSet, iconsToUpdate, of icons that have been changed in the database. I want to iterate over the visible rows to see if any of their icons need to be updated to reflect the new icons. I don't need to test the icons not currently in view as they will be drawn properly when rendered.
My problem is that getChildAt is returning null when it seems like it shouldn't. I know that getChildAt can only return views that are currently visible, but it is returning null for some of the visible rows.
Here is my code that iterates over the visible rows:
Logger.debug("First visible index: " + f_listView.getFirstVisiblePosition());
Logger.debug("Last visible index: " + f_listView.getLastVisiblePosition());
for (int i = f_listView.getFirstVisiblePosition(); i <= f_listView.getLastVisiblePosition(); i++) {
String tag = "asdf"; // Remove when bug is fixed.
if (f_listView == null) {
Logger.debug("f_listView is null");
} else if (f_listView.getChildAt(i) == null) {
Logger.debug("Child at index " + i + " is null");
} else {
tag = (String) f_listView.getChildAt(i).getTag();
Logger.debug("Successful at index " + i + ", tag is: " + tag);
}
if (iconsToUpdate.contains(tag)) {
setIcon(i, f_aim.getInHouseIcon(tag));
}
}
Here is the log corresponding to a run of this loop:
D/...: First visible index: 3
D/...: Last visible index: 8
D/...: Successful at index 3, tag is: ...
D/...: Successful at index 4, tag is: ...
D/...: Successful at index 5, tag is: ...
D/...: Child at index 6 is null
D/...: Child at index 7 is null
D/...: Child at index 8 is null
It should be noted that the first and last visible indexes are being correctly reported, as I am viewing rows 3-8 when I run this. Rows 6, 7, 8 are being rendered properly. How are they being displayed if they are null?
Also, I do not know if this is important, but row 5 is the last visible row when I am at the top of the listview.
Any info as to why these rows are being returned as null would be greatly appreciated.
Thanks!
listView.getChildAt(i) works where 0 is the very first visible row and (n-1) is the last visible row (where n is the number of visible views you see).
The get last/first visible return the position in the dataAdapter you have. So you since you start at position 3, with what looks like 6 visible views, that's when get for positions 6-8 you get null.
In your example getChildAt(0) would return position 3. What I usually do is store the position on my views so I can lookup on my dataAdapter later if I need values.
I think your for loop should look like this:
for (int i = 0; i <= f_listView.getLastVisiblePosition() - f_listView.getFirstVisiblePosition(); i++)
Try
f_listView.getChildAt(positionOfChildYouWantGet - f_listView.getFirstVisiblePosition());
When you call listView1.getLastVisiblePosition() and listView1.getFirstVisiblePosition(), the listview returns the positions of the items that are partially visible in the listview. For example, the first item may be half visible and the last item may be half visible. In this case, even though you can see part of the item in the listview, the adapter has not yet called the getView() function for the item and therefore the item is still considered null.
In my case i have a listView and the first item is full visible but when I do getFirstVisiblePosition() the result is 1 !
It gets more weird when I scroll and make the first item half visible then my getView show that getFirstVisiblePosition() is 0.
this my code :
public View getView(final int position, View convertView, final ViewGroup parent) {
row = convertView;
final AtomPaymentHolder holder = new AtomPaymentHolder();
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
row.setTag(items.get(position));
holder.songitem = items.get(position);
final int firstPosition = MainActivity.listviewSong.getFirstVisiblePosition() - MainActivity.listviewSong.getHeaderViewsCount();
final int lastPosition = MainActivity.listviewSong.getLastVisiblePosition();
if(MusicService.indexService>= firstPosition && MusicService.indexService<= lastPosition){
MainActivity.listviewSong.getChildAt(MusicService.indexService-firstPosition).setBackgroundColor(getContext().getResources().getColor(R.color.pressed_color));
}
(when I selected the next item and scroll, it works good)
They are partially visible maybe. But when the getView() tries to recycle the views it gets FULLY visible ones other takes them as null. for example if you scroll the list and the top item is partially visible and the bottom is partially visible so these are nulls but you still see them.
I tried by ever mean in OnListItemClickListener(),but fails. At last I made some modification in my customized adapter for listview. Here in getView(),I apply clickListener on the item which i added into the list frequently. n do all required functionality there. Here is my Code, where i add Image View in the list n so apply listener on imageview.
I Think it will help those who want to change the color when specific List Item is >selected. Go For it..
In getView() of customized Adapter
//---------------------------------code------------------------------------------
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.icon_image_layout, parent, false);
ImageView imageView = (ImageView) rowView.findViewById(R.id.Icon_ImageView);
imageView.setClickable(true);
final int pos=position;
imageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
// TODO Auto-generated method stub
try{
if(previous_view!=null)
previous_view.setBackgroundColor(Color.WHITE);
}catch (Exception e) {
System.out.println("Exception Occurs Previous View");
}
v.setBackgroundColor(Color.RED);
MainActivity.imageView.setImageResource(MainActivity.Image_Name[pos]);
previous_view=v;
return false;
}
});
I know this is very old post. But I'm answering because people are still looking for a work around on ListView getChildAt() null pointer exception.
This is because the ArrayApdater is REMOVING and RECYCLING the views that are not visible yet on the ListView because of height. So that if you have 10 item views, and ListView can display 4 - 5 at a the time :
The Adapter REMOVE the item views at position 5 to 9, so that any attempt to adapter.getChildAt(5... to 9) will cause null pointer exception
The Adapter also RECYCLE the item view, so that any reference you made on position 3 for example will be lost when you scroll down to 5 to 9, and also any Input that you make on position 3 (EditText, Checkbox, etc.) will be recycled when you scroll down to 5 to 9 and will be reused at another position later (ex position 1, 2 or 3, etc.) with the same value
The only way I found to control this is to forget about getting the View and to have :
Attribute HashMap<Integer, ImageView> iconViews or any type you want for handling the values you want to use for each item on the list. The first type must be unique for item like item->getId() or position. Initialize it with new HashMap<>() in the Constructor;
in getViews make iconViews.put(position, iconView);
Prevent Adapter from using recycled convertView, remove condition if(convertView == null) so that adapter always inflate a brand new view instance. Because the view instance is new each time, you must set the value from HashMap each time also like if it already contains the key if(iconViews.containsKey(position)){iconView = iconViews.get(position))};. Probably in this case there is not tons of Items, so that smooth scrolling won't be a must.
And finally create public methods to get the Values outside of Adapter passing item->getId() Integer as parameter. Ex : public ImageView getIconViewAt(int position) { return iconViews.get(position); } . It will be easy then to select Item from Adapter
See more from my answer.
Try
if (f_listView.getChildCount() > 0){
f_listView.getChildAt(i)
}
Isn't it because you are saying
f_listView.getChildAt(i)
And you should be retrieving the item at that position?
f_listView.getItemAtPosition(i)

Categories

Resources