Error in Listview update always stops - android

i have this listview update:
timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
display.elemek.clear();
em1.new AsyncEmBase().execute();
}
}, 0, 1000);
public class AsyncEmBase extends AsyncTask<Void, ListView, Void> {
protected void onPreExecute() {
// display.dataAdapter.clear();
}
#Override
protected Void doInBackground(Void... params) {
readInputRegisters(); /*this only makes display.elemek.add("SomeString")*/
return null;
}
protected void onPostExecute(Void unsed) {
if (display.dataAdapter == null) {
display.dataAdapter = new ArrayAdapter<String>(
display.activity, android.R.layout.simple_list_item_1,
display.elemek);
display.lv.setAdapter(display.dataAdapter);
} else {
display.dataAdapter.notifyDataSetChanged();
}
}
}
it runs smootly but after a while it stops and says:
11-27 15:21:05.769: E/AndroidRuntime(17991): java.lang.IllegalStateException: 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. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131296261, class android.widget.ListView) with Adapter(class android.widget.ArrayAdapter)]
i have tried everithing to update the listview but nothing is good.
Please help me!
edit1: new error: Invalid index 7, size is 0
But i clear the elemek arraylist before starting to insert the datas
display.java:
public class display extends Fragment {
public static TextView ain1, ain2, ain3, ain4, dout1, dout2, din1, din2,
din3, din4, cin1, cin2, cin3, cin4, k1, k2, k3, k4, k5, k6, k7, k8;
public static Activity activity;
public static ListView lv;
public static ArrayList<String> elemek;
public static ArrayAdapter<String> dataAdapter;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.display, container, false);
ain1 = (TextView) rootView.findViewById(R.id.textView2);
activity = getActivity();
lv = (ListView) rootView.findViewById(R.id.listView1);
elemek = new ArrayList<String>();
return rootView;
}

display.activity.runOnUiThread(...) is useless because onPostExecute() is already executed inside the UI thread
and I don't think you have to call notifyDataSetChanged() because you're setting the adapter just before.

Remove all your Thread because you dont need uiThread in onPostExecute() and add like this :
if ( display.dataAdapter == null) {
display.dataAdapter = new ArrayAdapter<String>(
display.activity,
android.R.layout.simple_list_item_1, display.elemek);
display.lv.setAdapter(display.dataAdapter);
}else{
display.dataAdapter.notifyDataSetChanged();
}

Related

Updating the list object passed to the adapter to create a GridView

