In our chat app we want to use cool new Library SQLBrite to update chat on database changes. Since our chat has endless scrolling, and chat room can have very big list of messages, we want to split ArrayList supplied to Chat ListView adapter into two lists. Check graphic for the idea.
We want to set point in database above which, old messages will be queried by normal SQLite queries. And below that point we want set SQLBrite, that will bring us fresh messages added to database.
Each part should populate its corresponding ArrayList. And two arrayLists should be combined in one adapter.
My question is it possible to do? If yes how we can combine and handle two dynamic ArrayLists in single adapter?
Edit 1
1. I need to keep chat scroll position during from resetting, and no flickers during ArrayLists update.
1.With the help of generics you can handle two arraylist with single ArrayList.
For example in adapter :
setListData(ArrayList<T> pListData)
{
mListData=pListData;
}
In View
getView(int position, View convertView, ViewGroup parent){
T commonModel= getItem(position);
if(T instanceof ArrayListOneModel){
ArrayListOneModel model1=(ArrayListOneModel)T;
do stuf for first arraylit...
}
}
If you are using same model you can set a type (enum ) for both arraylist
& during showing time you can check that.
3.Otherwise you can first add old data in arraylist & then using collection
addAll() add 2nd latest message list in it. then
adapter.notifyDataSetChanged()
will set first old message then will set latest message in your list
More Clarification:
In second approach if you have different models for both arraylist then contain an enum in both model as a setter getter.
public enum eType{
FIRST_LIST_TYPE,SECOND_LIST_TYPE
}
During Fetching data from different DB's set Type in model.
e.g
public class model{
private enum eType;
// other setter getter value from your DB
/**
* Setter getter:
*/
public void seteType(enum eType)
{
this.eType = eType;
}
public enum geteType()
{
return eType;
}
}
During fetching data set Type e.g.
Model model = new Model();
model.seteType(eType.FIRST_LIST_TYPE) ;
//same for 2nd db.
& simply check type inside getView() according to your requirement.
yes that is possible inside BaseAdapter getCount method write following code
#Override
public int getItemCount() {
return list1.size()+list2.size();
}
and inside getView method you can do something like below
public View getView(int position, View convertView, ViewGroup viewGroup) {
if(position < list1.size) {
Object object = list1.get(position);
//write code to inflate view here related to list 1
}
else {
Object object = list2.get(position - list1.size());
//write code to inflate raw here related to list 2
}
}
You can pass only one list in adopter, which means you have to merge both arrays.
In order to merge both array. they have to be of same type, i.e. Array of same custom object.
If arrays are updating dynamically, then merge arrays again, as their data changes, and call notifyDataSetChanged() each time, to reflect changes in listview
Yes you can do it. but both arraylist should have common data format.
for eg ..
In adapter you can make method like
public void addMessages( <your_array_list> data ) {
list.addAll(data); //where list is your data container
}
now you may have two arraylist
like
ArrayList<your_type> oldMsg;
ArrayList<your_type> newMsg;
..
..
...
.
.
so you can call adapter method which we have created
yourAdapter.addMessages(oldMsg);
yourAdapter.addMessages(newMsg);
Related
I am using listview in my app.I am adding items to list with this line:
conversationsAdapter.add(user);
and this initializes list
conversationsAdapter=new ArrayAdapter<JsonObject>(this,0) {
#Override
public View getView(int c_position,View c_convertView,ViewGroup c_parent) {
if (c_convertView == null) {
c_convertView=getLayoutInflater().inflate(R.layout.random_bars,null);
}
JsonObject user=getItem(c_position);
String name=user.get("name").getAsString();
String image_url="http://domain.com/photos/profile/thumb/"+user.get("photo").getAsString();
TextView nameView=(TextView)c_convertView.findViewById(R.id.tweet);
nameView.setText(name);
ImageView imageView=(ImageView)c_convertView.findViewById(R.id.image);
Ion.with(imageView)
.placeholder(R.drawable.twitter)
.load(image_url);
return c_convertView;
}
};
ListView conversationsListView = (ListView)findViewById(R.id.conversationList);
conversationsListView.setAdapter(conversationsAdapter);
conversationsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
startChat(conversationsAdapter.getItem(position));
}
});
My list view is looking like this:
I want to update an item in the list.How can I do this ?
Example:We can write a method like: changeName when this method calls,method sets name "Tolgay Toklar" to "Tolgay Toklar Test" so I want to update custom listview item attributes.
I totally disagree with tyczj. You never want to externally modify an ArrayAdapter's list and yes it's possible to update just an individual item. Lets start with updating an individual item.
You can just invoke getItem() and directly modify the object and call notifyDataSetChanged(). Example:
JSONObject object = conversationAdapter.getItem(position);
object.put("name", data);
conversationAdapter.notifyDataSetChanged();
Why does this work? Because the adapter will feed you the same object reference used internally, allowing you to modify it and update the adapter. No problem. Of course, I'd recommend instead building your own custom adapter to perform this directly on the adapter's internal list. As an alternative, I highly recommend using the ArrayBaseAdapter instead. It already provides that ability for you while fixing some other major bugs with Android's ArrayAdapter.
So why is tyczj wrong about modifying the external list? Simple. There's no guarantee that your external list is the same as the adapters. Once you perform a filter on the ArrayAdapter, your external list and the adapters are no longer the same. You can get into a dangerous scenario where (for example) index 5 no longer represents position 5 in the adapter because you later added an item to the adapter. I suggest reading Problems with ArrayAdapter's Constructors for a little more insight.
Update: How External List Fails
Lets say you create a List of objects to pass into an ArrayAdapter. Eg:
List<Data> mList = new ArrayList<Data>();
//...Load list with data
ArrayAdapter<Data> adapter = new ArrayAdapter<Data>(context, resource, mList);
mListView.setAdapter(adapter);
So far so good. You have your external list, you have an adapter instantiated with it and assigned to listview. Now lets say at some later point, the adapter is filtered and cleared.
adapter.filter("test");
//...later cleared
adapter.filter("");
Now at this point mList is NOT the same as the adapter. So if the adapter is modified:
adapter.add(newDataObject);
You'll find that mList does not contain that new data object. Hence why external lists like this can be dangerous as the filter creates a NEW ArrayList instance. It won't continue to use your mList referenced one. You could even try adding items to mList at this point and it won't be reflected in the adapter.
If you change the data in your list you need to call notifyDatasetCanged on the adapter to notify the list that the underlying data has changed needs to be updated and.
Example
List<MyData> data = new ArrayList<MyData>();
private void changeUserName(String name){
//find the one you need to change from the list here
.
.
.
data.set(myUpdatedData);
notifyDatasetChanged()
}
I have a Listview and a custom adapter, I am trying to change swap two arraylist items inside the apdapter in onclick of button as listview item.
My code is
viewHolder.btnUp.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Integer pos = (Integer) v.getTag();
if (!pojos.get(pos).equals(pojos.get(0))) {
Collections.swap(pojos, pos, pos - 1);
notifyDataSetChanged();
}
}
});
But I can not see the changes in listview, though arraylist has modified, but UI changes has not reflected.
Thats because your adapter has already finished its work. The adapter will turn the data into views and pass those views to the list view. Notice that changing the order in the original collection wont change the views inside the list view. What you could do is remove the views and add them at the correct positions. Get access to the list view by doing viewHolder.getParent()
If pojos is a local final variable, make sure the one the adaptor uses still points to the same collection otherwise the anonymous class will swap 2 elements in an collection that's not being used.
I would recommend to pass the arrayList to the adapter again and setting adapter to the list view, all this before the notifyDataSetChanged(); method.
I have a list of polymorphic entities, for example List<Person> where a person can be employee, manager, soldier, royalty, etc... and I want to display the person list in an android list-view where each type of person has different row template and somewhat different data.
What is the proper way to display the different rows of different types?
Two bad(?) solutions:
Put an abstract getView method inside the Person class, so that each different person will create the correct view via polymorphism. --> This will mix the model and the view of MVC - ie. if I will want to present the list in a different screen (for example a table view) then it will break.
Put the model-to-view logic inside the adapter (inherited from ArrayAdapter<Person>). For example inside getView I can check the getClass() of the person and fill the correct view accordingly. --> This method will end up with long chain of if-else statements, one for each person type, and will need to be extended when new types of persons are added - usually a smell of bad code in object oriented point of view. This is the solution suggested in "How to create list with different elements and actions?"
Is there a solution better than (2) above?
I'd personally go for something similar to your second solution. I'd probably include an abstract method in the Person class to identify the type of person, so you can use that instead of checking with getClass() on the Person object in the getView() call.
In your custom adapter, extend BaseAdapter and be sure that getViewTypeCount() returns the number of different kinds of persons you have in your data set and that getItemViewType(int position) returns the correct identifier for the position in your data set.
This question has a lot of useful information on the subject:
getViewTypeCount and getItemViewType methods of ArrayAdapter
create your own ListAdapter which extends from BaseAdapter like this
public class YourListAdapter extends BaseAdapter implements Filterable {
...
}
override the getViewTypeCount() and getItemViewType()
#Override
public int getViewTypeCount() {
return 2;//count of types
}
#Override
public int getItemViewType(int position) {
return YourPersonList.get(position).getMyViewType();
}
and in the getView(..)
int type = getItemViewType(position);
switch(type){
case 0:
//set the view to your layout
break;
case 1:
//set the view to another layout
break;
}
more or less something like that..
//EDIT: look at the post from michell https://stackoverflow.com/a/17152430/1847899
There are two methods you can override in an adapter that make this easier. They are:
#Override
public int getViewTypeCount(){
return viewTypeCount;
}
#Override
public int getItemViewType(int pos){
if (getItem(pos).type == employee)
return VIEW_TYPE_EMPLOYEE;
else if (getItem(pos).type == manager)
return VIEW_TYPE_MANAGER;
}
Using the above methods, you still need to do some work, for instance when the view needs to be inflated you may have to call getItemViewType(position) from inside getView, but when you get a view that has been recycled, the adapter should give you the proper type based on the row position.
When I grab info from a database (I am using MySQL), I'd like to also grab the id of the row and somehow assign it to each row of the 'listView`.
For example, let's say there is a table called fruit. fruit_id of 16 is orange. When the listView displays the list of fruit, and user clicks on a row that shows orange, i'd like to be able to access the fruit_id(16) of that row. But I'm not sure where to "hide" it.
Doing some initial research it seems there are multiple ways one can do this. The simplest might be something with using a tag, is this the best way? if so, how can you assign an id to it?
Create a class named Fruit.
class Fruit {
private int fruit_id;
private String fruit_name;
// Constructors
// Getters and Setters
}
Use an ArrayAdapter<Fruit> as the ListAdapter for your ListView. Then at ListView's onItemClickListener, get the Fruit object and get its id.
If you're using an ArrayAdapter to back your ListView, then #jaibatrik's suggestion is definitely a good one. However, if you're using a CursorAdapter, it's probably easier to exploit the return value of getItemId().
By default, a CursorAdapter will look for a column with the name "_id" in the Cursor you supply it and return that as id value whenever you click an item:
onItemClick(AdapterView<?> parent, View view, int position, long id)
That last value will contain the id of your cursor item. You can also override this behaviour and have it return any other unique value you may have access to in the Cursor.
The same is also true for an ArrayAdapter: by default it will return the position of the item in the array as unique id. However, you could easily make it return fruit_id for every item by overriding that method. Then it'll be passed in the onItemClick(...) directly, which saves you retrieving it (again) in there.
My questions is, if I grab, for example, item_id (not just item),
where do I put item_id in the listView rows (on Android side)?
The beauty of having objects that represent the data you're visualising in the list, is that you already have all the ingredients to make it work. Let's take the Fruit example given by #jaibatrik and add one getter for the sake of this example:
class Fruit {
private int fruit_id;
private String fruit_name;
// Constructors
// Getters and Setters
public int getId() { return fruit_id; }
}
In the comments you're describing you retrieve the fruit data from the database and populate it in a list:
List<Fruit> fruits = ...
That list should be the dataset backing your ArrayAdapter. To be more specific, since it's a typed class, you should have an ArrayAdapter<Fruit> instance that you set as adapter to the ListView.
Now, assuming you have an OnItemClickListener set against the ListView, it will fire whenever the user taps on an item. Using the parameters passed into the callback, you can retrieve the item that is associated with the position that was selected:
#Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fruit fruit = (Fruit) parent.getItemAtPosition(position);
int fruit_id = fruit.getId();
...
}
With the object retrieved, you can do anything you like with the data it holds. No need to explicitly set the id against the row views, since it should already be part of the dataset that backs the ListView.
I have a ListView which is populated from a string-array resource. I'm trying to figure out how I can use a key-value type of structure, so that the key is displayed in the ListView, and when a row is selected I can get the value.
I know I could maintain a separate array for the values and do a lookup using the selected index of the row but this is hard to maintain.
Also I think I could make a POJO and build the list with those objects with my own custom list adaptor, but I want to just use a simple xml file if possible.
I'm reading the documentation to see if I can do this in xml but I've not found anything like this yet?
Thanks for you time.
You might want to use the Map that is ordered such as TreeMap or LinkedHashMap and pass it to your adapter.
This would enable what you are trying to achieve.
You basically need to have an adapter that utilize the Key-Value pair data structure (Map) and set it in your list view, something like:
public class OrderedMapAdapter extends BaseAdapter {
Map.Entry[] entries;
/***
* Pass the key value pair map using an ordered map data structure
* #param orderedMap An ordered map, either LinkedHashMap or TreeMap
*/
public OrderedMapAdapter(LinkedHashMap orderedMap) {
this.entries = orderedMap.entrySet().toArray(new Map.Entry[0]);
}
public int getCount() {
return entries.length;
}
/***
* This should get you the position you want in Key-value pair format
* that you could use whenever the row is selected
*/
public Map.Entry getItem(int position) {
return entries[position];
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
// display your view here
return null;
}
}
I believe your second suggestion using a custom list adapter is going to be the easiest solution for what you want to do.
The SimpleAdapter drives the XML file approach you mentioned and is quite particular on the format of data it receives. If you can restructure the format of your input data to match, the SimpleAdapter will work fine.
Otherwise use the ArrayAdapter and a POJO, and build the list required for your implementation.
I have just created a bunch of POJOs and used a custom list adapter to display the relevant data from the Object in the List Row.