ListView and remembering data - android

I have some problems with a ListView(it's more of a visual problem), it remembers data such as text values from TextViews, from the ListView's rows, but tangles or forgets aspects regarding the TextViews background color.
The problem shows up only when the ListView contains many rows. I work with a SimpleCursorAdapter and a ViewBinder and want to highlight a TextView if a condition occurs:
In the ViewBinder implementation:
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
CharSequence display;
if(view.getId() == R.id.dueDate){
long timestamp = Long.parseLong(cursor.getString(3));
display=DateUtils.getRelativeTimeSpanString(timestamp);
if(timestamp+DaysToMillis(5)> new Date().getTime())
((TextView) view).setBackgroundResource(R.color.marker_red);
((TextView) view).setText(display);
return true;
}
else
return false;
}
So again: when there are many rows the ListView seems to tangle the background color. Some times when I re-scroll, it change's again the TextView's background and I can't seem to find the bug or the ListView's logic.
So, does the setViewValue(..) method gets called every time before inflating a row? Even if you scroll up or down and a row that's not visible anymore get's visible again? Or the setViewValue(..) method gets called just for a initial row inflating process and then the created objects are keept in the ListView's memory?
P.S. The text values from the same TextView is displayed correctly. I used the same logic in the same setViewValue(..) method.

Same newbie
It seems to me (and if I'm wrong please correct me... especially if you are Roman Guy) that ViewBinder, with it's setViewValue method recycles the listView's rows...
setViewValue(..) gets called for every "FROM - TO" binding for every row displayed, even for rows that were already inflated, and by scrolling up/down they got displayed again;
and regarding my amassing piece of code, for a row that gets displayed again:
((TextView) view).setText(display); - will set the correct content, same timestamp will get processed... but if it doesn't passes the:
if(timestamp+DaysToMillis(5)> new Date().getTime())
((TextView) view).setBackgroundResource(R.color.marker_red);
-the current TextView might have for background the background from a recycled TextView
anyways I had to add an else ((TextView) view).setBackgroundResource(0); to my if

Related

how to access ListView with CheckedTextView?

I'm now developing an application that uses a ListView with a
CheckedTextView on every item that managed by an ArrayAdapter to
support multiple chooses. The contents in my ListView are dynamic, that
means, can be changed during runtime. Now I try to use
ListView.getCheckedItemPositions() to get all checked items, Because I want
to save all the checked positions and auto-check them when user go back to
this page again. So I need to save checked results for every page.
For the first page everything works fine as expected. But when user goes to
another page and make some chooses, the result array that ListView returned
contains some positions that are never checked. I don't why ListView has
this strange behavior. Even for a page that in fact no checked happens but
ListView gives me a result that indicates there's one item has been checked.
could anyone who can teach me how to get the position of CheckedTextView
in its OnClickListener callback?
example code is appreciate.
Thanks in advance...
The listview recycles its views so when you go to a different page and then return to the previous page, the listview recalls the getView() function for its views. To make sure that the order of the checked views are not mixed up, create an arraylist that contains the check state of all the views before initializing the adapter. Then pass the arraylist as an argument for the adapter's constructor. There, in the getView() function, set the checked state of each checkable textview based on the arraylist. Then, return to the activity class and override the onItemClick() event. Using the view that is given to you when the function is called, do the following to get the checkable textview and set its checked state:
listView1.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> arg0, View selectedView, int position , long id)
{
CheckedTextView tv = (CheckedTextView)selectedView.findViewById(R.id.textview);
if (tv.isChecked())
{
tv.setChecked(false);
checkStatesOfViews.get(position) = false;
}
else
{
tv.setChecked(true);
checkStatesOfViews.get(position) = true;
}
}
});

How to correctly use TextSwitcher in ListView?

