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.
Related
I have implemented my RecyclerView and even added an onscrolllistener to support infinity scrolling and now I'm stuck with a, hopefully, easy problem: How can I add the newly loaded data to the existing dataset?
My current approach: I create a new array with the length of the existing dataset + the length of the newly loaded data. I System.arraycopy my existing dataset and add the new content with a for-loop.
This works but the list is always reset (scrolls back to the top) and I assume my way to add additional content is overly complicated/wrong, though the tutorials I have looked at seem to pass over this "detail".
Update: I'm currently calling "scrollToPosition" on the UI-Thead after the data has been loaded, but I doubt this is the correct way of doing this or am I wrong?
You shouldn't be adding stuff to your dataset, you will sooner or later run out of memory. What you can do is return a big number (I used Short.MAX_VALUE) item in getItemCount inside your adapter and in the method that requests a view for postion you should do position % list.size();
It is not a truly endless RecyclerView this way, but good enough. I will paste some code tomorrow, I don't have it here now :/
I think you have to add items inside your adapter. Let`s say
class Adapter extends Recycler.Adapter<Recycler.ViewHolder>{
List<YourCustomObject> list;
public Adapter(){
list = new ArrayList<>();
}
public void addItem(YourCustomObject item){
list.add(item);
notifyItemDateSetChanged(); //This method for adapter to notice that list size have been changed
}
// Here your views
}
There is implementation of Your fragment or Activity where you retrieve data from internet.Let` say
class MainActivity extends AppCompactActivity{
Adapter adapter = new Adapter();
List<YourCustomObjects> objects;
public void onCreateView(){
//////// Something yours
}
public void onLoadMore(){
///// Your operation to retrieve data and init it to your list objects
for(YourCustomObject object : objects){
adapter.addItem(object);
}
}
}
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);
I have an ActionBarActivity with a GridView.
The GridView has 2 columns in portrait and 3 columns in landscape.
When I select items in portrait (starting my ActionMode) and then rotate the device, the selected item highlighting shifts one item to the left. For example, if I select the second item and rotate, the first item will be highlighted. If I select the first item and rotate, no items are highlighted.
The actual selection in the code is correct, just the highlighting is wrong.
I notice it does not do this if I keep the numColumns the same for portrait and landscape.
I believe this issue started occurring after I changed my activity to an ActionBarActivity so it could be a bug..
Anyone know why or how to fix it?
I had a similar scenario and ended up solving the issue be creating a custom grid item with a boolean field to keep track of whether the item is selected or not and then highlighting the item appropriately through the custom adapter. Below is a rough outline of what I did:
(1) I created a custom grid item with a boolean field, which we will call selectedStatus for simplicity's sake. I also added the corresponding methods to my grid item class to get the selected status:
public boolean getSelectedStatus ()
{
return selectedStatus;
}
public void setSelectedStatus (boolean paramSelectedStatus)
{
this.selectedStatus = paramSelectedStatus;
}
(2) I then created a custom Adapter that extends BaseAdapter to handle the custom grid object I created. In this Adapter I check the if the selected status of the grid object is true or false and highlight the item accordingly, shown below:
#Override
public View getView (final int position, View convertView, ViewGroup parent)
{
// rest of getView() code...
if (!yourGridObject.getSelectedStatus())
{
convertView.setBackgroundColor(Color.TRANSPARENT);
}
else
{
convertView.setBackgroundColor(Color.LTGRAY);
}
// rest of getView() code...
return convertView;
}
(3) Lastly, you add the onItemClickListener to set the selected status and the background color of the grid items when they are selected (clicked):
yourGridView.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
YourGridObject yourGridObject = (YourGridObject) parent.getItemAtPosition(position);
if (!yourGridObject.getSelected())
{
view.setBackgroundColor(Color.LTGRAY);
yourGridObject.setSelected(true);
}
else
{
view.setBackgroundColor(Color.TRANSPARENT);
yourGridObject.setSelected(false);
}
}
});
Implementing selection this way ensures that the highlighting (selection) of the grid items will not change when the number of columns and rows swap since the selection status is contained within the grid objects themselves.
You don't need to manually handle selection of items as suggested by Willis. Android fully supports what you are asking. I will assume you are using an ArrayAdapter however this answer would apply to all adapters. Note some adapters (like CursorAdapter) won't suffer from your posted problem and don't require the following solution because it's already doing it internally.
The problem is solved in two parts. One, the adapter must enable stable Ids. Two, your adapter must actually return stable ids. You will need to extend the ArrayAdapter or which ever adapter you are using. Then ensure you have defined the following methods as shown below.
private class MyAdapter extends ArrayAdapter<YourObjects> {
#Override
public boolean hasStableIds() {
return true;
}
#Override
public long getItemId(int position) {
//Return a unique and stable id for the given position
//While unique, Returning the position number does not count as stable.
//For example:
return getItem(position).methodThatReturnsUniqueValue();
}
}
Most adapters do not enable hasStableIds. It's primarily only used when enabling a choiceMode. Which I assume you are doing here. By returning true, you are essentially telling Android to keep track of activated (highlighted) items based on their ID value instead of their position number.
Even with stable Ids enabled, you have to actually return an ID that is unique and stable across positional changes. Since most adapters do NOT enable stable IDs, they usually only return the position number as the stable id. Technically, if an item's position never changes over time then the position number "could" be used as the stable id. However, the safest way to return a stable/unique ID is to have one assigned to the class object being stored in the adapter and pull from that.
I created an expandable listview based on this link. Its just working fine. Now what i want is
1) How to make a childview to link another sub-child view
2) The sub- child view should be open as a new list view on the window(Right side of the view) is my expected layout. I googled but couldn't find how to achieve this. Please help me in achieving this. Thanks in advance.
You'll need to specify and handle onClick event of ListView row items.
Then you'll open a new Activity, based on the item clicked.
Parameters for new activity are supplied through intent extras, the new activity can use these values to get data from cloud or process the values to show certain results.
I've used CustomAdapter class several times to handle this scenario.
class Ocl implements OnClickListener{
#Override
public void onClick(View v) {
Intent intDetail = new Intent(getActivity(), PartDetail.class);
intDetail.putExtra(_ID, mParts[position].getSPr());
intDetail.putExtra(_LOT, mParts[position].getLotID());
intDetail.putExtra(_QTY, mParts[position].getQty());
intDetail.putExtra(_UID, mParts[position].getPartID());
startActivity(intDetail);
}
}
So, do you want your first child to expand into another ListView? Or maybe just open another Activity/Fragment that contains the matching ListView?
In case you want to the the first, you could design a CustomLayout for the Childview, which on OnClick expands, and changes its content to a specific ListView.
Otherwise you would just open up another ListView with data depending on Which Child in First List was Clicked.
Well, i am using some Like that to enlarge ChildViews on Click to show me detailed information.
Im using a Class to wrap my Data named Row. These Rows indicate if they are clickable and if so, the ListView will allow clicks on the rows. A Click will then be handled by the Class itself, making the displayed Text longer(more Detailed). And in order to relayout the items, you need to call notifyDataSetChanged.
#Override
public void onClick(Context context, MyExpandableListAdapter mela) {
this.setBig(!isBig());
mela.notifyDataSetChanged();
}
So, i would handle the row state (expanded/normal) in the getView Method of parents Adapter, to decide which childLayout i inflate.
would looke something like this
public View getView (args...) {
Object data = getItem(position);
if (data.isExpanded()) {
//inflate ListView Layout, create Addapter fill it....
} else {
//show some title or whatever to identify row.
}
}
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.