Consider folowing code:
public class MediaItemAdapter extends BaseAdapter {
final List<Row> rows;
public MediaItemAdapter(Activity activity, ArrayList<HashMap<String, String>> data) {
Log.d("mediaadapter", "listcreation: " + data.size());
rows = new ArrayList<Row>();// member variable
int i=0;
for (HashMap<String, String> addvert:data) {
rows.add(new MeadiaRow((LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE), addvert));
Log.d("mediaadapter", addvert.get("title"));
if (i % 20 == 0) {
rows.add(new SourseRow((LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE)));
}
i++;
}
}
It is constructor of my adapter. After each 20 MediaRows, I wish to add one sourse row. It has ArrayList data. The problem that data is always empty. I build my adapter according to this tutorial. In a tutorial thingth works fine. But in tutorial initial dataset is predefined. In my cade initial data set is empty and populates in working time. However, after I call notifyDataSetChanged the data remains empty (even though the dataset is not).
How can I get updated values for data?
What is nesessary to do - is to move
int i=0;
for (HashMap<String, String> addvert:data) {
rows.add(new MeadiaRow((LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE), addvert));
Log.d("mediaadapter", addvert.get("title"));
if (i % 20 == 0) {
rows.add(new SourseRow((LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE)));
}
i++;
}
from adapter to overriden notifyDataSetChanged in adapter class
Related
I have multiple RecyclerViews using common RecyclerView.Adapter. Is there is some way to tell RecyclerView to get data from adapter using some offset? Something like 1st view gets 0-15 items from adapter, 2nd view gets 15-30 and so on.
Give the position and and the Object List to the Fragment inside your viewpager at FragmentStatePagerAdapter :
#Override
public Fragment getItem(int position) {
return YourFragment.newInstance(position,yourListOfObjects);
}
Then inside your YourFragment according to position take 15 items from the List and make a adpater, example with RxJava, but you can modify it, to work with regular Java:
int positionOfFragment;
List<Product> list;
Observable
.just(list)
.take(positionOfFragment*15)
.subscribe(new Action1<List<Product>>() {
#Override
public void call(List<Product> orders) {
ProductAdapter adapter = new ProductAdapter(orders, getActivity());
//setAdapter to RecyclerView;
}
});
You mean something like ExpandableListView?
EDITED:
Try this.
Create separate data structure for filtered data at required positions
private List<Object> data;
private List<Object> dataToShow;
public CustomAdapter(List<Object> data){
this.data = data;
this.dataToShow = new ArrayList<>(data);
}
...in all overriden methods use dataToShow field
create method to filter data
public void showDataAtPositions(int startPos, int endPos){
dataToShow.clear();
for(int index = 0; index < data.size(); index++){
if(index >=startPos && index <= endPos){
dataToShow.add(data.get(index));
}
}
notifyDataSetChanged();
}
then just call this method
adapter.showDataAtPositions(0, 15);
adapter.showDataAtPositions(16, 30);
this should work
My application is returning the latest data from firebase to the buttom of the ListView. But I want it to be on the top! I have thought about it and I think there is only two possible ways to do it.
1. Invert the Listview.
I think that this way is how it should be done but I couldn't figure it out. I have searched a lot on the web but no suitable solution for my case
This is my adapter code
public void onStart() {
super.onStart();
// Setup our view and list adapter. Ensure it scrolls to the bottom as data changes
final ListView listView = getListView();
// Tell our list adapter that we only want 50 messages at a time
mChatListAdapter = new ChatListAdapter(mFirebaseRef.limit(50), this, R.layout.chat_message, mUsername);
listView.setAdapter(mChatListAdapter);
}
And this is the code for the ChatListAdapter constructor for a custom list class ChatListAdapter which extends special list adapter class FirebaseListAdapter:
public ChatListAdapter(Query ref, Activity activity, int layout, String mUsername) {
super(ref, Chat.class, layout, activity);
this.mUsername = mUsername;
}
[Edit] This is some of the code for FirebaseListAdapter which extends BaseAdapter class
public FirebaseListAdapter(Query mRef, Class<T> mModelClass, int mLayout, Activity activity) {
this.mRef = mRef;
this.mModelClass = mModelClass;
this.mLayout = mLayout;
mInflater = activity.getLayoutInflater();
mModels = new ArrayList<T>();
mModelKeys = new HashMap<String, T>();
// Look for all child events. We will then map them to our own internal ArrayList, which backs ListView
mListener = this.mRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
T model = dataSnapshot.getValue(FirebaseListAdapter.this.mModelClass);
mModelKeys.put(dataSnapshot.getKey(), model);
// Insert into the correct location, based on previousChildName
if (previousChildName == null) {
mModels.add(0, model);
} else {
T previousModel = mModelKeys.get(previousChildName);
int previousIndex = mModels.indexOf(previousModel);
int nextIndex = previousIndex + 1;
if (nextIndex == mModels.size()) {
mModels.add(model);
} else {
mModels.add(nextIndex, model);
}
}
}
2. Descending query the data.
The second way seams impossible to me, because when I searched on Firebase API documentation and on the web, I couldn't find anyway to order retraived data on descending way.
My data on firebase look like the following:
glaring-fire-9714
chat
-Jdo7-l9_KBUjXF-U4_c
author: Ahmed
message: Hello World
-Jdo71zU5qsL5rcvBzRl
author: Osama
message: Hi!
Thank you.
A simple solution would be to manually move the newly added data to the top of the listview. As you rightly noticed, new data added to a listview will automatically be appended to the bottom of the list, but you may freely move entries once they are added. Something like the following would help you manually move the newest entry to the top of the list:
int iSwapCount = listView.getCount() - 1;
int iPosition = listView.getCount() - 1;
for (int j = 0; j < iSwapCount; j++)
{
Collections.swap(yourlistobject, iPosition, iPosition - 1);
iPosition = iPosition - 1;
}
The above code will begin by calculating the number of swaps that will be required to move last list entry to the top of the list, which is determined by the number of elements in the list - 1. The same is true for calculating the last position in the list. From there Collections.swap will be used to swap the last element in the list with the element before it; this will be repeated until the last element is now the first element, with the rest of the entries in the list remaining in the same order. This code would have to be called each time a new entry is added so that the overall order of the list is maintained.
I realize it has been a while since you asked but I had the same issue. It does not appear that there is a direct answer here.
Here's the change to the firebase adapter to get new items on the top of the list.
Notice the change from add(...) to add(0,...) and add(next...) to add(prev...)
Look for comments:
// prepend instead append
Example:
...
// Insert into the correct location, based on previousChildName
if (previousChildName == null) {
mModels.add(0, model);
mKeys.add(0, key);
} else {
int previousIndex = mKeys.indexOf(previousChildName);
int nextIndex = previousIndex + 1;
if (nextIndex == mModels.size()) {
//mModels.add(model);
//mKeys.add(key);
// prepend instead append
mModels.add(0,model);
mKeys.add(0,key);
} else {
//mModels.add(nextIndex, model);
//mKeys.add(nextIndex, key);
// prepend instead append
mModels.add(previousIndex, model);
mKeys.add(previousIndex, key);
}
}
...
Here is a simple way to invert a FirebaseUI list using a RecyclerView:
boolean reverseList = true;
LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, reverseList);
if (reverseList) {
manager.setStackFromEnd(true);
}
mRecyclerView.setLayoutManager(manager);
I am working on a XMPP based chat in android.. and I am struck at a point where I need to update the position of an item in the listview to the top in case a new.message arrives.
The use case is.. I am on Contacts screen of the app and a new message comes.. so this contact should move to top of the list and get bold. This is what is similar to whatsapp as well
How can this be done. My class imolemebts activity and i have implemented custom list adapter.
So howcan I find if an item exists in the listview and secondly how to dynamically change position
First, keep in mind that a ListView is just a representation of a list of Objects. So if you want to know if an item is in the ListView, you just have to check if the corresponding Object is in your list of Objects.
Is the same when you want to change the position of one item, you have to change the position of the Object in the list.
Start by defining these objects:
private ArrayList<MyObject> lists = new ArrayList<MyObject>();
private MyCustomAdapter myAdapter;
The first time you create your ListView, just do as usually:
//fill your list with your objects
lists.add(myObject1);
lists.add(myObject2);
lists.add(myObject3);
//create and set the adapter
myAdapter = new MyCustomAdapter(..., ..., lists);
myListView.setAdapter(myAdapter);
Now you can know if your lists contains a specific object (which is the same that checking if an item is in your ListView) by simply testing that:
lists.contains(anObject);
Then, if you want to change the position of a specific item in the ListView, you have to create a new list and put the elements in the correct order. You can use something like that (not tested but it should work):
private ArrayList<MyObject> moveItemToTop(ArrayList<MyObject> lists, int positionOfItem) {
if (lists == null || positionOfItem < 0 || positionOfItem >= lists.size()) {
return lists;
}
ArrayList<MyObject> sortedList = new ArrayList<MyObject>();
//add the item to the top
sortedList.add(lists.get(positionOfItem));
for (int i=0; i<lists.size(); i++) {
if (i != positionOfItem) {
sortedList.add(lists.get(i));
}
}
return sortedList;
}
Or even this (which is way easier...).
Finally, call these two methods to update your ListView:
myAdapter = new MyCustomAdapter(..., ..., moveItemToTop(lists, itemPosition));
myListView.setAdapter(myAdapter);
This is how I resolved it
private void moveMessageToTop(MessageObject message) {
int index = 0;
for (Friends friend : mFriends) {
if (friend.getName().equalsIgnoreCase(message.getFrom().split("#")[0])) {
index = mFriends.indexOf(friend);
break;
}
}
if (index != 0) {
mFriends.add(0,new Friends(message.getFrom().split("#")[0], message
.getMessage()));
} else {
Friends frnd = mFriends.get(index);
frnd.setStatus(message.getMessage());
mFriends.add(0, frnd);
mFriends.remove(index);
}
((ListAdapter) lvFriends.getAdapter()).notifyDataSetChanged();
}
I am following a great coding example over here: This SO question. It is regarding implementing a SectionIndexer interface to an array adapter.
However, how would you do the same thing if your ArrayAdapter is passing an ArrayList< MyObject > not an ArrayList< String >?
For example, this is where my code is different then his code. He has:
class AlphabeticalAdapter extends ArrayAdapter<String> implements SectionIndexer {
private HashMap<String, Integer> alphaIndexer;
private String[] sections;
public AlphabeticalAdapter(Context c, int resource, List<String> data) {
alphaIndexer = new HashMap<String, Integer>();
for (int i = 0; i < data.size(); i++) {
String s = data.get(i).substring(0, 1).toUpperCase();
alphaIndexer.put(s, i);
}
// other stuff
}
I am having problems adapting that for loop to my situation. I can't measure the size like he does. Where he has the above, my adapter begins with.
public class CustomAdapter extends ArrayAdapter<Items> implements
SectionIndexer {
public ItemAdapter(Context context, Items[] objects) {
Where he is passing one ArrayList, I have to pass in three, but to make that happen, had to wrap in a custom object class. One of the ArrayLists that I want to sort is one of three fields in the class called "name". It is a string obviously.
I want to scroll through that alphabetically with SectionIndex based on that name field. How do I change the example code from the other question to work in this scenario?
Where he has "data.size()", I need something like "name.size()" - I think?
Where he is passing one ArrayList, I have to pass in three, but to
make that happen, had to wrap in a custom object class. One of the
ArrayLists that I want to sort is one of three fields in the class
called "name".
You don't have three ArrayLists, you have an ArrayList of custom objects that were built from three ArrayLists(so the size is the size of the List that you pass to the adapter). From this point of view the only change in your code is to use the name from that custom object Items to build the sections:
for (int i = 0; i < data.size(); i++) {
String s = data.get(i).name.substring(0, 1).toUpperCase();
if (!alphaIndexer.containsKey(s)) {
alphaIndexer.put(s, i);
}
}
// ...
There aren't other changes. Also you may need to sort the List of Items that you pass to the adapter using:
Collections.sort(mData);
where your Items class must implement the Comparable<Items> interface:
class Items implements Comparable<Items> {
String name;
// ... rest of the code
#Override
public int compareTo(Items another) {
// I assume that you want to sort the data after the name field of the Items class
return name.compareToIgnoreCase(another.name);
}
}
Overview:
I have an Expandable List View which displays items of the type ToDoElement.
Each ToDoElement item has one String for the Group-Element and 1-3 Strings for the Child-Elements
My custom List Adapter expListAdapter gets the informations about the group- and child-items with the methods createGroupList() and createChildList().
In the Method createChildList() i check how many Strings for the child-items are in the ToDoElement and create the child-items for the Adapter.
At the end I can sort my list by comparing the Strings for the group-items.
All of this works fine, but here is my Problem:
After the items in the List View are sorted correctly, the number of child-items is wrong.
That's because my List Adapter doesn't know that the number of child-items has changed. So some child-items aren't displayed and other child-items are just empty, because there is no data for them.
My Suggestion:
I know that somehow the methods createGroupList() and createChildList() have to be called again from my Adapter, but i don't know how to do it. I've tried expListAdapter.notifyDataSetInvalidated(), because somebody told me that this method would call createGroupList() and createChildList() again, but is doesn't work.
Code Fragments:
Definition of my List Adapter:
expListAdapter = new MyListAdapter(this, createGroupList(), createGroupList());
setListAdapter( expListAdapter );
createGroupList() and createChildList():
private List createGroupList() {
ArrayList result = new ArrayList();
for (int i=0; i < Liste.AnzahlElemente(); i++) { //
result.add(Liste.getSize(i).getGroupString());
}
return result;
}
private List createChildList() {
ArrayList result = new ArrayList();
for(int i=0; i < Liste.getSize(); i++) {
ArrayList secList = new ArrayList();
for( int n = 1 ; n <= 3 ; n++ ) {
if (Liste.getElement(i).getChildString(n).length() != 0){
secList.add( "- " + Liste.getElement(i).getChildString(n));
}
}
result.add( secList );
}
return result;
}
Sorting the List:
(Liste is an ToDoListe object, which manages the ToDoElements in an Array List)
Collections.sort(Liste.getListe(), new NameComparator());
expListAdapter.notifyDataSetInvalidated();
//expListAdapter.notifyDataSetChanged(); //same effect as notifyDataSetInvalided
my NameComparator:
public class NameComparator implements Comparator<ToDoElement>{
public int compare(ToDoElement item1, ToDoElement item2) {
return item1.getGroupString().compareTo(item2.getGroupString());
}
}
Thanks a lot for your help!!
I solved my own problem!
I wrote an Update method for the my custom List Adapter.
It looks like this:
public class MyListAdapter extends BaseExpandableListAdapter{
private ArrayList<String> ueberschriften;
private ArrayList<ArrayList<String>> stichpunkte;
public MyListAdapter(Context context, List _ueberschriften, List _stichpunkte) {
this.ueberschriften = (ArrayList)_ueberschriften;
this.stichpunkte = (ArrayList)_stichpunkte;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
// [...] some other methods
public void update(List _GroupStrings, List _ChildStrings) {
GroupStrings = (ArrayList<String>) _GroupStrings;
ChildStrings = (ArrayList<ArrayList<String>>) _ChildStrings;
}
}
Sorting the list in my activity looks like this:
Collections.sort(Liste.getListe(), new NameComparator());
expListAdapter.update(createGroupList(), createChildList());
expListAdapter.notifyDataSetChanged();
now the number of child-items also updates correctly!
cheers!
The list in your Adapter and the list displayed are separate lists.
In case the adapter is filled with new data or data is rearanged, you need to inform the view with notifyDataSetChanged() - this will trigger the redrawing of the view.
notifyDataSetInvalidate() should only be called in case your data is not available any more.
As described here:
Android ListView Adapter notifyDataSetInvalidated() vs notifyDataSetChanged()