I am trying to update the contents of the list object which has been passed to the adapter inorder to create a GridView. Here is the code:
public class GameActivity extends Activity {
List<String> nums;
GridView sudoku_grid;
ArrayAdapter<String> adapter;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_temp);
sudoku_grid=(GridView)findViewById(R.id.sudoku_grid);
nums= Arrays.asList("1","2","3","5","7","8","2","3","1","9","3","3","1");
adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,nums);
sudoku_grid.setAdapter(adapter);
}
public void trial(View view)
{
Runnable r=new Runnable() {
#Override
public void run() {
nums.add("7");
adapter.notifyDataSetChanged();
}};
new Thread(r).start();
}
trial() executes every time a button is clicked. As soon as the thread in trial() starts, instead of the contents being updated, it results in starting the previous activity. Can anyone tell me where am I going wrong?
You need to pass the nums object again to the adapter.
Create a class that extends ArrayAdapter<String>
as follows:
class MyArrayAdapter extends ArrayAdapter<String>{
List<String> nums;
public MyArrayAdapter(Context context,int itemID,List<String> nums){
this.nums=nums;
}
#ovverride
public int getCount() {
return nums.size();
}
public void changeObjects(List<String> nums){
this.nums=nums;
this.notifyDataSetChanged();
}
public View getView(int position, View convertView, ViewGroup parent){
//.... Implement own code
}
}
Let me know if there is any more problem or queries.
I think you can not run notifyDataSetChanged() in your own thread. It needs to run on UI Thread.
Try:
GameActivity.this.runOnUiThread(new Runnable() {
...
instead.

Setting the items after creating ArrayAdapter?

In my app, I am showing a list of items downloaded from internet (of type pItem). I am using a custom ArrayAdapter that extends ArrayAdapter<pItem> which is basically like this:
public class PArrayAdapter extends ArrayAdapter<pItem> {
private List<pItem> pList = new ArrayList<pItem>();
public PArrayAdapter(Context context) {
super(context, R.layout.p_row);
this.context = context;
}
public void SetList(List<PItem> pl){
pList = pl;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = convertView;
if (rowView == null) {
LayoutInflater vi;
vi = LayoutInflater.from(getContext());
rowView = vi.inflate(R.layout.p_row, null);
}
// ...
return rowView;
}
}
Now, normally when creating a custom ArrayAdapter, the list of items is set in the constructor. But in my case, as I dynamically fetch the items in an AsyncTask (the list is changing constantly), I separated the setting of the items with the creation of the adapter.
In my main activity, this is what I do: (only the main part of the code)
public class MainPageActivity extends SherlockFragmentActivity implements
OnScrollListener {
// List of items
private List<pItem> pList = new ArrayList<pItem>();
// Adapter
private arrayAdapter pAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_page);
//...
listView = (ListView) findViewById(R.id.p_list);
adpater = new PArrayAdapter(getApplicationContext());
new ASTask().execute("");
}
private class ASTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... args) {
// Add the downloaded items to list
pList.addAll(Downloader.GetItems());
return "";
}
#Override
protected void onPostExecute(String result) {
Log.d("Main", String.valueOf(pList.size())); // Shows the right number
// Show Items
pAdapter.setList(pList);
listView.setAdapter(pAdapter);
Handler mHandler = new Handler(Looper.getMainLooper());
Runnable rn = new Runnable() {
#Override
public void run() {
pAdapter.notifyDataSetChanged();
}
};
mHandler.post(rn);
}
#Override
protected void onPreExecute() {}
#Override
protected void onProgressUpdate(Void... values) {}
}
}
Here is the problem: When I run it, nothing is shown in the listView. It doesn't give any errors.
But if I set the list in the constructor of PArrayAdapter, everything works fine and I see the items.
What am I doing wrong here?
in onPostExecute
remove this code
Handler mHandler = new Handler(Looper.getMainLooper());
Runnable rn = new Runnable() {
#Override
public void run() {
pAdapter.notifyDataSetChanged();
}
};
mHandler.post(rn);
add this line only
pAdapter.notifyDataSetChanged();
Add
pAdapter.notifyDataSetChanged();
at the end of onPostExecute() method.
Try like this:
#Override
protected void onPostExecute(String result) {
Log.d("Main", String.valueOf(pList.size())); // Shows the right number
// Show Items
pAdapter.setList(pList);
listView.setAdapter(pAdapter);
pAdapter.notifyDataSetChanged();
}
OK. I found what was my problem. It was actually a rookie mistake:
Instead of assigning the input list to the list inside adapter:
pList = pl;
I should have add all the items like this:
pList.addAll(pl);
This solved the problem.

ArrayAdapter in Fragment

I'm beginner in Android Development!
I'm trying insert into Fragment parsing data
Trying to fix error but I have an error "
output cannot be resolved to a variable"
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
ProgressDialog mProgressDialog = new ProgressDialog(getActivity());
mProgressDialog.setCancelable(false);
mProgressDialog.setCanceledOnTouchOutside(false);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setMessage("Загрузка данных");
mProgressDialog.show();
new ParseSite().execute("http://www.babyblog.ru/magazine/");
View view = inflater.inflate(R.layout.magazine, container, false);
ListView listview = (ListView) view.findViewById(R.id.listViewData);
listview.setAdapter(new ArrayAdapter<String>(getActivity().getApplicationContext(),
android.R.layout.simple_list_item_1 , output));
return view;
}
private class ParseSite extends AsyncTask<String, Void, List<String>> {
protected List<String> doInBackground(String... arg) {
List<String> output = new ArrayList<String>();
try
{
HtmlHelper hh = new HtmlHelper(new URL(arg[0]));
List<TagNode> links = hh.getLinksByClass("razdel-name");
for (Iterator<TagNode> iterator = links.iterator(); iterator.hasNext();)
{
TagNode divElement = (TagNode) iterator.next();
output.add(divElement.getText().toString());
}
}
catch(Exception e)
{
e.printStackTrace();
}
return output;
}
}
}
The variable 'output' has not been defined.
i.e.
listview.setAdapter(new ArrayAdapter<String>(getActivity().getApplicationContext(),
android.R.layout.simple_list_item_1 , output));
That variable is not in scope.
You attemot to use it in onCreate of your fragment, but it is declared in the ASyncTask class.
You need to go and read about ASyncTasks and how you work with them.
http://developer.android.com/guide/components/processes-and-threads.html#WorkerThreads
as a shortcut, try this:
public class YourFragment extends Fragment {
ListView listview;
#Override
public View onCreateView(
// other stuff
listview = (ListView) view.findViewById(R.id.listViewData);
// remove the setAdapater line
}
private class ParseSite extends AsyncTask<String, Void, List<String>> {
// other stuff
protected void onPostExecute(List<String> result) {
listview.setAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1 , output));
}
}
Your output variable is still null. You have to implement onPostExecute(List result) in your AsyncTask.
This will give you the output you want.
this blogpost can explain how AsyncTask works. But be careful, AsyncTask has a couple of hidden pitfalls. Read all about that here

