How to correctly use TextSwitcher in ListView?
Asked Answered
L

4

8

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)));
Litigable answered 23/5, 2011 at 18:44 Comment(0)
D
3

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.

Darnell answered 8/6, 2011 at 21:43 Comment(1)
@Litigable exactly. ListView is reusing a TextSwitcher that previously held another record.Darnell
G
4

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.

Gowrie answered 5/6, 2011 at 0:14 Comment(6)
Thanks, @wnafee. Could you please clarify where (at which moment, at which method) do I need to add my views (TextViews) to ViewFlipper?Litigable
I would replace your code underneath the switch statement case R.id.timetext with the new ViewFlipper code. In there you would probably setup your ViewFlipper, then add your 2 text views in there then set the flipping interval then call start flipping and then you're good to go. (btw, your code above doesn't show where you get your text from your cursor, so it's hard to see at what point you actually retrieve your data from your db, but I assume it is in setViewValue() but you omitted it. You should be doing your cursor lookup in there based on columnIndex passed to you)Gowrie
@wnafee, I've updated the question with explanation how I read my values from the database.Litigable
@LA_, thanks for updating. Most likely, the problem is with the concept of a convertView that listViews use. Basically, lists reuse row items that go off the screen (eg. user scrolls & row scrolls off screen) and sends them to be used and refilled by the next row coming onto the screen. If you do not clear all the text items inside that recycled view before you put in your new information, you might see old data that is from other rows still in there. Try clearing everything inside the view object before adding stuff in there. This is along the lines of what codefusionmobile posted.Gowrie
@wnafee, doesn't itemTime.removeAllViews(); clear everything inside the view? Or, should I do it each time (not only if (itemTime.getChildCount() != 2))?Litigable
@CodeFusionMobile, @wnafee, will not I have the same problem with ViewFlipper?Litigable
D
3

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.

Darnell answered 8/6, 2011 at 21:43 Comment(1)
@Litigable exactly. ListView is reusing a TextSwitcher that previously held another record.Darnell
M
0

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.

Manger answered 3/6, 2011 at 3:13 Comment(1)
No, they are from the database, not from resources.Litigable
S
0

This worked for me :

myViewFlipper.setFlipInterval(1000);
myViewFlipper.startFlipping();
Solfatara answered 24/7, 2016 at 22:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.