My TextSwitcher for each record in ListView should display first value (text1) and then another value (text2), then first value again and so on. It should happen only if text2 not empty. Otherwise text1 should be always shown (without any changes and animation).
I've created Runnable(), which changes boolean variable (time2) to then call items.notifyDataSetChanged(). It works as expected and in result setViewValue() for my ListView is called.
Here is the code:
items.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
int viewId = view.getId();
switch(viewId) {
case R.id.timetext:
TextSwitcher itemTime = (TextSwitcher) view;
if (itemTime.getChildCount() != 2) {
itemTime.removeAllViews();
itemTime.setFactory(new ViewSwitcher.ViewFactory() {
#Override
public View makeView() {
TextView t = new TextView(MyActivity.this);
t.setTextSize(18);
t.setTypeface(null, Typeface.BOLD);
t.setTextColor(Color.WHITE);
return t;
}
});
itemTime.setAnimateFirstView(true);
itemTime.setInAnimation(AnimationUtils.loadAnimation(MyActivity.this,
R.anim.push_up_in));
itemTime.setOutAnimation(AnimationUtils.loadAnimation(MyActivity.this,
R.anim.push_up_out));
}
if (!text2.equals("")) {
if (!time2) {
itemTime.setText(text1);
} else {
itemTime.setText(text2);
}
} else {
itemTime.setCurrentText(text1);
}
return true;
}
return false;
}
} );
It works almost as expected. With one minor item - when text2 should be shown, it changes displayed value to some other value first (from another record!) and then animation is played. Change of text2 to text1 happens correctly.
My understanding that the reason is the following - before displaying text2, all views of itemTime are removed and hence it is recreated and that is why some other value is shown for a second. But why does it show value from some other record?
Actually text2 and text1 are values from the database, for ex.
text2 = cursor.getString(cursor.getColumnIndexOrThrow(DbAdapter.KEY_TIME_2)), probably, something is wrong here and setViewValue called with wrong parameters?
Upd. text1 and text2 are read from the database at setViewValue. Here is example of the full code:
itemTime.setText(cursor.getString(cursor.getColumnIndexOrThrow(DbAdapter.KEY_CLOSE_TIME_1)) + " - " + cursor.getString(cursor.getColumnIndexOrThrow(DbAdapter.KEY_OPEN_TIME_1)));
I know this might not answer the question directly, but I'm going to respond to your comment about creating a Runnable() to do the work of switching for you because I suspect that it is probably messing with your data (hard to tell when you cant see the full code).
I advise you to use a ViewFlipper instead of a TextSwitcher. The reason for doing that is that once you added the TextView's inside your ViewFlipper, you can just set your flip interval and then start the flipping and it will do it automatically for you.
As simple as this:
/* Add your items to your ViewFlipper first */
myViewFlipper.setFlipInterval(1000); //time in millseconds
myViewFlipper.startFlipping();
In your current method that you described, when you call items.notifyDataSetChanged() you incur a huge performance hit because all items of your database are going to be re-read and your list will be "re-drawn" again. You should only do that if your actual data really changed rather than using it to switch between text that you already have and doesn't change from creation time.
As a nice surprise, you might notice that your problem goes away because you don't have to re-read everything from you DB again and reduces the chances of mix-up of item1 and item2 since you will only need to read them once when the row is created in your ListView
Just my 2 cents.
Let me know how it goes.
I think I see what's going on here, and it's because of the way ListView works.
ListView recycles all of its views internally so that you only have as many views created as can be displayed on the screen. However, this also means that when you bind values to a view in your setViewValue method, you are not always given the view that was in the same position in the list before.
Say you have three list items: itemA, itemB, itemC in that order. Each contains text1, text2, and text3 respectively at first.
When you call items.notifyDataSetChanged(), ListView recycles all those list items however it feels like, so you may get a new order of itemC, itemA, itemB; and the text would then read text3, text1, text2.
As a result, when you change the text of the first list item to "text2", you will in fact see "text3" change to "text2" instead of a transition from "text1" to "text2" like you are expecting.
Are text1 and text2 stored in the resources file (res/values/strings.xml)? If so, Android will sometimes confuse variables. Simply running Project > Clean on this project may fix the problem.
This worked for me :
myViewFlipper.setFlipInterval(1000);
myViewFlipper.startFlipping();

setting textview visibility to GONE when there is no text to show