Android Synchronization with AsyncTask

This is my main activity:
adapter.setNotifyOnChange(true);
lv = (ListView) findViewById(R.id.listView1);
lv.setAdapter(adapter);
public void onClick(View view) {
adapter.add(text);
new DoSomethingWithListViewChild(lv, index).execute();
}
And this is the AsyncTask DoSomethingWithListViewChild:
public class DoSomethingWithListViewChild extends AsyncTask<Void, Void, Void> {
TextView tv;
public DoSomethingWithListViewChild (ListView lv, int index) {
View v = lv.getChildAt(index - lv.getFirstVisiblePosition());
tv = (TextView) v.findViewById(R.id.textTitle);
}
#Override
protected Void doInBackground(Void... params) {
// Do something with TV
return null;
}
The problem is that lv is still empty when the code View v = lv.getChildAt(index - lv.getFirstVisiblePosition()); is being executed.
I tried some synchronization with lock objects but I'm ending with deadlocks or other errors.
How do I wait untill lv is not empty?
Edit:
Ok, I tried this:
...
ConditionVariable lock = new ConditionVariable(false);
...
public void onClick(View view) {
adapter.add(text);
lock.block();
new DoSomethingWithListViewChild(lv, index).execute();
}
And in my custom adapter:
public class MyAdapter extends ArrayAdapter<SpannableString> {
ConditionVariable lock;
public MyAdapter(Context context, int layout_resource_id, ConditionVariable lock) {
...
this.lock = lock;
...
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
...
lock.open();
return convertView;
}
}
But the programm just hangs. I need to find the exact moment when lv is populated to open the lock. It clearly not in the getView method.. It even don't reach the lock.open();
May be needs to call adapter.notifyDayaSetChanged() after adapter.add(text)

Android ListView not refreshing after notifyDataSetChanged

My ListFragment code
public class ItemFragment extends ListFragment {
private DatabaseHandler dbHelper;
private static final String TITLE = "Items";
private static final String LOG_TAG = "debugger";
private ItemAdapter adapter;
private List<Item> items;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.item_fragment_list, container, false);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.setHasOptionsMenu(true);
super.onCreate(savedInstanceState);
getActivity().setTitle(TITLE);
dbHelper = new DatabaseHandler(getActivity());
items = dbHelper.getItems();
adapter = new ItemAdapter(getActivity().getApplicationContext(), items);
this.setListAdapter(adapter);
}
#Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
adapter.notifyDataSetChanged();
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
if(dbHelper != null) { //item is edited
Item item = (Item) this.getListAdapter().getItem(position);
Intent intent = new Intent(getActivity(), AddItemActivity.class);
intent.putExtra(IntentConstants.ITEM, item);
startActivity(intent);
}
}
}
My ListView
<?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" >
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
But this does not refresh the ListView. Even after restarting app the updated items are not shown. My ItemAdapter extends BaseAdapter
public class ItemAdapter extends BaseAdapter{
private LayoutInflater inflater;
private List<Item> items;
private Context context;
public ProjectListItemAdapter(Context context, List<Item> items) {
super();
inflater = LayoutInflater.from(context);
this.context = context;
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ItemViewHolder holder = null;
if(convertView == null) {
holder = new ItemViewHolder();
convertView = inflater.inflate(R.layout.list_item, parent,false);
holder.itemName = (TextView) convertView.findViewById(R.id.topText);
holder.itemLocation = (TextView) convertView.findViewById(R.id.bottomText);
convertView.setTag(holder);
} else {
holder = (ItemViewHolder) convertView.getTag();
}
holder.itemName.setText("Name: " + items.get(position).getName());
holder.itemLocation.setText("Location: " + items.get(position).getLocation());
if(position % 2 == 0) {
convertView.setBackgroundColor(context.getResources().getColor(R.color.evenRowColor));
} else {
convertView.setBackgroundColor(context.getResources().getColor(R.color.oddRowColor));
}
return convertView;
}
private static class ItemViewHolder {
TextView itemName;
TextView itemLocation;
}
}
Can someone help please?
Look at your onResume method in ItemFragment:
#Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); // reload the items from database
adapter.notifyDataSetChanged();
}
what you just have updated before calling notifyDataSetChanged() is not the adapter's field private List<Item> items; but the identically declared field of the fragment. The adapter still stores a reference to list of items you passed when you created the adapter (e.g. in fragment's onCreate).
The shortest (in sense of number of changes) but not elegant way to make your code behave as you expect is simply to replace the line:
items = dbHelper.getItems(); // reload the items from database
with
items.addAll(dbHelper.getItems()); // reload the items from database
A more elegant solution:
1) remove items private List<Item> items; from ItemFragment - we need to keep reference to them only in adapter
2) change onCreate to :
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setHasOptionsMenu(true);
getActivity().setTitle(TITLE);
dbHelper = new DatabaseHandler(getActivity());
adapter = new ItemAdapter(getActivity(), dbHelper.getItems());
setListAdapter(adapter);
}
3) add method in ItemAdapter:
public void swapItems(List<Item> items) {
this.items = items;
notifyDataSetChanged();
}
4) change your onResume to:
#Override
public void onResume() {
super.onResume();
adapter.swapItems(dbHelper.getItems());
}
You are assigning reloaded items to global variable items in onResume(), but this will not reflect in ItemAdapter class, because it has its own instance variable called 'items'.
For refreshing ListView, add a refresh() in ItemAdapter class which accepts list data i.e items
class ItemAdapter
{
.....
public void refresh(List<Item> items)
{
this.items = items;
notifyDataSetChanged();
}
}
update onResume() with following code
#Override
public void onResume()
{
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
**adapter.refresh(items);**
}
In onResume() change this line
items = dbHelper.getItems(); //reload the items from database
to
items.addAll(dbHelper.getItems()); //reload the items from database
The problem is that you're never telling your adapter about the new items list. If you don't want to pass a new list to your adapter (as it seems you don't), then just use items.addAll after your clear(). This will ensure you are modifying the same list that the adapter has a reference to.
If the adapter is already set, setting it again will not refresh the listview. Instead first check if the listview has a adapter and then call the appropriate method.
I think its not a very good idea to create a new instance of the adapter while setting the list view. Instead, create an object.
BuildingAdapter adapter = new BuildingAdapter(context);
if(getListView().getAdapter() == null){ //Adapter not set yet.
setListAdapter(adapter);
}
else{ //Already has an adapter
adapter.notifyDataSetChanged();
}
Also you might try to run the refresh list on UI Thread:
activity.runOnUiThread(new Runnable() {
public void run() {
//do your modifications here
// for example
adapter.add(new Object());
adapter.notifyDataSetChanged()
}
});
If you want to update your listview doesn't matter if you want to do that on onResume(), onCreate() or in some other function, first thing that you have to realize is that you won't need to create a new instance of the adapter, just populate the arrays with your data again.
The idea is something similar to this :
private ArrayList<String> titles;
private MyListAdapter adapter;
private ListView myListView;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
myListView = (ListView) findViewById(R.id.my_list);
titles = new ArrayList<String>()
for(int i =0; i<20;i++){
titles.add("Title "+i);
}
adapter = new MyListAdapter(this, titles);
myListView.setAdapter(adapter);
}
#Override
public void onResume(){
super.onResume();
// first clear the items and populate the new items
titles.clear();
for(int i =0; i<20;i++){
titles.add("New Title "+i);
}
adapter.notifySetDataChanged();
}
So depending on that answer you should use the same List<Item> in your Fragment. In your first adapter initialization you fill your list with the items and set adapter to your listview. After that in every change in your items you have to clear the values from the main List<Item> items and than populate it again with your new items and call notifySetDataChanged();.
That's how it works : ).
An answer from AlexGo did the trick for me:
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
messages.add(m);
adapter.notifyDataSetChanged();
getListView().setSelection(messages.size()-1);
}
});
List Update worked for me before when the update was triggered from a GUI event, thus being in the UI thread.
However, when I update the list from another event/thread - i.e. a call from outside the app, the update would not be in the UI thread and it ignored the call to getListView. Calling the update with runOnUiThread as above did the trick for me. Thanks!!
Try this
#Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
adapter = new ItemAdapter(getActivity(), items);//reload the items from database
adapter.notifyDataSetChanged();
}
adpter.notifyDataSetInvalidated();
Try this in onPause() method of Activity class.
If your list is contained in the Adapter itself, calling the function that updates the list should also call notifyDataSetChanged().
Running this function from the UI Thread did the trick for me:
The refresh() function inside the Adapter
public void refresh(){
//manipulate list
notifyDataSetChanged();
}
Then in turn run this function from the UI Thread
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.refresh()
}
});
Try like this:
this.notifyDataSetChanged();
instead of:
adapter.notifyDataSetChanged();
You have to notifyDataSetChanged() to the ListView not to the adapter class.
adapter.setNotifyDataChanged()
should do the trick.

Categories

Resources