Sorting Data in Firebase - android

I'm using Firebase listview at its shown below and its working like a charm but the problem is it displays the last pushed key in the child("Posts") at the end of my listview(lv) I want the last pushed key to be displayed above all or if i can sort it by date.
query = ref.child("Posts").orderByKey();
FirebaseListAdapter<ChatRoomJ> adapter = new FirebaseListAdapter<ChatRoomJ>(
ChatRoom.this,
ChatRoomJ.class,
R.layout.lvchatroom,query
) {
#Override
protected void populateView(View v, ChatRoomJ model, final int position) {
final String date = model.getDate();
final String displayName = model.getDisplayName();
((TextView) v.findViewById(R.id.displayname)).setText(displayName);
((TextView) v.findViewById(R.id.datel)).setText(date);
});
}
};
lv.setAdapter(adapter);

Method 1:
There is no way to reorder the data with your current code, unless you make your own class that extends FirebaseListAdapter and override the getItem() method. This is a great and easy way to do it (courtesy of Monet_z_Polski). I used the same trick with perfect results so far.
public abstract class ReverseFirebaseListAdapter<T> extends FirebaseListAdapter<T> {
public ReverseFirebaseListAdapter(Activity activity, Class<T> modelClass, int modelLayout, Query ref) {
super(activity, modelClass, modelLayout, ref);
}
public ReverseFirebaseListAdapter(Activity activity, Class<T> modelClass, int modelLayout, DatabaseReference ref) {
super(activity, modelClass, modelLayout, ref);
}
#Override
public T getItem(int position) {
return super.getItem(getCount() - (position + 1));
}
Then when you make the adapter, you use your new custom class that modifies the starting position for each item that is sent to the list.
Method 2:
The other way would be to read the data in to your app (probably into an ArrayList or something like that) and reorder the data on the client before populating the list with the properly ordered data. This can be as easy as reading the data into the ArrayList with a start index of 0 for each item. Something like chatRoomList.add(0, nextChatRoom); for example. That way when you populate the list on the screen it will be in the order you want.
There are other methods involving making a custom adapter, but I personally think that would be more complicated than these two methods. The first is my favorite.

Related

Object List on RecyclerView

I'm having a little big problem on Android Studio. A have an object "Sport" that can have multiple lessons. So inside this object there is a list.
public class Sport implements Serializable {
private String cid;
private String cname;
String csportpic;
long ccreatedat;
List<Lesson> lessons;
public Sport() {
}
The object has gets and sets for each element. So the data coming from Firebase in well placed.
In a recycleview I want to display the lessons data and also some variable of the object sport(cname, cid), so I'm passing to the recycleview constructor the object Sport.
Do you have any ideia how to perform a loop through lesson List in a recyclerview? Can it be done inside onBindViewHolder?
Sorry, got it working.
For some reason the first loop was not working correctly. Perhaps the Object was not well written, or the query to the database.
public void onBindViewHolder(#NonNull final CoachVH holder, final int position)
{
for (Lesson lesson:sportlesson.get(position).getLessons())
{
holder.classid.setText(String.valueOf(new SimpleDateFormat("dd-MM-yyyy HH:mm").format(lesson.getLessonTime())));
holder.classname.setText(sportlesson.get(position).getCname());
holder.classaddress.setText(String.valueOf(lesson.getLessonAddress()));
holder.classprice.setText(String.valueOf(lesson.getLessonPrice()));
}
}

Is there a way to get document ID of item from FirestoreRecyclerAdapter?

I can query Firestore and get IDs manually, but I'm using FirestoreRecyclerAdapter because it comes with a ton of additional work done out of the box. So, if I have code like this:
FirebaseFirestore db = FirebaseFirestore.getInstance();
Query query = db.collection(COLLECTION_NAME)
.whereEqualTo("userId", USER_ID)
.limit(LIMIT);
FirestoreRecyclerOptions options = new FirestoreRecyclerOptions
.Builder<SomeEntity>()
.setQuery(query, SomeEntity.class)
.build();
FirestoreRecyclerAdapter adapter = new FirestoreRecyclerAdapter<SomeEntity, MyHolder>(options) {
#Override
public void onBindViewHolder(MyHolder holder, int position, SomeEntity model) {
// I need model ID here
}
}
So, if I use FirestoreRecyclerAdapter, it automatically deserializes my SomeEntity class into model object. This is all fine and dandy, but now I need to attach listeners to the list, and react with model ID. Now, if I was manually deserializing objects from Firestore I'd do something like this:
for (DocumentSnapshot document : task.getResult()) {
SomeEntity entity = document.toObject(SomeEntity.class).setId(document.getId());
list.add(entity);
}
But, since FirestoreRecyclerAdapter is doing deserialisation for me, I don't get to call document.getId() myself. And by the time I'm in onBindViewHolder method, I don't have access to document ID anymore. Is there some method I'm missing, some way to retrieve IDs from adapter that I overlooked?
Note, I do not consider redundantly storing IDs as a field a solution. I will rather inherit and override FirestoreRecyclerAdapter instead, but I'd prefer if I could solve this without that much work.
When you need the document ID, you should know something already about the item you're dealing with. Typically this will be the position of the item in the list. I.e. when the user clicks on an item, the click handler is passed the ID of that item. If you have the position, you can get the DataSnapshot from the adapter with:
adapter.getSnapshots().getSnapshot(position);
So if you want to get the ID, you'd get it with:
String id = adapter.getSnapshots().getSnapshot(position).getId();
holder.itemView.setOnClickListener { v ->
Toast.makeText(v.context, snapshots.getSnapshot(position).id , Toast.LENGTH_SHORT).show()
}
This comes Under BindviewHolder
Use this in OnBindViewHolder
String DocID = Objects.requireNonNull (getCurrentList ()).snapshot ().get (position).getId ();
If you are using items.class you can create a string with with getter and #DocumentID.
private String docID;
#DocumentId
public String getDocID() {
return docID;
}
Then call this in onBindViewHolder
protected void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position, #NonNull #NotNull Items model) {
model.getDocId();
}

ParseQueryAdapter; using class in Android

I feel like a broken record.
After many attempts, I have failed at getting a listview through Parse data to display a specific set of information.
Here is my model...this is all data from users:
#ParseClassName("Midwifefirm")
public class Midwifefirm extends ParseObject {
public Midwifefirm() {
// A default constructor is required.
}
//practice name
public String getPracticeName() {
return getString("practicename");
}
public void setPracticeName(String practicename) {
put("practicename", practicename);
}
//education
public String getEducation() {
return getString("education");
}
public void setEducation(String education) {
put("education", education);
}
//years in practice
public String getYearsinPractice() {
return getString("yearsinpractice");
}
public void setYearsinPractice(String yearsinpractice) {
put("yearsinpractice", yearsinpractice);
}
//practice philosophy
public String getPracticePhilosophy() {
return getString("practicephilosophy");
}
public void setPracticePhilosophy(String practicephilosophy) {
put("practicephilosophy", practicephilosophy);
}
I have this adapter; I am wondering what to place in the query section, as I just want to pull the data into the ListView that is defined in the data model:
public class CustomMidwifeAdapter extends ParseQueryAdapter<Midwifefirm> {
public CustomMidwifeAdapter(Context context) {
super(context, new ParseQueryAdapter.QueryFactory<Midwifefirm>() {
public ParseQuery<Midwifefirm> create() {
// Here we can configure a ParseQuery to display
// only top-rated meals.
ParseQuery query = new ParseQuery("Midwives");
return query;
}
});
}
#Override
public View getItemView(Midwifefirm midwifefirm, View view, ViewGroup parent) {
if (view == null) {
view = View.inflate(getContext(), R.layout.activity_midwife_result_list, null);
}
//use midwifefirm as item view/list
super.getItemView(midwifefirm, view, parent);
// find in layout the practice name
TextView titleTextView = (TextView) view.findViewById(R.id.practicename);
//in the midwifefirm data model, call getPracticename
titleTextView.setText(midwifefirm.getString("practicename"));
// Add education view
TextView EducationView = (TextView) view.findViewById(R.id.education);
EducationView.setText(midwifefirm.getString("education"));
// Add yearsexperience view
TextView ExperienceView = (TextView) view.findViewById(R.id.yearsinpractice);
ExperienceView.setText(midwifefirm.getString("yearsinpractice"));
//Add practice philosophy view
TextView PracticePhilosophyView = (TextView) view.findViewById(R.id.practicephilosophy);
PracticePhilosophyView.setText(midwifefirm.getString("practicephilosophy"));
return view;
}
}
And here is the Main Activity:
public class MidwifeResultList extends ListActivity {
private ParseQueryAdapter<ParseObject> mainAdapter;
private CustomMidwifeAdapter midwifeListAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//initialize main ParseQueryAdapter
mainAdapter = new ParseQueryAdapter<ParseObject>(this, Midwifefirm.class);
//which keys in Midwife object
mainAdapter.setTextKey("practicename");
mainAdapter.setTextKey("education");
mainAdapter.setTextKey("yearsinpractice");
mainAdapter.setTextKey("practicephilosophy");
// Initialize the subclass of ParseQueryAdapter
midwifeListAdapter = new CustomMidwifeAdapter(this);
// Default view is all meals
setListAdapter(mainAdapter);
}
Every time I run this, I get no results.
Thanks in advance for any help
Michael
I can tell you why I think it fails now and I can tell you why I'm very sure it will fail after you sort out the current issue.
It seems that you're trying to use different classes
#ParseClassName("Midwifefirm")
public class Midwifefirm extends ParseObject {
and
ParseQuery query = new ParseQuery("Midwives");
You need to be consistent and use the same name. Either use Midwives or Midwifefirm for both. Let's assume you picked the latter. You're also saying
all that is stored in the user table...wasn't sure if I needed to create new tables.
The query above wants to get all entries of type Midwives. If there's no such type, it'll return nothing. So you have two options:
In you Parse dashboard, reate a class Midwifefirm (don't forget to update the String inside #ParseClassName above) and store your Midwifefirm data in there. You don't need to change your query for this.
Add a column to your ParseUser class, such as type, that you can set to Midwifefirm or whatever if that user is a Midwifefirm or whatever. Then in your query you need to add:
ParseQuery query = new ParseQuery("Midwives");
query.whereEquals("type", "Midwifefirm");
I greatly prefer the former.
Anyway, once your done that, the issue is that you're not using a custom view for this. You're relying on the one provided by Android by default for ListActivity. I am fairly sure it doesn't have any of the fields you're after, so you should create a custom view for this, then at the top of onCreate in your Activity make sure you use it
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_custom_view);
By the way, the following are redundant if you populate then in getItemView anyway:
mainAdapter.setTextKey("practicename");
mainAdapter.setTextKey("education");
mainAdapter.setTextKey("yearsinpractice");
mainAdapter.setTextKey("practicephilosophy");
One final advice: if you're still having issues, set breakpoints and do some investigations first. What you need to check is:
Whether you get anything at all from Parse when you do your query. Your adapter has an useful addOnQueryLoadListener that you may use to check whether anything's been retrieved at all.
If stuff is retrieved successfully, you need to check whether the list view is populated correctly. Again, use breakpoints, this time in getItemView maybe.
I'm going to do a wild guess here using the lovely brainwrecking API help of Parse.com about ParseQueryAdapters
Before continuing, may I mind you that my experience with ParseQueryAdapters is a minimum but I think I have a basic knowledge about them + I have some experience with Parse on its own. ANYHOW,
As an example they use both these
final ParseQueryAdapter adapter = new ParseQueryAdapter(this, "Midwives");
adapter.setTextKey("name");
ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);
and
// Instantiate a QueryFactory to define the ParseQuery to be used for fetching items in this
// Adapter.
ParseQueryAdapter.QueryFactory<ParseObject> factory =
new ParseQueryAdapter.QueryFactory<ParseObject>() {
public ParseQuery create() {
ParseQuery query = new ParseQuery("Midwives");
return query;
}
};
// Pass the factory into the ParseQueryAdapter's constructor.
ParseQueryAdapter<ParseObject> adapter = new ParseQueryAdapter<ParseObject>(this, factory);
adapter.setTextKey("name");
// Perhaps set a callback to be fired upon successful loading of a new set of ParseObjects.
adapter.addOnQueryLoadListener(new OnQueryLoadListener<ParseObject>() {
public void onLoading() {
// Trigger any "loading" UI
}
public void onLoaded(List<ParseObject> objects, ParseException e) {
// Execute any post-loading logic, hide "loading" UI
}
});
// Attach it to your ListView, as in the example above
ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);
To start of, the reason why I think nothing is loading inside your list has to do with a little mixup between the initilization of your ParseQueryAdapter and your custom adapter.
You configure the basic adapter, and also initialize a custom adapter but you don't do anything with the custom adapter, tho the custom adapter seems to contain the logics to load your data model.
I think what you're looking for is something like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//initialize main ParseQueryAdapter
mainAdapter = new CustomMidwifeAdapter<ParseObject>(this);
//which keys in Midwife object
mainAdapter.setTextKey("practicename");
mainAdapter.setTextKey("education");
mainAdapter.setTextKey("yearsinpractice");
mainAdapter.setTextKey("practicephilosophy");
// Default view is all meals
setListAdapter(mainAdapter);
}
All you need to pass is the context (aka "this"), and the constructor of your custom class will handle the factory internal
super(context, new ParseQueryAdapter.QueryFactory<Midwifefirm>() {
public ParseQuery<Midwifefirm> create() {
// Here we can configure a ParseQuery to display
// only top-rated meals.
ParseQuery query = new ParseQuery("Midwives");
return query;
}
});
Tho to be honest since you do:
new ParseQueryAdapter<ParseObject>(this, Midwifefirm.class);
I wonder if you'd need to change your "QueryFactory" to
super(context, new ParseQueryAdapter.QueryFactory<Midwifefirm>() {
public ParseQuery<Midwifefirm> create() {
// Here we can configure a ParseQuery to display
// only top-rated meals.
ParseQuery query = new ParseQuery(MidWifefirm.class);
return query;
}
});
Where you pass a class to the the query rather than the tableName, but I could be wrong on that one.
Either way I hope this has helped in some way!

Changing a business object in an Adapter

I'm building an android app similar to Facebook (gets a newsfeed stored in db from a REST API), and I am now implementing a reddit-like voting system, in which every feed item has a vote state (none, up, down) stored in db.
When I do retrieve the newsfeed json, i set its voteState to the vote state in the json, then I display it through a newsfeed adapter.
But if i set the voteState in the newsfeed adapter to another value (based on an onClickListener), this changed value does not reach the actual newsfeed.
This is because every time I scroll the newsfeed, the newsfeed adapter gets a new instance of the newsfeed, and doesn't care about the value I changed.
Thus, I'm looking for the best way to modify permanently the newsfeed vote state from the newsfeed adapter (this can be generalized to any variable assigned to an ArrayList and then displayed through an ArrayListAdapter).
If you have any suggestions, please feel free to respond.
Thank you :)
EDIT :
public class NewsfeedAdapter extends RecyclerView.Adapter<NewsfeedAdapter.ViewHolder> {
private Newsfeed newsfeed;
private FeedItem item;
public NewsfeedAdapter(..., Newsfeed newsfeed) {
...;
this.newsfeed = newsfeed;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
protected Button button_up/down....
public ViewHolder(View v) {
super(v);
button = findViewById ... (R.id.button);
...
}
}
// Create new views (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
...
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final FeedItem item = newsfeed.get(position);
holder.button_up.setTag(position);
holder.button_up.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
changeVote(item, "up");
}
});
holder.button_down.setTag(position);
holder.button_down.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
changeVote(item, "down");
}
});
}
public void changeVote(FeedItem item, String flag) {
// Log.d(TAG, "State of vote :" + item.getVoteState());
if (item.getVoteState().equals(flag)) {
item.setVoteState("none");
Log.d(TAG, "Deleting vote :" + item.getVoteState());
new Connexion(activity, fragment, "vote", "Deleting vote...").execute(String.valueOf(item.getId()), flag, "delete");
}
else if (item.getVoteState().equals("none")){
item.setVoteState(flag);
Log.d(TAG, "Voting :" + item.getVoteState());
new Connexion(activity, fragment, "vote", "Voting...").execute(String.valueOf(item.getId()), flag, "insert");
} else {
item.invertVoteState(flag);
Log.d(TAG, "Inverting :" + item.getVoteState());
new Connexion(activity, fragment, "vote", "Voting...").execute(String.valueOf(item.getId()), flag, "invert");
}
} }
You have two options:
1) Send updates to the rest API every time a piece of data is changed. This is really expensive and not something you want to do most likely.
2) Create a local data layer between your rest API and your listview - most likely an SQLite database or some other ORM lib. This way you can hold an instance of the DB in your adapter, and whenever a piece of data changes, you can update that through a simple db method call, and then periodically send updates to the rest API, while also checking for new updates from the rest API and then pushing them to the DB.
I would highly recommend using an SQLite DB, as you can keep a counter in your DB code of a certain number of changes, or time passed since last sync, and keep the data the user is seeing up to date with out smashing the users data usage with repeated network calls.

