How to correctly use TextSwitcher in ListView? - android

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();

Related

How to change number of column of a gridview at same visible item. Android

I am writing an android app where I am using a grid view to display some items. I want to give users a configurable view where they can change the no of columns on the activity by clicking floating action button. I am changing the column no using
gridView.setNumColumns(selectedColumnNo);
This is working fine But the problem is if a user changes no of column after some scrolling the First Visible Position is set to the first item of the array list, so the user has to scroll the view again. Can someone please tell me where I am doing wrong. Or Is this the proper way to do this or should I use a different approach.
A code snippets will be helpful
Thanks.
Update::
currently I am using the bellow snippets
findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int firstPosition = gv.getFirstVisiblePosition();
if(gv.getNumColumns()==2)
{
gv.setNumColumns(1);
gv.setSelection(firstPosition);
}
else {
gv.setNumColumns(2);
gv.setSelection(firstPosition);
}
}
});
Now the problem is on every 4th switch grid view is showing the first element of the arraylist
Right before you call setNumColumns(), save the GridView's first visible position:
int firstPosition = gridView.getFirstVisiblePosition();
Then, after you change the number of columns, pass that integer to setSelection():
gridView.setSelection(firstPosition);
"Selection", counter-intuitively, is not the same thing as "activation". It will ensure that the view is on-screen, but not visibly affect it in any other way.

Change text of listview in onItemClick (listview is filled by a string-array/arrayAdapter)

I want to change the text in my listview.
In my onCreate I fill my listview and setting an OnItemClicklistener:
onCreate:
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1, trainingstage);
setListAdapter(adapter);
ListView listView = getListView();
listView.setOnItemClickListener(this);
onItemClick:
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
TextView tv = (TextView)view.findViewById(android.R.id.text1);
switch (position) {
case 0:
tv.setText("Hello");
IF I CLICK TEXT SHOULD CHANGE!
break;
That works fine, but if I restart the activity the old text is back. I want to change the text forever, if I click on the listitem!
You can achieve this by two options:
Easier way
Change text in trainingstage and set string array to STATIC!
Better way
If you want to save the text after click, you need some kind of database to store the change. Either use sqlite if you want to save different text. If number of text is fixed, better to use SharedPreferences. You would also need to restore the text from here to set the TextView on Activity start.
ListViews as the name implies is the VIEW where data is presented to users. You use your ArrayAdapter to populate the ListView from your DATA. The DATA in this example is your trainingstage String list.
This is a good data separation technique used all over Android. If you just change the textview anything else that uses that data wont be updated, you have only updated the view. What you need to do is actually update the DATA layer(trainingstage) which in turn will be passed to the adapter then rendered by the list view.
Without knowing exactly where and how you get trainingstage data it's hard to give you a suggestion on how to do that but a simple example would be:
in onItemClick
trainingstage.get(position) = "My New String";
parent.notifyDatasetChanged();
This probably won't persist when you restart your activity. You will need to do additional steps to save the STATE of your DATA. Aashish talks briefly about that in an above post.

ListView and remembering data

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

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.

Categories

Resources