Listview notifydatachanged() timing concern - android

I have an app that works on the emulator but will intermittently crash when testing on a real device. I am running a modified version of Code Overtones Android Crash report. (http://jyro.blogspot.com/2009/09/crash-report-for-android-app.html) The emailed cause states:
The content of the adapter has changed but ListView did not receive a
notification. Make sure the content of your adapter is not modified
from a background thread, but only from the UI thread. [in
ListView(2131296303, class android.widget.ListView) with Adapter(class
android.widget.SimpleAdapter)]
The code does not modify the adapter outside of the UI main thread. All access to the adapter is through the onPostExecute of an AsyncTask. The code is:
#Override
protected void onPostExecute(String result) {
MyLog.d(TAG, "onPostExecute");
foodDescArrayList.clear();
int entries = holdFoodDescArrayList.size();
HashMap<String, String> listEntry;
for (int i = 0; i < entries; i++) {
listEntry = new HashMap<String, String>();
listEntry = holdFoodDescArrayList.get(i);
foodDescArrayList.add(listEntry);
}
holdFoodDescArrayList.clear();
hideProgress();
foodDescAdapter.notifyDataSetChanged();
String ents = " entries";
if (rowsReturned == 1) {
ents = " entry";
}
foodDescHeader.setText("Page " + Integer.toString(currentPage + 1)
+ " of " + Integer.toString(pageCount) + " (" + Integer.toString(rowsReturned) + ents + ")");
loadActive = false;
}
The holdFoodDescArrayList is a stand alone list filled in the background task from an SQLite database. the foodDescArrayList is the array associated with the ListView adapter. (I've found that there is a performance boost when queuing this way. Maybe because the adapter is out of the loop during database access. Less overhead?)
The crash always occurs on first entry (onCreate) after a home key exit and then reentry to the top level activity that calls the list activity. The time between crashes is from 30 minutes to 2 hours during continuous testing. The code that is crashing has been traversed hundreds of times and is linear with no exits.
The only possible hole I can find in reviewing the code is the clear() preceding the array load. Does the clear function act as one change and the group of adds as a second change? Is there a timing consideration? There are from 1 to 24 entries in the list, so the load should take milliseconds and not seconds...
I'm looking for ideas and clues. Please scan the code and see if you see a glaring error or side effect in the code. It is the only place the ListView's associated array data is altered in the app. The background code only alters the hold array.
Please don't enter a full code rewrite in an answer. I'm continuing to try and find the why and which. I don't want to waste more than a few minutes of your time. I will look at this post often for the next few days and answer any questions I find. Thanks for any help...
----------- Update to add code requested ----------
The code in onCreate is:
setContentView(R.layout.fooddeslist);
foodDescHeader = (TextView) findViewById(R.id.foodDescHeading);
foodDescListView = (ListView) findViewById(R.id.foodDescList);
foodDescArrayList = new ArrayList<HashMap<String, String>>();
holdFoodDescArrayList = new ArrayList<HashMap<String, String>>();
foodDescAdapter = new SimpleAdapter(this, foodDescArrayList,
R.layout.longdescitem, new String[] { GC.FOODDESCLIST_LINE1,
GC.FOODDESCLIST_LINE2 },
new int[] { R.id.longdescListItemLine1,
R.id.longdescListItemLine2 });
foodDescListView.setAdapter(foodDescAdapter);
registerForContextMenu(foodDescListView);
The array entries are:
listEntry = new HashMap<String, String>();
The hold array is a duplicate of the adapter's array. The hold array is loaded in the doInBackground function in the AsyncTask. I'd show the background code, but it's about 500 lines of code. The end result is that the hold array is loaded with the 2 lines to display and ancillary data that is unique to each entry. Row ids for various bits of data in other tables that is used when an entry is selected. The duplicate tables allow me to take eons (to gigahertz processors) loading the array. The transfer in the UI running post execute just takes a couple of milliseconds.
longdescitem.xml is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView android:id="#+id/longdescListItemLine1"
android:textStyle="italic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#ff9900"
/>
<TextView android:id="#+id/longdescListItemLine2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#b89300"
/>
</LinearLayout>
R.layout.fooddesclist is:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/foodDescHeading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginLeft="2dip"
android:text="Page 1 of 10 (nnn entries)"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#b89300" />
<ImageButton
android:id="#+id/foodDescForward"
android:layout_width="50px"
android:layout_height="50px"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginRight="12dip"
android:layout_marginTop="5dip"
android:background="#drawable/forward"
android:clickable="true"
android:onClick="foodDescForwardClicked" />
<ImageButton
android:id="#+id/foodDescBack"
android:layout_width="50px"
android:layout_height="50px"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="12dip"
android:layout_marginTop="5dip"
android:background="#drawable/back"
android:clickable="true"
android:onClick="foodDescBackClicked" />
<View
android:id="#+id/foodDescSpacer1"
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_alignParentLeft="true"
android:layout_below="#id/foodDescForward"
android:layout_marginBottom="2dip"
android:layout_marginTop="2dip"
android:background="#drawable/divider" />
<ListView
android:id="#+id/foodDescList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="#id/foodDescSpacer1"
android:layout_marginLeft="2dip"
android:layout_marginRight="2dip"
android:cacheColorHint="#00000000"
android:divider="#b89300"
android:dividerHeight="1.0px" />
</RelativeLayout>
-------- Another Update --------------
I've added some code to the opPostExecute function.
protected void onPostExecute(String result) {
MyLog.d(TAG, "onPostExecute");
foodDescArrayList.clear();
foodDescAdapter.notifyDataSetChanged();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
MyLog.d(TAG, "Sleep failed: " + e.getMessage());
}
int entries = holdFoodDescArrayList.size();
HashMap<String, String> listEntry;
for (int i = 0; i < entries; i++) {
listEntry = new HashMap<String, String>();
listEntry = holdFoodDescArrayList.get(i);
foodDescArrayList.add(listEntry);
}
holdFoodDescArrayList.clear();
hideProgress();
foodDescAdapter.notifyDataSetChanged();
String ents = " entries";
if (rowsReturned == 1) {
ents = " entry";
}
foodDescHeader.setText("Page " + Integer.toString(currentPage + 1)
+ " of " + Integer.toString(pageCount) + " (" + Integer.toString(rowsReturned) + ents + ")");
loadActive = false;
}
After two hours of steady testing on the Atrix, it hasn't crashed yet. Before the code, I would get a crash at least once and sometimes twice in 2 hours. I added the notifydatasetchanged after the clear and followed it with a 300 msec sleep. I think the operating system was stepping on its toes because the original notify got lost. Although the database queries can take up to 8 seconds, most of the time it's instant answer. The progress dialog never had a chance to fully set up. The screen would flicker and then the listview would show. The progress display was never identifiable. (The progress dialog is turned on in the preExecute function.) The entire AsyncTask was completing in less than 300 milliseconds. Maybe this is a fix and maybe not.

I'm answering this myself to close the post as answered.

Related

Breaking a line without increasing text length

I'm able to break a line using following code:
String str1 = "TEST1"; // length = 5
String str2 = "TEST2"; // length = 5
TextView textView = (TextView)findViewById( R.id.text_view );
textView.setText(str1 + '\n' + str2);
But the final text length is equal to 11.
Question:
Is there any special character or method that will allow me to reach the same result inside my TextView without increasing text length?
What I'm trying to achieve:
I have a data format, which is stored in JSON. It looks like
[{type: line, params: {line params}}, {type: text, params: {text params}, ...]
There is always line at the start
Each paragraph begins with line ( so it acts like a line separator which is stored at the beginning of line, not at the end )
Size of each line equals to 1, i.e. each line counts as a single character
Each paragraph ends with text's last character ( not '\n' )
There are some line params ( like BulletList, Numeric list, Paragraph )
I need a strict mapping between my TextView and source data, i.e. for each cursor position in my TextView I need to count how many characters preceed it in source data.
Take two TextViews and add one below another .Then you won't find any length problem.
like : <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView1" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/textView1"
android:text="TextView2" />
</RelativeLayout>
String str1 = "TEST1";
String str2 = "TEST2";
TextView text=(TextView)vi.findViewById(R.id.text);
text.setText(str1);
text.append(Html.fromHtml(< br>));
text.append(str2);
Hope it works :)
For your question my answer will be no. But you could make your own TextView and change how it calculates the length of the text by for example ignoring "/n" when counting the length.
Well there is tricky way
String str1 = "TEST1"; // length = 5
String str2 = "TEST2"; // length = 5
textView = (TextView)findViewById( R.id.textView1 );
textView.setWidth(120);
textView.setTextSize(20);
textView.setText(str1 + str2);
//textView.getText().toString().length() length = 10
in XMl
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="TextView" />

android replace delimeter with newline

I have a string which has delimiter in it. I want to know the best way to replace the delimiter with a new line. I have had various issues with using the String Tokenizer the main problem being NoSuchElementException. Basically my approach thus far is to retrieve data from a database Once this has been achieved I store each of the records in a string String question = c.getString(1);
Here is the string tokenizer StringTokenizer st = new StringTokenizer(question,"<ENTER>"); I loop through the tokens using a while loop
while (st.hasMoreTokens()) {
System.err.println(st.nextToken());
// quest.setText(String.valueOf(st.nextToken("<ENTER>")));
}
Working example in code
String in = "What is the output of: <ENTER><ENTER>echo 6 % 4;";
in=in.substring(in.indexOf("<ENTER>")+7,in.lastIndexOf("<ENTER>"));
String[] mSplitted= in.replaceAll("<ENTER><ENTER>", "<ENTER>").split("<ENTER>");
for(int i=0;i<mSplitted.length;i++)
{
System.out.println("values: "+mSplitted[i]);
quest.setText(String.valueOf(mSplitted[i]));
}
xml code
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/quest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="false"
android:text="TextView" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0.08"
android:text="#string/hello" />
<Button
android:id="#+id/Next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
output
can't you use split() of String.
String in = "<ENTER>title=Java-Samples<ENTER>" +
"<ENTER>author=Emiley J<ENTER>" +
"<ENTER>publisher=java-samples.com<ENTER>" +
"<ENTER>copyright=2007<ENTER>";
in=in.substring(in.indexOf("<ENTER>")+7,in.lastIndexOf("<ENTER>"));
String[] mSplitted= in.replaceAll("<ENTER><ENTER>", "<ENTER>").split("<ENTER>");
String mFinal="";
for(int i=0;i<mSplitted.length;i++)
{
System.out.println("values: "+mSplitted[i]);
mFinal= mFinal+ mSplitted[i];
}
quest.setText(mFinal);
The reason behind NoSuchElementException is that you are checking whether your Tokenizer has more tokens, but if it does, then you are reading two tokens at the same time.
Documentation says hasMoreTokens tests if there are more tokens available from this tokenizer's string.
If this method returns true, then a subsequent call to nextToken() with no argument will successfully return a token.
That means hasMoreTokens is only sure about the next token but not the token next to that!
Your condition should be..
while(st.hasMoreTokens()) {
String key = st.nextToken();
String val="";
if(st.hasMoreTokens())
val = st.nextToken();
System.out.println(key + "\n" + val);
}
As your condition will work for only even number of elements.

how do i ignore/use a carriage return in android

I am attempting to collect data from a user in the form of an edittext. The user will input a string and click a button to perform the action below:
public String encode(String s){
String result = "";
String element = "";
HashMap<String, String> translate = new HashMap<String, String>();
//initializing translate
translate.put("A",".-");
translate.put("B","-...");
translate.put("C","-.-.");
translate.put("D","-..");
translate.put("E",".");
translate.put("F","..-.");
translate.put("G","--.");
translate.put("H","....");
translate.put("I","..");
translate.put("J",".---");
translate.put("K","-.-");
translate.put("L",".-..");
translate.put("M","--");
translate.put("N","-.");
translate.put("O","---");
translate.put("P",".--.");
translate.put("Q","--.-");
translate.put("R",".-.");
translate.put("S","...");
translate.put("T","-");
translate.put("U","..-");
translate.put("V","...-");
translate.put("W",".--");
translate.put("X","-..-");
translate.put("Y","-.--");
translate.put("Z","--..");
translate.put("1",".----");
translate.put("2","..---");
translate.put("3","...--");
translate.put("4","....-");
translate.put("5",".....");
translate.put("6","-....");
translate.put("7","--...");
translate.put("8","---..");
translate.put("9","----.");
translate.put("0","-----");
s = s.toUpperCase();
for(int i=0; i < s.length();i++)
{
element = (String) translate.get(String.valueOf(s.charAt(i)));
if(element == null)
result += String.valueOf(s.charAt(i));
else
result += element;
}
return result;
}
If the user hits "enter" on the keyboard of the phone it will insert a newline / carriage return. How can I address this so that it does add a new line? I wouldn't mind using the carriage return as a way to issue the command to change focus OUT OF the edittext area, but if not that then just not allow it to be used at all.
You should use android:singleLine="true" in your EditText's XML tag, like this:
<EditText
android:id="#+id/edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
This will make the enter button change the focus to the next view in your UI, rather than inserting a new line in your EditText.
I would put this in the comments but I think my reputation is still not high enough to add comments.
Since android:singleLine="true" is now deprecated, another option is to use android:inputType="text". It's a slight change to #Tiago_Pasqualini's answer:
<EditText
android:id="#+id/edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"
/>
This too will make the enter button change focus to the next view in the UI and skip entering a new line into the EditText.

How to retrieve ALL the images saved in a SQLite db into a layout?

In my android project, i have saved camera snapshots along with some description in a SQLite database. Now i want to retrieve all those images plus the description one by one and view them in a xml layout that i have created.
here is my xml layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="#+id/showTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show Data" />
<ImageView
android:id="#+id/showIv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/marker_default"
/>"
</LinearLayout>
</LinearLayout>
i tried following code snippets seperatly,
String[] columns = new String[]{"_id", "name","description","image"};
Cursor c = myDb.query(DATABASE_TABLE, columns, null, null, null, null, null);
if (c.getCount()>0){
c.moveToNext();
byte[] data = c.getBlob(c.getColumnIndex("image"));
return data;
}else{
return null;
//i converted the byte array to bitmap and displayed on the layout
String result = "";
int iname= c.getColumnIndex("name");
int ides= c.getColumnIndex("description");
TextView tv = (TextView) findViewById(R.id.showTv);
for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
result += c.getString(iname) + c.getString(ides);
}
return result;
but i want to get both image and the description together for ALL the data.
please suggest me a method to all the Images and description one by one.
Thank you...
The layout you made won't support more than one Image. Since there is only one ImageView. Unless you want to do a lot of image manipulation, that is. So you should choose an View that fits your needs. I would look at the Gallery widget or ListView widget. Then you need to create a cursor adapter that will adapt the data from your database to your Gallery or Listview widget.
Take a look at:
listview with sqllite

Android SQLite: displaying data entries

I have been trying to work out the bug on this but can't seem to find what I'm looking for search Google for the past few days, i have hit and missed on stuff that is leaning towards what i want to do but haven't ran across anything that is what i am trying to do.
To start off, I built my SQLite DB along with "TheNewBoston Android Tuts" everything works fine, now that I am trying to change the view database around it errors out or crashes the app at start up. I am wanting to display certain columns in a row from the DB with the Cursor using the for loop and to dynamically load the TableView with the data:
DbEntry[ data | data| Displayed | data | Display | Display | data ]
I can do this if i have a TextView off on its own but with it in a TableLayout>TableRow i think that's where the problem is but not sure... would like to be able to load the table rows from the class its self instead of setting the text views if possible. I hope i gave enough info on what i am trying to do, if anyone can point me to a really good tutorial or give some light on this, it wold be greatly appreciated.
XML:
<TableLayout
android:id="#+id/view_Table"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:weightSum="5" >
<TableRow
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="4" >
<TableRow
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1" >
<TextView
android:id="#+id/view_DbCol3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="DB Col 3 Displayed Data" />
</TableRow>
with xml i hope i am doing the table right that its similar to hmtl kind of things by putting table rows in side one another
SQLview Class
setContentView(R.layout.sqlview);
TextView tv = (TextView) findViewById(R.id.view_DbCol3);
try {
DBload info = new DBload(this);
info.open();
String data = info.getData();
info.close();
tv.setText(data);
} catch (Exception e) {
// Error Message Dialog
String error = e.getLocalizedMessage();
Dialog d = new Dialog(this);
d.setTitle("DB ERROR");
TextView tv1 = new TextView(this);
tv1.setText(error);
d.setContentView(tv1);
d.show();
}
DbHelper Class: getData Method
public String getData() {
String[] cols = new String[] {KEY_PLACE};
// I have also tried with all my KEY variables
// reads info from DB columns
Cursor c = ourDB.query(KEY_TABLE, cols, null, null, null, null, null);
String result = "";
int Ione = c.getColumnIndex(KEY_PLACE);
for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
result = result
+ c.getString(Ione)
+ "\n";
}
return result;
}
the error i am getting is
11-17 00:13:09.993: E/CursorWindow(590): Bad request for field slot 0,-1. numRows = 2, numColumns = 1
like i said i hope i have given enough information for little help if possible, thanks alot
This link will help you alot.
anywayz try this in you code
if(c.getCount()>0) {
int index=0;
while(c.moveToNext()){
result = result
+ c.getString(index)
+ "\n"; `enter code here`
index++;
}
}
Hope this helps..
The error seems to say that your column 'KEY_PLACE' doesn't exists, because the columnIndex method returns -1.
Check the value of KEY_PLACE and the name of the column returned in your cursor
Hope it helps
Jokahero
PS: You should check that your cursor is not null before using it:
if (c != null) {
while (c.moveToNext()) {
// ...
}
}
And also don't forget to close your cursor when the job is finished:
c.close();

Categories

Resources