i have a listview in which i have 2 textviews, one of these textview contains zero text by default, but can be changed by the user. the problem is that i need to do so when the textview is empty the visibility of it is set to GONE. i have 2 ideas of how this might work, either defining it in xml or defining it in the database somehow.
public long createDate(String date) {
ContentValues initialValues1 = new ContentValues();
initialValues1.put(KEY_DATE, date);
initialValues1.put(KEY_TIMESTAMP, "00:00");
if(text==""){
initialValues1.put(KEY_DICTTAG,View.GONE); //this does NOT work
}else{
initialValues1.put(KEY_DICTTAG,text);
}
initialValues1.put(KEY_DICTALARMTIME, "0");
initialValues1.put(KEY_DICTLISTIMAGE, R.drawable.list_icon);
return mdiktationsDb.insert(DATABASE_TABLE, null, initialValues1);
}
i know the textview wont be seen by the user when there is no text, but i need it to not be seen by the system so to speak. this is because i need the timestamp textview to be centerd in the relative layout when there is no text in the dicttag textview. i could not post the xml layout because i do not have any room
It is a little unclear what the actual problem is but here goes....
First, are you sure that your if statement is working properly? You are comparing a String in Java with ==. I don't know where text is but use if (text.equalsIgnoreCase("")) instead.
Second, TextView.setVisibility(View.GONE) on your TextView should work as intended assuming you are handling it on the main UI thread. You can always override your ListView adapter and make a custom adapter and do your visibility operations there for each item in the ListView. There are plenty of posts and tutorials for doing that.
txt.setVisibility(View.GONE) for hide
txt.setVisibility(View.VISIBLE); for show
Try if (text.equals ("")) instead of if (text == "")

Marking text in the row of the list as "READ" permenatly Android

What I'm trying to accomplish here is to set the text in a given row as read if the user clicks on it, now I was able to do that by using the onclick method, the problem with it is that it goes away when an intent is fired or the user exits the app. I want the Text to be set up as read permanently. here is my piece of code if anybody can help I would greatly appreciate it.
Thank you in advance:
public void onListItemClick(ListView parent, View v, int position, long id) {
LinearLayout ll = (LinearLayout) v;
TextView clickedTextView = (TextView) ll.getChildAt(1);
clickedTextView.setTextColor(Color.GREEN);
StringTokenizer st = new StringTokenizer(strings[position],"<#>");
for(int i=0;i<3;i++)
{
coupon = st.nextToken("<#>");
}
sharable=st.nextToken();
Intent i = new Intent(getApplicationContext(), CouponImage.class);
i.putExtra("The coupon", coupon);
i.putExtra("Sharable", sharable);
startActivity(i);
}
You'll have to store the read status for each text item, either in a SQLite Database, or in a flat file in Internal Storage.
This is because whenever you scroll the list, leave the app and come back, etc. you end up with the ListAdapter re-rendering your row view, and your views in a list are never 1:1 with underlying data due to view recycling. If you want your change to be "sticky" you need to think about modifying the underlying data for the ListAdapter in a way it the adapter knows how to render correctly, not just changing this particular instance of a row -- you're marking the item read, not just setting one instance of a rendered view of the item as read. You can think of it as an MVC thing if it helps.
That is, the real change here should be to your Adapter's getView method, with a change to its data source and then possibly a call to notifyDataSetChanged.
Fredley's answer about SQLite or storage may be overkill if that data doesn't need to persist beyond this one session in the activity, or if the data you're working with is also transient (e.g. network data that changes often that's temporarily loaded into an ArrayAdapter), and in any case it's a bit misleading because just dumping data to disk doesn't solve the fundamental issue with your conflation of views of data with your models.

How to Identify a ListViewItem to Update a Single Row

I have a ListView displays information about an object. When I click a ListView Item, I open an Activity that let's me manipulate parameters of the object held in the adapter. These parameters are updated and stored remotely.
When I return to the ListView (via the back button), I want to update the ListView Item that I clicked originally by requesting the parameter values from the remote server.
I am currently doing this up updating the entire ListView by clearing it and rebuilding it.
How do I reference the ListView Item so that I can update the data for that item only?
Thanks,
Jason
Just set the data for your adapter and call notifyDatasetChanged. Android only draws the rows that are visible anyway, so it's pretty efficient.
Knowing the position of the view, this can be done.
View singleItemView = myListView.getChildAt(position);
ImageView icn = (ImageView) singleItemView.findViewById(R.id.icn_icon);
ProgressBar prg = (ProgressBar) singleItemView.findViewById(R.id.prg_icon);
icn.setVisibility(View.GONE);
prg.setVisibility(View.VISIBLE);
(EDIT)
This doesn't work because the "position" of the View could be changed upon returning from another Activity. After the onResume() fires, getView() is called and the position of the visible ListView Items are numbered starting with 0. So, the ListView items (id and position) become renumbered based on visiblity. As a result, I end up accessing the wrong object.

Categories

Resources