Change the background of an item in a ListView displayed - android

I have a ListView populated from a database using a customized ResourceCursorAdapter with a bindView to format each list item. The items are displayed in descending chronological order and I need to hightlight the first future event. Unfortunately I only know that it is the first future event when I process the next entry and find that it is in the past. What I really need is the ability to 'look ahead' in the bindView processing but that is not there.
The only alternative (that I can think of) is to process all items and then go back to change the background of the first future event (which I would then know).
The code I'm using is
EventsAdapter db = new EventsAdapter(this);
db.open();
Cursor cursor = db.fetchAllRecords();
final MyAdapter listitems = new MyAdapter(this,R.layout.timeline_detail, cursor, 0);
timeline.setAdapter(listitems);
db.close();
View v = timeline.getChildAt(firstEvent); // firstevent is set in the bindView
v.setBackgroundColor(Color.parseColor("#ffd1d1"));
However the View v is always null and, when I run it in debug mode with a breakpoint at the View statement, the listview has not yet rendered on the screen. I'm assuming, therefore, that that is why the view is still null.
How can I get and changeView the ListView item that needs to be changed at the time that the ListView is first displayed?

Try and extend your adapter and override getView.
final MyAdapter listitems = new MyAdapter(this,R.layout.timeline_detail, cursor, 0){
public View getView(int position, View convertView, ViewGroup parent){
View v = super.getView(position, convertView, parent);
if (position==0){
//Do something to the first item (v)
}else{
//Revert what you did above, since views get recycled.
}
return v;
}
};
or just include that in your MyAdapter class.

Related

Animate ListView items on notifyDataSetChanged()

I want to add some animation to my ListView with SimpleCursorAdapter when my DB data changes.
I am using SimpleCursorAdapter with LoaderManager
I tried to add animation in getView method
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view=super.getView(position, convertView, parent);
//My animation(nineoldandroid)
ObjectAnimator.ofFloat(view,"alpha",0,1).setDuration(500).start();
return view;
}
But this method animates all items, although some items were the same before updating.
Does Android have methods to detect which items need update, remove or add?
For example, maybe there are methods for getting changes between old and new cursor before calling myAdapter.swapCursor(newCursor); It would be possible to mark items(removed, updated, new) and make removing animation before swapping and ither animation in getView.
How can I solve this problem?
There are many ways to achieve this, you can try the answers in this question :
How to Animate Addition or Removal of Android ListView Rows
The 1st answer is working
No, at least I haven't found that Android can report to me if there are new or deleted items. I made something similar where the items of the ListView animate to their new positions and I had to track their positions myself to do that.
For your use case, it is probably easier to put the fading animation just before you delete the row from the database. So wherever in your code it is that you delete the row from the database and trigger a refresh, you do the fade animation on the item and on completion of the animation you delete the row and trigger a refresh.
Update
Hmmm, I read too quickly. Your example is not a fade-out on deleted items, but a fade-in on new items.
You will have to track item IDs yourself in your adapter. This approach can only work if you have a limited number of items to show because the list of previous item IDs will have to be stored in memory. If you can not be certain the list is never too large then you'll have to find a way to add the 'newness' information to the cursor itself.
Let me see if I can cook something up. Be back later...
Update 2
Below is a SimpleCursorAdapter that keeps track of the item ID from the previous cursor. In getView() it kicks of the fade-in animation if the requested view is for an item that was not present before.
private static class FadeInAdapter extends SimpleCursorAdapter {
private List<Long> previousItemIds = new ArrayList<Long>();
private FadeInAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View result = super.getView(position, convertView, parent);
// check if this view is for an item that is new
long itemId = getItemId(position);
if (!previousItemIds.contains(itemId)) {
// do the fade-in animation
result.setAlpha(0);
result.animate().alpha(1).setDuration(500).start();
}
return result;
}
#Override
public void changeCursor(Cursor cursor) {
Cursor oldCursor = getCursor();
if (oldCursor != null) {
// store previous item IDs to check against for newness with the new cursor
final int idColumnIndex = oldCursor.getColumnIndexOrThrow(DatabaseContract._ID);
previousItemIds.clear();
for (oldCursor.moveToFirst(); !oldCursor.isAfterLast(); oldCursor.moveToNext()) {
previousItemIds.add(oldCursor.getLong(idColumnIndex));
}
}
super.changeCursor(cursor);
}
}
Maybe RecyclerView helps. Try notifyItemChanged

Android selection issue in ListView

