ListView Activity, dynamically insertion of list items - android

I am trying to insert list items dynamically into the list view. As list view is created and displayed on the screen now suppose i got one items from the server or some where, now i want to add this item in the same list view what i have displayed. How to do that ?? Is there any way to insert items dynamically in the displayed list view without creating the list agtain and again. And is there any way to change the status of list item, that means can we interact while it is displaying?? your's reply will be appreciated. Thnx in advance !!

Add whatever you want to the data structure (meaning the List) that is being used by your Adapter and then call notifiyDataSetChanged() on your Adapter
With a regular ArrayAdapter it would be something like:
MyAdapter adapter = new MyAdapter(this, R.layout.row, myList);
listView.setAdapter(adapter);
...
//make a bunch of changes to data
list.add(foo);
listView.getAdapter().notifyDataSetChanged();
I can provide a more complicated example with a BaseAdapter as well.
EDIT
I created a little sample because this question seems to be pretty common.
In my sample I did everything in one class, just to make it a bit easier to follow it all in one place.
In the end, it's very much a model-view-controller type situation. You can even run the actual project by cloning it from here: https://github.com/levinotik/Android-Frequently-Asked-Questions
The essence of it is this:
/**
* This Activity answers the frequently asked question
* of how to change items on the fly in a ListView.
*
* In my own project, some of the elements (inner classes, etc)
* might be extracted into separate classes, but for clarity
* purposes, I'm doing everything inline.
*
* The example here is very, very basic. But if you understand
* the concept, it can scale to anything. You have complex
* views bound to complex data wit complex conditions.
* You could model a facebook user and update the ListView
* based on changes to that user's data that's represented in
* your model.
*/
public class DynamicListViewActivity extends Activity {
MyCustomAdapter mAdapter;
#Override
public void onCreate(Bundle state) {
super.onCreate(state);
ListView listView = new ListView(this);
setContentView(listView);
/**
* Obviously, this will typically some from somewhere else,
* as opposed to be creating manually, one by one.
*/
ArrayList<MyObject> myListOfObjects = new ArrayList<MyObject>();
MyObject object1 = new MyObject("I love Android", "ListViews are cool");
myListOfObjects.add(object1);
MyObject object2 = new MyObject("Broccoli is healthy", "Pizza tastes good");
myListOfObjects.add(object2);
MyObject object3 = new MyObject("bla bla bla", "random string");
myListOfObjects.add(object3);
//Instantiate your custom adapter and hand it your listOfObjects
mAdapter = new MyCustomAdapter(this, myListOfObjects);
listView.setAdapter(mAdapter);
/**
* Now you are free to do whatever the hell you want to your ListView.
* You can add to the List, change an object in it, whatever.
* Just let your Adapter know that that the data has changed so it
* can refresh itself and the Views in the ListView.
*/
/**Here's an example. Set object2's condition to true.
If everyting worked right, then the background color
of that row will change to blue
Obviously you would do this based on some later event.
*/
object2.setSomeCondition(true);
mAdapter.notifyDataSetChanged();
}
/**
*
* An Adapter is bridge between your data
* and the views that make up the ListView.
* You provide some data and the adapter
* helps to place them into the rows
* of the ListView.
*
* Subclassing BaseAdapter gives you the most
* flexibility. You'll have to override some
* methods to get it working.
*/
class MyCustomAdapter extends BaseAdapter {
private List<MyObject> mObjects;
private Context mContext;
/**
* Create a constructor that takes a List
* of some Objects to use as the Adapter's
* data
*/
public MyCustomAdapter(Context context, List<MyObject> objects) {
mObjects = objects;
mContext = context;
}
/**
* Tell the Adapter how many items are in your data.
* Here, we can just return the size of mObjects!
*/
#Override
public int getCount() {
return mObjects.size();
}
/**
* Tell your the Adapter how to get an
* item as the specified position in the list.
*/
#Override
public Object getItem(int position) {
return mObjects.get(position);
}
/**
* If you want the id of the item
* to be something else, do something fancy here.
* Rarely any need for that.
*/
#Override
public long getItemId(int position) {
return position;
}
/**
* Here's where the real work takes place.
* Here you tell the Adapter what View to show
* for the rows in the ListView.
*
* ListViews try to recycle views, so the "convertView"
* is provided for you to reuse, but you need to check if
* it's null before trying to reuse it.
* #param position
* #param convertView
* #param parent
* #return
*/
#Override
public View getView(int position, View convertView, ViewGroup parent) {
MyView view;
if(convertView == null){
view = new MyView(mContext);
} else {
view = (MyView) convertView;
}
/**Here's where we utilize the method we exposed
in order to change the view based on the data
So right before you return the View for the ListView
to use, you just call that method.
*/
view.configure(mObjects.get(position));
return view;
}
}
/**
* Very simple layout to use in the ListView.
* Just shows some text in the center of the View
*/
public class MyView extends RelativeLayout {
private TextView someText;
public MyView(Context context) {
super(context);
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.addRule(CENTER_IN_PARENT);
someText = new TextView(context);
someText.setTextSize(20);
someText.setTextColor(Color.BLACK);
someText.setLayoutParams(params);
addView(someText);
}
/**
* Remember, your View is an regular object like any other.
* You can add whatever methods you want and expose it to the world!
* We have the method take a "MyObject" and do things to the View
* based on it.
*/
public void configure(MyObject object) {
someText.setText(object.bar);
//Check if the condition is true, if it is, set background of view to Blue.
if(object.isSomeCondition()) {
this.setBackgroundColor(Color.BLUE);
} else { //You probably need this else, because when views are recycled, it may just use Blue even when the condition isn't true.
this.setBackgroundColor(Color.WHITE);
}
}
}
/**
* This can be anything you want. Usually,
* it's some object that makes sense according
* to your business logic/domain.
*
* I'm purposely keeping this class as simple
* as possible to demonstrate the point.
*/
class MyObject {
private String foo;
private String bar;
private boolean someCondition;
public boolean isSomeCondition() {
return someCondition;
}
MyObject(String foo, String bar) {
this.foo = foo;
this.bar = bar;
}
public void setSomeCondition(boolean b) {
someCondition = b;
}
}
}
If you grasp the concept here, you should be able to adapt (no pun intended) this to ArrayAdapters, etc.

