I'm working on refreshing a list in Android but can't seem to get it right.
I've used notifyDataSetChanged(); at every point that I thought applicable (currently using dialogs for input), but it doesn't work, and I've got to the point of plastering it around all over the place and it still won't refresh.
Am I right in saying this should refresh the list while your looking at it, or it will rebuild the list and you still have to refresh the view?
If anyone has got any suggestions for the positioning of it in relation to constructing a list I'd be glad to hear.
Is this a ListActivity? I have a ListActivity in my project at the moment and I have my own adapter class within it that extends ArrayAdapter.
My experience is that calling notifyDataSetChanged() on my extended list adapter class instance does immediately cause a refresh of the list View being displayed. So, as soon as I call .notifyDataSetChanged() on my adapter instance, the list View is regenerated which therefore causes my adapter's implementation of getView() to be called to generate each individual row view again. So, the user selects a context menu item which triggers some change to the data and then a call to .notifyDataSetChanged(), and the screen instantly refreshes with the new data.
So to add some code snippets to be clear:
I have a ListActivity
public class VarListActivity extends ListActivity {
Within it, I extend ArrayAdapter
class VarAdapter extends ArrayAdapter{
...
#Override
public View getView(int position, View convertView, ViewGroup parent){
// Creates the views based upon myData
...
#Override
public int getCount(){
...
And I create an instance of that array adapter
la = new VarAdapter(this, R.layout.row0);
And when a context menu item is selected
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.a_context_menu_option:
// Does a call to perform modifications to myData
la.notifyDataSetChanged();
return true;
I'm just chucking this all down just in case it's of any similarity to your situation, but really we need to know a bit more about your code.
Related
I'm new on both Parse and Android development, and currently developing a chat feature that powered by ParseQueryAdapter on Android. The problem is I want the UX like any other chat app: latest message on the bottom and 'load more' button on top, and I couldn't get the result I want.
I tried orderByAscending("updatedAt") on my query and the result is seems right: item list ordered from older to newer (top to bottom). Problem appears when max item (25 items) is reached and 'load more' button appears. 'load more' button put on the bottom of the ListView and the top message is still the first message delivered.
Now I start to think to order my query orderByDescending("updatedAt") back and modify ParseQueryAdapter's getItem method so items will be ordered reversely.
I tried to subclass ParseQueryAdapter's getItem into:
#Override
public ParseObject getItem(int position) {
return super.getItem(getCount() - position - 1);
}
I get it from this post since ParseQueryAdapter is based on BaseAdapter.
And sadly it returns NullPointerException when getting object's getParseUser on my ParseQueryAdapter subclass as below:
public class ChatContentCustomAdapter extends ParseQueryAdapter<ParseObject> {
public ChatContentCustomAdapter(Context context, final String roomId){ ... }
public View getItemView(final ParseObject object, View v, ViewGroup parent) {
if (object.getParseUser("sender") == cUser) {
//populating layout
}
}
}
What should I do to overcome this problem?
It is going to be way too much hassle for you to achieve what you really want using the ParseQueryAdapter. I would recommend that fetch the object from Parse and implement a custom BaseAdapter to plugin to your ListView.
I'm working on a fragment that contains a listview (this fragment is PlaceHolderFragment generated when create activity). I extends ArrayAdapter to make my custom adapter and fill my listview with this adapter.
One important thing is in one row of listview, there are 2 buttons: first is the enable/disable button to change status of an user (when user's status is active then it's disable, otherwise enable), second is the delete button (to delete user). So I have to implement OnClickListener for this 2 buttons in method getView() of adapter
When click either buttons, it will send request to server and manipulate database (change user's status or delete user from database). The thing is when I click enable button (for example), it is success and user's status in database is changed, or when I click delete button, user will be delete from database successfully
BUT after I click that button, it's state does not changed (I mean if user is enabled, now the button must change to disable, or if user is deleted, that row must be remove from screen). I have to reload this fragment by hand (switch to other fragment and then come back)
The question is how can I reload activity (I already implement onResume to load all data to adapter, so if I can make this method onResume of fragment run, it will work as my expectation), or at least how can I reload the listview to update new data?
Note: notifyDataSetChanged() DOES NOT work because the data in adapter actually doesn't change yet, only data on server are changed
Note 2: if you need me to post my code, please comment and I will edit my post, because I think my code is long
Thank you and best regards!
EDIT 1
I've posted my solution in the answer below, it fix the problem but I have to say that this is a very very BAD practice in android. For example, when you want to delete an item with this approach, you may want to put a AlertDialog for user to confirm, but AlertDialog can only show in Activity (or Fragment), it can't be show from Adapter. So instead, you should use some different methods such as ContextMenu or CustomDialog.
After couple of weeks searching google and try different methods, I finally found a way to archive what I want, and it's very simple. I post my answer here for anyone facing this problem like me in the future
public class CustomAdapter extends ArrayAdapter<YourClass> {
private List<YourClass> items;
private CustomAdapter adapter;
private Context context;
private Button button;
private YourClass item;
public CustomAdapter(Context context, List<YourClass> items) {
super(context, R.layout.custom_list_item, items);
this.items = items;
this.context = context;
this.adapter = this; //This is an important line, you need this line to keep track the adapter variable
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater li = LayoutInflater.from(getContext());
v = li.inflate(R.layout.custom_list_item, null);
}
item = items.get(position);
button = (Button) v.findViewById(R.id.button); //Find the button
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
//Do something inside here like update, remove your item...
//But the important is
items.remove(item); //Actually change your list of items here
adapter.notifyDataSetChanged(); //notify for change
}
});
}
return v;
}
That's it, all you need to implement when you need the listview to reload in case you need to implement OnClick inside the adapter. Hope you guy find it easier than me when you face this problem
What I understand from your question that you want to refresh the list data when click on one of the button as you need.
Now You should call notifydataset change rather reloading the activity again.
For this you need to change the array that yu are using for the adapter(Don't change the array object just the Item of the array)
like arraylst.get(index).activ=true etc etc and then call notifiydataset change.
I'm developing a Androidapplication using ListView.
ListView have a one file in each and every ListItem. Here, I have set onItemClickin ListView. So, that if user clicks the ListItememail application gets open and attach the particular file in email. Its for the single File, this gets implemented and working fine.
Now I want attach the multiple file in email. i.e. the implementing the CheckBoxin each ListItemand checked items have to attached into the Mail.
I know its possible because its very similar to the file manager application that checking the multiple file and deleting the all file by clicking the single Button. But don't know how to do.
In you ListAdapter create a SparseBooleanArray
private SparseBooleanArray checkStatus;
This SparseBooleanArray stores the checked items. Now in getView do the following
#Override
public View getView(int position, View view, ViewGroup parent) {
ViewCache viewCache;
if (view == null){
viewCache = new ViewCache();
view = layoutInflater.inflate(R.layout.list_box, null, false);
viewCache.checkBox = view.findViewById(R.id.check_box);
viewCache.checkBox.setOnCheckedChangeListener(onCheckedChangeListener);
//other views in the list box
...........
}
vewCache = (ViewCache)view.getTag();
viewCache.checkBox.setTag(position);
viewCache.checkBox.setChecked(isChecked(position));
//set other views
........
}
This is the class ViewCache
private static class ViewCache{
CheckBox checkBox;
//other views in the list box
.......
}
This method checks whether the position is checked
private boolean isChecked(int position){
return checkStatus.get(position, false);
}
This is the onCheckChangeListener
CompoundButton.OnCheckedChangeListener onCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
checkStatus.put(((Integer)compoundButton.getTag()), b);
}
};
Finally you can get the checked items from the SparseBooleanArray checkStatus. Think it will help you.
You can try implementing your own ArrayAdapter. Initialize it with an array of your file objects and use it in the list view.
Next make a list of indexes that is visible by the adapter and can be manipulated from the outside. In your onItemClick method you have the position of the clicked item. If it's in that list remove it, otherwise - insert it. Let's call that list selection.
Next in your adapter's getView method construct a view with a checkbox inside. Again you have the current position, because it's passed as an argument. Set the checkbox state depending on the presence of the position in selection.
Finally implement your button's onClick so that it does whatever you do with your file objects only for those objects of your file_array whose positions are in your selection.
Hope that helps
In the above answers Sreejith has given a good explanation of how to store the states of the checked items in the list view using a SparseBooleanArray. This solves the first part of your problem.
The second part regarding the passing of the states of these items to the other activities can be achieved using the Application class.
Application class:
Base class for those who need to maintain global application state. Sometimes you want to store data, like global variables which need to be accessed from multiple Activities - sometimes everywhere within the application. In this case, the Application object will help you.
Here is a sample code for this:
public class TopClass extends Application {
private static TopClass topClass;
public TopClass getInstance()
{
return topClass;
}
#Override
public void onCreate ( )
{
super.onCreate();
topClass = this;
}
public ArrayList<String> arrList = new ArrayList<String>();
}
You need to set tag android:name="TopClass" in the application manifest file under the application tag. Something like this:
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:name="TopClass" >
....
....
Here is how you can access it from the activity:
TopClass top = (TopClass)getApplicationContext();
top.arrList.add("StackOverflow");
Now you can access the same variable from other activities similarly.
can you help me, if there is any way, how to call method startActivityForResult(..) from class extends ArrayAdapter?
Thanks.
EDIT: ADDED CODE OF ARRAYADAPTER:
There is code of MyAdapter class:
public class MyAdapter extends ArrayAdapter {
public static final String bundle_text = "some_text";
public MyAdapter(Context context, int textViewResourceId, List<MyAdapterItem> data) {
super(context, textViewResourceId, data);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
// ...
row.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(getContext(), MyNewActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
int id = 1;
Bundle bundle = new Bundle();
bundle.putLong(MyAdapter.bundle_text, id);
intent.putExtras(bundle);
getContext().startActivity(intent);
}
});
return super.getView(position, convertView, parent);
}
}
All works, but from ArrayAdapter class is not possible to call startActivityForResult, I can call only startActivty.
But after MyNewActivity is finished, I would like to refresh listview, but I do not know how, when I cannot call startActivityForResult.
This a common misconception (as is old thread, I will just give an outline)
Adapter is only for data to list view - it does return a view as in your getView for a row, but doesn't really handle any GUI events - those are done in activity. In fact, if adapter is loading a lot of rows from internet or database it should be done outside the UI thread.
ACTIVITY IS FOR EVENTS. You attach a click listener to a listview within your activity. Then there is no problem calling e.g. a detail edit activity - it is all on UI thread. So no problem starting an activity from UI thread.
PROBLEM OF UPDATING ADAPTER! If you back return from detail activity you will find the list is NOT updated as adapter was NOT updated. To do this, start detail Activity for RESULT. Don't clutter up the detail "form" with any adapter stuff. In fact, you may choose to return the POJO that was created or edited in detail record, and not bother with the database at all.
Now in your activity you listen for onActivityResult() and do the insert into the adapter (and database backend if you have e.g. SQLite in app), then notify Data changed for list view. It will update the list view automatically.
PERFORMANCE can be an issue if listview is reloaded. In most cases you can get away by adding to adapter and simply notify the list view data is changed. Since data display is "incremental" you may have edited row 3 of rows 1 - 5 shown. Then list view will only refresh those 1-5 rows display positions. But if you reload say 500 rows when only 1 is changed you could get performance issues :)
I have a problem with listView, I used ArrayList to store data, and a customized Adapter. But, when I remove all the data, and add one item again, it does not display anything in this list. What happens to my List, can anyone help me?
static ArrayList<String> chattingListData=new ArrayList<String>();
static IconicAdapter chattingListDataAdapter;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
chattingListDataAdapter=new IconicAdapter(this);
setListAdapter(chattingListDataAdapter);
registerForContextMenu(this.getListView());
}
class IconicAdapter extends ArrayAdapter {
Activity context;
IconicAdapter(Activity context) {
super(context, R.layout.chatting_list, chattingListData);
this.context=context;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater=context.getLayoutInflater();
View row=inflater.inflate(R.layout.chatting_list, null);
TextView label=(TextView)row.findViewById(R.id.chattinglist_userName);
label.setText(chattingListData.get(position));
return(row);
}
}
I use static ArrayList to modify data from outside, when I start the ListView activity and add data, it's Ok, but when I remove all data, I can not add anymore to the data list. Please help me.
I use static ArrayList to modify data from outside
Don't do that.
Step #1: Use a non-static ArrayList data member as the basis for your ArrayAdapter.
Step #2: Expose methods on your activity that adds and removes items via the add(), insert(), and remove() methods on ArrayAdapter. By using these methods, your ListView will automatically update when you make the changes.
Step #3: Use the methods you wrote in Step #2 to modify the contents of your ListView, by whoever is doing this (not shown in your code).
You can also use the Adapter's notifyDataSetChanged() method, in order to force it to refresh.