I have a ListView (set to CHOICE_MODE_SINGLE)
I have a SimpleCursorAdapter who fill my ListView. Now i work on selection.
serviceListView.setAdapter(
new SimpleCursorAdapter(this, R.layout.service_listitem, cursor, new String[] { "name" }, new int[] { R.id.service_name }) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final View renderer = super.getView(position, convertView, parent);
if (position == newSelectedPosition) {
renderer.setBackgroundResource(R.layout.list_view_layer_list);
} else {
renderer.setBackgroundResource(android.R.color.transparent);
}
return renderer;
}
}
);
So i want when i select a row my layout to be applied. This works fine.
But in some cases when i push for 2-3 secs a row and then drag a little bit and release the row i obtain 2 rows selected.
I try several ways to get ride of the initial selection, overwriting OnTouchListener, OnScrollListener, OnLongClickListener. No results.
Any help is welcome
Thanks
Have you tried calling notifyDataSetChanged() on your Adapter after the selection has been changed? That should cause all the rows to be rebound again, and all the views (except for the selected one) to revert back to the transparent background.
I quit this implementation.
I will try to "simulate" ListView by using a ScrollView with TextView-s for each row

Values disappearing from a Listview on Scroll - Android

I have a custom adapter extended from the SimpleCursorAdapter. Using this I'm binding a ListView which contains a checkbox and Two textboxes. On opening the activity, the list appears fine. But on clicking the checkboxes and entering some text in the textboxes and scrolling down, and then up again, the data disappears.
In fact any change disappears, even if they were already checked. I uncheck them, then scroll down and up, the go back to checked. So basically, they go back whatever state they were when retrieved.
Any ideas why? Thanks.
You need to have an arraylist of the states of each item in the list,, then load these states each time the list item view is loaded.Do this by overriding GetView() method in the adapter and add your saved state to the list based on the item position
Listview tends to recreate its views every time your list is scrolled up or down. You need to have some kind of model class that can save the state of your checkbox and textbox in memory in case some change is done(for that particular row) and later display it on the view.
As mentioned on other answers in this post u can use getview to programatically induce values that you have stored in your model classes to your views based on the list view position.
Something like this
#Override
public View getView(int position, View convertView, ViewGroup parent) {
System.out.println("getView " + position + " " + convertView);
ViewHolder holder = null;
if (convertView == null)
{
convertView = mInflater.inflate(R.layout.item1, null);
holder = new ViewHolder();
holder.textView = (TextView)convertView.findViewById(R.id.text);
convertView.setTag(holder);
}
else {
holder = (ViewHolder)convertView.getTag();
}
// Pass on the value to your text view like this. You can do it similarly for a check box as well
holder.textView.setText(mData.get(position));
return convertView;
}
Android does not render all ListView entries at once, but only those visible on the screen. When "new" List-Rows come into view the
public View getView(int position, View convertView, ViewGroup parent)
method of your Adapter gets called and the view is recreated.
To fill in previousely saved values you probalby have to overwrite the getView method.

Load ActivityList with pre-selected rows

I have a list of custom objects that I am loading into an ActivityList that allows multiple selections and displays the checkbox on the right side. Those custom objects contain a field named "enabled". When I load the data I want to scroll through the list of objects and check the checkbox for each row that represents an object that has the enable field marked true. Currently I have all of the records loading into the ActivityList as I want but I can not make any of the rows "checked" even though objects are marked as "enabled".
This is the code I am using to mark a row as checked
for (int i = 0; i < sourceList.length; i++) {
DataSource d = sourceList[i];
view.getChildAt(i).getClass().toString());
CheckedTextView checkView = (CheckedTextView)view.getChildAt(i);
checkView.setChecked(Boolean.parseBoolean(d.enabled));
}
I have put this code directly after calling setListAdapter and I have put it in the onContentChanged() function. However, in both places the rows are not displayed yet so the view.getChildAt(i) returns null so obviously the row does not get checked.
Can anyone tell me where I can put this code so that it will be executed after all rows have been added and displayed on the screen?
Thank you!
Originally I did not have a custom Adapter I was just using ArrayAdapter. In order to override the getView() method I created a custom Adapter and extended ArrayAdapter. I am still allowing the ArrayAdapter class to do most of the work but I am overriding getView(). Here is my code for getView()
#Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = super.getView(position, convertView, parent);
if (convertView.getClass() == CheckedTextView.class){
CheckedTextView checkView = (CheckedTextView)convertView;
DataSource d = getItem(position);
checkView.setText(d.getName());
checkView.setChecked(Boolean.parseBoolean(d.enabled));
}
return convertView;
}
Even with this code none of the check boxes are being checked. The DataSource's name field is being set as the text but the setChecked() method does not seem to be working for me. I also tried hard coding that to be
checkView.setChecked(true);
That did not work for me either. Do you have any more ideas on what I might be doing wrong?
Thanks again!
Can you show your adapter code? The place to call setChecked is in your getView code in your adapter. It will be something like:
#Override
getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(
R.layout.my_list_row, parent, false);
}
CheckedTextView checkView = (CheckedTextView)
convertView.findViewById(R.id.check_view);
DataSource d = sourceList[i];
checkView.setChecked(Boolean.parseBoolean(d.enabled));
return convertView;
}
ListView doesn't really expose its children via getChildAt. The intended interface is via constructing and populating rows in getView.
What you need to do is call myList.setItemChecked(position, Boolean.parseBoolean(d.enabled)); in your getView method (where myList is an instance of ListView).
Also make sure you've called myList.setChoiceMode(int choiceMode) before setting your list adapter.
If you have Checkable views inside another view, then the containing view needs to implement Checkable as well for checkable items in lists to work properly (see CheckableRelativeLayout for instance)