Detailed view of list row

In my project have to populate the list view from local database.I have implemented it.When I click the row in list item I need to show all the details in list row in next activity.I implemented custom list adapter.I not yet started to code for detailed list row.How can I pass all details in single row to another activity.Can anyone help me?
You can set the information in tags of textviews of your custom list and pass them through intents.
one thing that you can do is just pass the id (PK) of the item . Then on next activity you can fetch it again from database.
another options is that you can create a class with all the data you want to forward as class'members and serialize the object and send it along with the intent.
here is a example
public class ActivityExtra implements Parcelable {
public Integer a=0;
public String b="";
private GameActivityExtra(Parcel in) {
this.a = in.readInt();
this.b = in.readString();
}
public GameActivityExtra() {
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(a);
dest.writeString(b);
}
public static final Parcelable.Creator<GameActivityExtra> CREATOR = new Parcelable.Creator<GameActivityExtra>() {
public GameActivityExtra createFromParcel(Parcel in) {
return new GameActivityExtra(in);
}
public GameActivityExtra[] newArray(int size) {
return new GameActivityExtra[size];
}
};
}
now create instance of this class in your activity . and use intent.putextra(...) to put it . and get the same object while receiving it.
Use #nitesh goel answer to make your object class parcelabe.
Then onitemclick use
intent.putExtra("object", object);
to send your object to other activity.
And in corresponding activity use
intent.getParcelableExtra("object");
to get your object. then you can get everything of that object.

Categories

Resources