Yes, by using an adapter, you fill in the ListView, update items whenever needed, add new items, etc.
If you're pulling data over the net, you can start by using a plain ArrayAdapter (usually by subclassing and overriding the getView() method to build your layout) and then adding and removing items from the list it provides. If you add items to the list, they will appear at the end of list when you scroll down (or immediately if you are at the bottom). If you modify an item, the list will immediately update on screen.
If you use a setter on the model objects, the adapter won't know about that, but you can call notifyDataSetChanged(). You will probably also want to look at the setNotifyOnChange() method in case you want to make several changes to the list at once without causing flicker on the screen.

use notifyDataSetChanged() method.

Related

How to have my ListView catch onLongClick() (not onItemLongClick)

I have a ListView that is refreshed at a high rate (3 times per second).
I need to catch a long press on such a ListView (as well as on the parent layout); the ListView has it's height set to wrap_content.
I can catch the long click on the parent layout, but I wish the long click on any item to be handled by the parent layout.
OnItemLongClick does not work well due to the high refresh rate, I have tried the onLongClickListener to the ListView but the the long click is not fired.
The rows are set as non-clickable, not-focusable as well as all the items contained in the row.
The question is how to handle a long click anywhere in the ListView if the position / item does not matter?
I faced similar issue (with clicks on ImageButtons) when I was updating progress of my downloads in a ListView.
The solution was to update the individual rows instead of calling adapter.notifyDataSetChanged() (as what I understand is it interferes with your click/touch listeners). So to do this I problem was to find the row accurately. I need two things for this:
mapping of URL strings to my Transfer objects (mPathToTransfers),
list of URLs (mPathKeys).
Here's some code from my adapter:
protected LinkedHashMap<String, BaseTransfer> mPathToTransfers;
protected List<String> mPathKeys;
#Override
public int getCount() {
mPathKeys = new ArrayList<String>(mPathToTransfers.keySet());
return mPathToTransfers.size();
}
#Override
public BaseTransfer getItem(int position) {
return mPathToTransfers.get(mPathKeys.get(position));
}
/**
* Update transfer (download) progress in ListView's specific row
*/
public void setItemTransferredSize(String path, long transferredSize, ListView lv) {
if (mPathToTransfers.containsKey(path)) {
BaseTransfer dItem = (BaseTransfer) mPathToTransfers.get(path);
dItem.setTransferredSize(transferredSize);
Holder holder = getViewHolderForTransfer(path, lv);
if (holder != null)
setTransferProgressUpdate(holder, dItem);
}
}
/**
* Use to get the View Holder of the row of the transfer
*
* #param path
* #param lv
* #return {#linkplain Holder} for row, if it is visible, null otherwise
*/
private Holder getViewHolderForTransfer(String path, ListView lv) {
int rowIndex = mPathKeys.indexOf(path);
if (lv.getFirstVisiblePosition() <= rowIndex && lv.getLastVisiblePosition() >= rowIndex)
return (Holder) lv.getChildAt(rowIndex - lv.getFirstVisiblePosition()).getTag();
else
return null;
}
If you have ambiguities, ask in comments below (or in your question's comments). :)

FragmentPagerAdapter isn't updating the view positions when a data member is added or removed

When the FragmentPagerAdapter is first made, everything is fine. When data is added or removed from the adapter at a certain position, though, the views stay in the positions that they were in.
Here is a video demonstrating the issue in my WIP app, and here is an APK of my app that you can use to reproduce the problem.
My full code can be viewed at my GitHub project.
Here's my adapter code (also available here):
public class TaskListFragmentAdapter extends FragmentPagerAdapter {
private static final String TAG = "TaskListFragmentAdapter";
public ArrayList<Group> groups;
/**
* Fill constructor
* #param fm The FragmentManager to use
* #param groups The groups to display
*/
public TaskListFragmentAdapter(FragmentManager fm, ArrayList<Group> groups) {
super(fm);
this.groups = groups;
}
/* (non-Javadoc)
* Gets the fragment at position
* #see android.support.v4.app.FragmentPagerAdapter#getItem(int)
*/
#Override
public TaskListFragment getItem(int position) {
Log.v(TAG, "Getting item " + position);
return TaskListFragment.newInstance(groups.get(position));
}
/* (non-Javadoc)
* Gets the position of a fragment
* #see android.support.v4.view.PagerAdapter#getItemPosition(java.lang.Object)
*/
#Override
public int getItemPosition(Object o) {
TaskListFragment item = (TaskListFragment) o;
int position = groups.indexOf(item.group);
if(position >= 0) {
Log.v(TAG, "Item found at index " + position + ": " + item.group.toString());
return position;
} else {
Log.v(TAG, "Item not found");
return POSITION_NONE;
}
}
/* (non-Javadoc)
* Gets the count of fragments
* #see android.support.v4.view.PagerAdapter#getCount()
*/
#Override
public int getCount() {
return groups.size();
}
/* (non-Javadoc)
* Gets the title of a fragment
* #see android.support.v4.view.PagerAdapter#getPageTitle(int)
*/
#Override
public CharSequence getPageTitle(int position) {
return groups.get(position).getName();
}
}
The way I am changing the data is simple; I have a groups ArrayList in the activity, and it is changed using groups.add(position, item). The activity then simply does:
adapter.groups = groups;
adapter.notifyDataSetChanged();
UPDATE:
I revised my code a bit, and added proper .equals() methods to groups, and the result has changed a bit.
getItemPosition is now being called twice (for positions 1 and 2) when I insert a group at the beginning, and the last two group views seem to be updating correctly. The one added and the original first one, though, are still not updating. I have updated the APK.
Here is the full output from the adapter when a group is inserted at the beginning and the data set is changed:
V/TaskListFragmentAdapter(3597): Item found at index 1: com.gawdl3y.android.tasktimer.classes.Group#b3336038
V/TaskListFragmentAdapter(3597): Item found at index 2: com.gawdl3y.android.tasktimer.classes.Group#b330f330
That's it.
I have solved this issue, finally. What I ended up doing in the end was switching from FragmentPagerAdapter to a custom version of FragmentStatePagerAdapter. My resulting code can be seen at my GitHub project. The relevant files are TaskListFragmentAdapter.java and NewFragmentStatePagerAdapter.java. This custom version was created by UgglyNoodle here, with modifications by myself.
I just looked in to your sources and I think that the problem is that you should refresh your groups in your tasks after you add a new group.
Let's say that you have 3 groups and on the 1'st you have some of tasks (like on the video). Now you are adding a group before your first group so it goes to position 0 of your list. Now you have 4 groups, and your tasks still have group field set to 0 because they were on the 0 group before you added a new one and you didn't refresh it. So even if ViewPager is recreating your fragments, your tasks are not where you want them to be.
So I think that when you add a new group and you call reorder(groups) you should create a method that will refresh task.group field for all tasks.
From my understanding of your question, and from my brief reading of your source, I think the issue is that you are calling notifyDataSetChanged() on the FragmentPagerAdapter only. However, you also need to notify your ViewPager about this change in the data set.
If you are willing to try ViewPager Indicator library, you could also call notifyDataSetChanged() on the indicator. If not, I suggest you look into the code for TabPageIndicator in this library to see how it achieves the effect you are looking for.

Swipe / (Fling) with dynamic views

What i want to:
I want to add a swipe or what i learned it's named on android fling, to my app.
I have a dynamic number of views, the number is the amount of dates from an ICS file which i parse, i want to make a swipe effekt on all of these views together.
But if i have let's say 12 of these each having a ListView with 8 items (max) it would use a lot of memory i guess. So i would like that only the 2 before current selected view and the 2 after to be initialized.
What i have tried:
In my search i stumpled around this stackoverflow question which mentions HorizontalPager. But i dont know to make it work with a number of ListView's and load them dynamically.
I tried a ViewGroup and then add and remove a ListView but it's not working, it display's the ViewGroup but not the ListView
public class HourView extends ViewGroup
{
private ListView listView;
/**
* #param context
*/
public HourView(Context context)
{
super(context);
init(false);
}
/**
*
* #param context
* #param day the current day
*/
public HourView(Context context, String day,
boolean shouldBeVisible)
{
super(context);
this.day = day;
init(shouldBeVisible);
}
private void init(boolean shouldBeVisible)
{
if (shouldBeVisible)
{
listView = new ListView(getContext());
if (day == null)
{
day = Event.dtFormatDay.format(new Date());
}
new GetEvents(day).execute();
addView(listView);
}
else
{
removeAllViews();
listView = null;
}
}
}
The GetEvents() is a AsyncTask (a class inside the viewgroup class) that gets some events from a local database, the code is the onPostExecute is as follows
protected void onPostExecute(String errMsg)
{
if (errMsg != null)
{
listView.setAdapter(new SkemaHoursRowAdapter(getContext(), eventItems));
listView.requestLayout();
addView(listView, layoutParams);
}
}
eventItems is an array i parse to my custom rowadapter (which i know works). The view group is displayed but the listView is not.
Any suggestions??
I ended up not using a viewgroup instead i made a loader-listadapter which i display if the correct list has not been updated yet.
So instead of extending ViewGroup it extends ListView and it's adapter get set at at first to the load-adapter and when loaded it sets it to the correct listview.

Re-index/Refresh a SectionIndexer

Is there any way to re-index a SectionIndexer after new items are added to a ListView?
I found this solution, but the overlay is position in the top left corner after the SectionIndexer is refreshed.
Anyone have any ideas?
Once the FastScroller (its in AbsListView class that ListView extends from) obtains your sections by calling SectionIndexer#getSections(), it never re-obtains them unless you enable/disable fast-scrolling like mentioned in the link you mentioned. To get the value to be displayed on screen, FastScroller calls the section's toString method.
One potential solution is to have a custom SectionIndexer that have the following characteristics:
The sections array is of fixed length (max length of the expected number of sections. For example, if the sections represent English alphabet it will be 26)
Have a custom object to represent sections, rather than using strings
Overwrite the toString method of your custom section object to display what you want based on the current 'section values'.
-
e.g. In your custom SectionIndexer
private int mLastPosition;
public int getPositionForSection(int sectionIndex) {
if (sectionIndex < 0) sectionIndex = 0;
// myCurrentSectionLength is the number of sections you want to have after
// re-indexing the items in your ListView
// NOTE: myCurrentSectionLength must be less than getSections().length
if (sectionIndex >= myCurrentSectionLength) sectionIndex = myCurrentSectionLength - 1;
int position = 0;
// --- your logic to find the position goes in here
// --- e.g. see the AlphabeticIndexer source in Android repo for an example
mLastPosition = position;
return mLastPosition;
}
public Object[] getSections() {
// Assume you only have at most 3 section for this example
return new MySection[]{new MySection(), new MySection(), new MySection()};
}
// inner class within your CustomSectionIndexer
public class MySection {
MySection() {}
public String toString() {
// Get the value to displayed based on mLastPosition and the list item within that position
return "some value";
}
}
I found that the best way to do this is to call setContentView(R.layout.whatever) and then re-populate the ListView with your new adapter / new data items. This will redraw the ListView with your new items and the FastScroll Overlay will appear in the correct place.
I found notifyDataSetInvalidated working fine, here's the idea:
public class MyAdapter extends XXXAdapter implements SectionIndexer {
...
public void updateDataAndIndex(List data, Map index) {
// update sections
// update date set
notifyDataSetInvalidated();
}
}
update your data set and index (sections) somehow, and then notifyDataSetInvalidated, the index will refresh.
You can force reloading sections list to ListView by listView.setAdapter(yourAdapter)

How to refresh Android listview?

How to refresh an Android ListView after adding/deleting dynamic data?
Call notifyDataSetChanged() on your Adapter object once you've modified the data in that adapter.
Some additional specifics on how/when to call notifyDataSetChanged() can be viewed in this Google I/O video.
Also you can use this:
myListView.invalidateViews();
Please ignore all the invalidate(), invalidateViews(), requestLayout(), ... answers to this question.
The right thing to do (and luckily also marked as right answer) is to call notifyDataSetChanged() on your Adapter.
Troubleshooting
If calling notifyDataSetChanged() doesn't work all the layout methods won't help either. Believe me the ListView was properly updated. If you fail to find the difference you need to check where the data in your adapter comes from.
If this is just a collection you're keeping in memory check that you actually deleted from or added the item(s) to the collection before calling the notifyDataSetChanged().
If you're working with a database or service backend you'll have to call the method to retrieve the information again (or manipulate the in memory data) before calling the notifyDataSetChanged().
The thing is this notifyDataSetChanged only works if the dataset has changed. So that is the place to look if you don't find changes coming through. Debug if needed.
ArrayAdapter vs BaseAdapter
I did find that working with an adapter that lets you manage the collection, like a BaseAdapter works better. Some adapters like the ArrayAdapter already manage their own collection making it harder to get to the proper collection for updates. It's really just an needless extra layer of difficulty in most cases.
UI Thread
It is true that this has to be called from the UI thread. Other answers have examples on how to achieve this. However this is only required if you're working on this information from outside the UI thread. That is from a service or a non UI thread. In simple cases you'll be updating your data from a button click or another activity/fragment. So still within the UI thread. No need to always pop that runOnUiTrhead in.
Quick Example Project
Can be found at https://github.com/hanscappelle/so-2250770.git. Just clone and open the project in Android Studio (gradle). This project has a MainAcitivity building a ListView with all random data. This list can be refreshed using the action menu.
The adapter implementation I created for this example ModelObject exposes the data collection
public class MyListAdapter extends BaseAdapter {
/**
* this is our own collection of data, can be anything we
* want it to be as long as we get the abstract methods
* implemented using this data and work on this data
* (see getter) you should be fine
*/
private List<ModelObject> mData;
/**
* our ctor for this adapter, we'll accept all the things
* we need here
*
* #param mData
*/
public MyListAdapter(final Context context, final List<ModelObject> mData) {
this.mData = mData;
this.mContext = context;
}
public List<ModelObject> getData() {
return mData;
}
// implement all abstract methods here
}
Code from the MainActivity
public class MainActivity extends Activity {
private MyListAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView list = (ListView) findViewById(R.id.list);
// create some dummy data here
List<ModelObject> objects = getRandomData();
// and put it into an adapter for the list
mAdapter = new MyListAdapter(this, objects);
list.setAdapter(mAdapter);
// mAdapter is available in the helper methods below and the
// data will be updated based on action menu interactions
// you could also keep the reference to the android ListView
// object instead and use the {#link ListView#getAdapter()}
// method instead. However you would have to cast that adapter
// to your own instance every time
}
/**
* helper to show what happens when all data is new
*/
private void reloadAllData(){
// get new modified random data
List<ModelObject> objects = getRandomData();
// update data in our adapter
mAdapter.getData().clear();
mAdapter.getData().addAll(objects);
// fire the event
mAdapter.notifyDataSetChanged();
}
/**
* helper to show how only changing properties of data
* elements also works
*/
private void scrambleChecked(){
Random random = new Random();
// update data in our adapter, iterate all objects and
// resetting the checked option
for( ModelObject mo : mAdapter.getData()) {
mo.setChecked(random.nextBoolean());
}
// fire the event
mAdapter.notifyDataSetChanged();
}
}
More Information
Another nice post about the power of listViews is found here: http://www.vogella.com/articles/AndroidListView/article.html
Call runnable whenever you want:
runOnUiThread(run);
OnCreate(), you set your runnable thread:
run = new Runnable() {
public void run() {
//reload content
arraylist.clear();
arraylist.addAll(db.readAll());
adapter.notifyDataSetChanged();
listview.invalidateViews();
listview.refreshDrawableState();
}
};
i got some problems with dynamic refresh of my listview.
Call notifyDataSetChanged() on your Adapter.
Some additional specifics on how/when to call notifyDataSetChanged() can be viewed in this Google I/O video.
notifyDataSetChanged() did not work properly in my case[ I called the notifyDataSetChanged from another class]. Just in the case i edited the ListView in the running Activity (Thread). That video thanks to Christopher gave the final hint.
In my second class i used
Runnable run = new Runnable(){
public void run(){
contactsActivity.update();
}
};
contactsActivity.runOnUiThread(run);
to acces the update() from my Activity. This update includes
myAdapter.notifyDataSetChanged();
to tell the Adapter to refresh the view.
Worked fine as far as I can say.
If you are using SimpleCursorAdapter try calling requery() on the Cursor object.
if you are not still satisfied with ListView Refreshment, you can look at this snippet,this is for loading the listView from DB, Actually what you have to do is simply reload the ListView,after you perform any CRUD Operation
Its not a best way to code, but it will refresh the ListView as you wish..
It works for Me....if u find better solution,please Share...
.......
......
do your CRUD Operations..
......
.....
DBAdapter.open();
DBAdapter.insert_into_SingleList();
// Bring that DB_results and add it to list as its contents....
ls2.setAdapter(new ArrayAdapter(DynTABSample.this,
android.R.layout.simple_list_item_1, DBAdapter.DB_ListView));
DBAdapter.close();
The solutions proposed by people in this post works or not mainly depending on the Android version of your device. For Example to use the AddAll method you have to put android:minSdkVersion="10" in your android device.
To solve this questions for all devices I have created my on own method in my adapter and use inside the add and remove method inherits from ArrayAdapter that update you data without problems.
My Code: Using my own data class RaceResult, you use your own data model.
ResultGpRowAdapter.java
public class ResultGpRowAdapter extends ArrayAdapter<RaceResult> {
Context context;
int resource;
List<RaceResult> data=null;
public ResultGpRowAdapter(Context context, int resource, List<RaceResult> objects) {
super(context, resource, objects);
this.context = context;
this.resource = resource;
this.data = objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
........
}
//my own method to populate data
public void myAddAll(List<RaceResult> items) {
for (RaceResult item:items){
super.add(item);
}
}
ResultsGp.java
public class ResultsGp extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
...........
...........
ListView list = (ListView)findViewById(R.id.resultsGpList);
ResultGpRowAdapter adapter = new ResultGpRowAdapter(this, R.layout.activity_result_gp_row, new ArrayList<RaceResult>()); //Empty data
list.setAdapter(adapter);
....
....
....
//LOAD a ArrayList<RaceResult> with data
ArrayList<RaceResult> data = new ArrayList<RaceResult>();
data.add(new RaceResult(....));
data.add(new RaceResult(....));
.......
adapter.myAddAll(data); //Your list will be udpdated!!!
For me after changing information in sql database nothing could refresh list view( to be specific expandable list view) so if notifyDataSetChanged() doesn't help, you can try to clear your list first and add it again after that call notifyDataSetChanged(). For example
private List<List<SomeNewArray>> arrayList;
List<SomeNewArray> array1= getArrayList(...);
List<SomeNewArray> array2= getArrayList(...);
arrayList.clear();
arrayList.add(array1);
arrayList.add(array2);
notifyDataSetChanged();
Hope it makes sense for you.
If you want to maintain your scroll position when you refresh, and you can do this:
if (mEventListView.getAdapter() == null) {
EventLogAdapter eventLogAdapter = new EventLogAdapter(mContext, events);
mEventListView.setAdapter(eventLogAdapter);
} else {
((EventLogAdapter)mEventListView.getAdapter()).refill(events);
}
public void refill(List<EventLog> events) {
mEvents.clear();
mEvents.addAll(events);
notifyDataSetChanged();
}
For the detail information, please see Android ListView: Maintain your scroll position when you refresh.
Just use myArrayList.remove(position); inside a listener:
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override public void onItemClick(AdapterView<?> parent, android.view.View view, int position, long id) {
myArrayList.remove(position);
myArrayAdapter.notifyDataSetChanged();
}
});
You need to use a single object of that list whoose data you are inflating on ListView. If reference is change then notifyDataSetChanged() does't work .Whenever You are deleting elements from list view also delete them from the list you are using whether it is a ArrayList<> or Something else then Call
notifyDataSetChanged() on object of Your adapter class.
So here see how i managed it in my adapter see below
public class CountryCodeListAdapter extends BaseAdapter implements OnItemClickListener{
private Context context;
private ArrayList<CountryDataObject> dObj;
private ViewHolder holder;
private Typeface itemFont;
private int selectedPosition=-1;
private ArrayList<CountryDataObject> completeList;
public CountryCodeListAdapter(Context context, ArrayList<CountryDataObject> dObj) {
this.context = context;
this.dObj=dObj;
completeList=new ArrayList<CountryDataObject>();
completeList.addAll(dObj);
itemFont=Typeface.createFromAsset(context.getAssets(), "CaviarDreams.ttf");
}
#Override
public int getCount() {
return dObj.size();
}
#Override
public Object getItem(int position) {
return dObj.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View view, ViewGroup parent) {
if(view==null){
holder = new ViewHolder();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.states_inflator_layout, null);
holder.textView = ((TextView)view.findViewById(R.id.stateNameInflator));
holder.checkImg=(ImageView)view.findViewById(R.id.checkBoxState);
view.setTag(holder);
}else{
holder = (ViewHolder) view.getTag();
}
holder.textView.setText(dObj.get(position).getCountryName());
holder.textView.setTypeface(itemFont);
if(position==selectedPosition)
{
holder.checkImg.setImageResource(R.drawable.check);
}
else
{
holder.checkImg.setImageResource(R.drawable.uncheck);
}
return view;
}
private class ViewHolder{
private TextView textView;
private ImageView checkImg;
}
public void getFilter(String name) {
dObj.clear();
if(!name.equals("")){
for (CountryDataObject item : completeList) {
if(item.getCountryName().toLowerCase().startsWith(name.toLowerCase(),0)){
dObj.add(item);
}
}
}
else {
dObj.addAll(completeList);
}
selectedPosition=-1;
notifyDataSetChanged();
notifyDataSetInvalidated();
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
Registration reg=(Registration)context;
selectedPosition=position;
reg.setSelectedCountryCode("+"+dObj.get(position).getCountryCode());
notifyDataSetChanged();
}
}
Consider you have passed a list to your adapter.
Use:
list.getAdapter().notifyDataSetChanged()
to update your list.
After deleting data from list view, you have to call refreshDrawableState().
Here is the example:
final DatabaseHelper db = new DatabaseHelper (ActivityName.this);
db.open();
db.deleteContact(arg3);
mListView.refreshDrawableState();
db.close();
and deleteContact method in DatabaseHelper class will be somewhat looks like
public boolean deleteContact(long rowId) {
return db.delete(TABLE_NAME, BaseColumns._ID + "=" + rowId, null) > 0;
}
I was not able to get notifyDataSetChanged() to work on updating my SimpleAdapter, so instead I tried first removing all views that were attached to the parent layout using removeAllViews(), then adding the ListView, and that worked, allowing me to update the UI:
LinearLayout results = (LinearLayout)findViewById(R.id.results);
ListView lv = new ListView(this);
ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
SimpleAdapter adapter = new SimpleAdapter( this, list, R.layout.directory_row,
new String[] { "name", "dept" }, new int[] { R.id.name, R.id.dept } );
for (...) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("name", name);
map.put("dept", dept);
list.add(map);
}
lv.setAdapter(adapter);
results.removeAllViews();
results.addView(lv);
while using SimpleCursorAdapter can call changeCursor(newCursor) on the adapter.
I was the same when, in a fragment, I wanted to populate a ListView (in a single TextView) with the mac address of BLE devices scanned over some time.
What I did was this:
public class Fragment01 extends android.support.v4.app.Fragment implements ...
{
private ListView listView;
private ArrayAdapter<String> arrayAdapter_string;
...
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
...
this.listView= (ListView) super.getActivity().findViewById(R.id.fragment01_listView);
...
this.arrayAdapter_string= new ArrayAdapter<String>(super.getActivity(), R.layout.dispositivo_ble_item, R.id.fragment01_item_textView_titulo);
this.listView.setAdapter(this.arrayAdapter_string);
}
#Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord)
{
...
super.getActivity().runOnUiThread(new RefreshListView(device));
}
private class RefreshListView implements Runnable
{
private BluetoothDevice bluetoothDevice;
public RefreshListView(BluetoothDevice bluetoothDevice)
{
this.bluetoothDevice= bluetoothDevice;
}
#Override
public void run()
{
Fragment01.this.arrayAdapter_string.add(new String(bluetoothDevice.toString()));
Fragment01.this.arrayAdapter_string.notifyDataSetChanged();
}
}
Then the ListView began to dynamically populate with the mac address of the devices found.
I think it depends on what you mean by refresh. Do you mean that the GUI display should be refreshed, or do you mean that the child views should be refreshed such that you can programatically call getChildAt(int) and get the view corresponding to what is in the Adapter.
If you want the GUI display refreshed, then call notifyDataSetChanged() on the adapter. The GUI will be refreshed when next redrawn.
If you want to be able to call getChildAt(int) and get a view that reflects what is what is in the adapter, then call to layoutChildren(). This will cause the child view to be reconstructed from the adapter data.
I had an ArrayList which I wanted to display in a listview. ArrayList contained elements from mysql.
I overrided onRefresh method and in that method I used tablelayout.removeAllViews(); and then repeated the process for getting data again from the database.
But before that make sure to clear your ArrayList or whatever data structre or else new data will get appended to the old one..
If you want to update the UI listview from a service, then make the adapter static in your Main activity and do this:
#Override
public void onDestroy() {
if (MainActivity.isInFront == true) {
if (MainActivity.adapter != null) {
MainActivity.adapter.notifyDataSetChanged();
}
MainActivity.listView.setAdapter(MainActivity.adapter);
}
}
If you are going by android guide lines and you are using the ContentProviders to get data from Database and you are displaying it in the ListView using the CursorLoader and CursorAdapters ,then you all changes to the related data will automatically be reflected in the ListView.
Your getContext().getContentResolver().notifyChange(uri, null); on the cursor in the ContentProvider will be enough to reflect the changes .No need for the extra work around.
But when you are not using these all then you need to tell the adapter when the dataset is changing. Also you need to re-populate / reload your dataset (say list) and then you need to call notifyDataSetChanged() on the adapter.
notifyDataSetChanged()wont work if there is no the changes in the datset.
Here is the comment above the method in docs-
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
I was only able to get notifyDataSetChanged only by getting new adapter data, then resetting the adapter for the list view, then making the call like so:
expandableAdapter = baseFragmentParent.setupEXLVAdapter();
baseFragmentParent.setAdapter(expandableAdapter);
expandableAdapter.notifyDataSetChanged();
on other option is onWindowFocusChanged method, but sure its sensitive and needs some extra coding for whom is interested
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
// some controls needed
programList = usersDBHelper.readProgram(model.title!!)
notesAdapter = DailyAdapter(this, programList)
notesAdapter.notifyDataSetChanged()
listview_act_daily.adapter = notesAdapter
}
If I talked about my scenario here, non of above answers will not worked because I had activity that show list of db values along with a delete button and when a delete button is pressed, I wanted to delete that item from the list.
The cool thing was, I did not used recycler view but a simple list view and that list view initialized in the adapter class. So, calling the notifyDataSetChanged() will not do anything inside the adapter class and even in the activity class where adapter object is initialized because delete method was in the adapter class.
So, the solution was to remove the object from the adapter in the adapter class getView method(to only delete that specific object but if you want to delete all, call clear()).
To you to get some idea, what was my code look like,
public class WordAdapter extends ArrayAdapter<Word> {
Context context;
public WordAdapter(Activity context, ArrayList<Word> words) {}
//.......
#NonNull
#Override
public View getView(final int position, View convertView, ViewGroup group) {
//.......
ImageButton deleteBt = listItemView.findViewById(R.id.word_delete_bt);
deleteBt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (vocabDb.deleteWord(currentWord.id)) {
//.....
} else{
//.....
}
remove(getItem(position)); // <---- here is the trick ---<
//clear() // if you want to clear everything
}
});
//....
Note: here remove() and getItem() methods are inherit from the Adapter class.
remove() - to remove the specific item that is clicked
getItem(position) - is to get the item(here, thats my Word object
that I have added to the list) from the clicked position.
This is how I set the adapter to the listview in the activity class,
ArrayList<Word> wordList = new ArrayList();
WordAdapter adapter = new WordAdapter(this, wordList);
ListView list_view = (ListView) findViewById(R.id.activity_view_words);
list_view.setAdapter(adapter);
After adding/deleting dynamic data in your "dataArray" do:
if you use an ArrayAdapter
adapter.notifyDataSetChanged();
if you use a customAdapter that extends ArrayAdapter
adapter.clear();
adapter.addAll(dataArray);
adapter.notifyDataSetChanged();
if you use a customAdapter that extends BaseAdapter
adapter.clear();
adapter.getData().addAll(dataArray);
adapter.getData().notifyDataSetChanged();
The easiest is to just make a new Adaper and drop the old one:
myListView.setAdapter(new MyListAdapter(...));

Categories

Resources