ListView in ArrayAdapter order get's mixed up when scrolling

I have a ListView in a custom ArrayAdapter that displays an icon ImageView and a TextView in each row. When I make the list long enough to let you scroll through it, the order starts out right, but when I start to scroll down, some of the earlier entries start re-appearing. If I scroll back up, the old order changes. Doing this repeatedly eventually causes the entire list order to be seemingly random. So scrolling the list is either causing the child order to change, or the drawing is not refreshing correctly.
What could cause something like this to happen? I need the order the items are displayed to the user to be the same order they are added to the ArrayList, or at LEAST to remain in one static order. If I need to provide more detailed information, please let me know. Any help is appreciated. Thanks.
I was having similar issues, but when clicking an item in the custom list, the items on the screen would reverse in sequence. If I clicked again, they'd reverse back to where they were originally.
After reading this, I checked my code where I overload the getView method. I was getting the view from the convertedView, and if it was null, that's when I'd build my stuff. However, after placing a breakpoint, I found that it was calling this method on every click and on subsequent clicks, the convertedView was not null therefore the items weren't being set.
Here is an example of what it was:
public View getView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null)
{
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.listitemrow, null);
RssItem rssItem = (RssItem) super.getItem(position);
if (rssItem != null)
{
TextView title = (TextView) view.findViewById(R.id.rowtitle);
if (title != null)
{
title.setText(rssItem.getTitle());
}
}
}
return view;
}
The subtle change is moving the close brace for the null check on the view to just after inflating:
public View getView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null)
{
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.listitemrow, null);
}
RssItem rssItem = (RssItem) super.getItem(position);
if (rssItem != null)
{
TextView title = (TextView) view.findViewById(R.id.rowtitle);
if (title != null)
{
title.setText(rssItem.getTitle());
}
}
return view;
}
I hope this helps others who experience this same problem.
To further clarify the answer of farcats below in more general way, here is my explanation:
The vi.inflate operation (needed here for parsing of the layout of a row from XML and creating the appropriate View object) is wrapped by an if (view == null) statement for efficiency, so the inflation of the same object will not happen again and again every time it pops into view.
HOWEVER, the other parts of the getView method are used to set other parameters and therefore should NOT be included within the if (view == null) statement.
Similarily, in other common implementation of this method, some textView, ImageView or ImageButton elements need to be populated by values from the list[position], using findViewById and after that .setText or .setImageBitmap operations.
These operations must come after both creating a view from scratch by inflation and getting an existing view if not null.
Another good example where this solution is applied for BaseAdapter appears in BaseAdapter causing ListView to go out of order when scrolled
The ListView reuses view objects when you scroll. Are you overriding the getView method? You need to make sure you set each property for every view, don't assume that it will remember what you had before. If you post that method, someone can probably point you at the part that is incorrect.
I have a ListView, AdapterView and a View (search_options) that contains EditText and 3 Spinners. ListView items are multiple copies of (search_options) layout, where user can add more options in ListView then click search to send sql query built according to users options.
I found that convertView mixing indecies so I added a global list (myViews) in activity and passed it to ArrayAdapter. Then in ArrayAdapter (getView) I add every newly added view to it (myViews).
Also on getView instead of checking if convertView is null, I check if the global list (myViews) has a view on the selected (position).. It totally solved problems after losing 3 days reading the internet!!
1- on Activity add this:
Map<Integer, View> myViews = new HashMap<>();
and then pass it to ArrayAdapter using adapter constructor.
mSOAdapter = new SearchOptionsAdapter(getActivity(), resultStrs, myViews);
2- on getView:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder viewHolder;
if (!myViews.containsKey(position)) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
view = inflater.inflate(R.layout.search_options, parent, false);
/// ...... YOUR CODE
myViews.put(position, view);
FontUtils.setCustomFontsIn(view, getContext().getAssets());
}else {
view = myViews.get(position);
}
return view;
}
Finally no more mixing items...

Categories

Resources