How I'm currently updating my lists in my project clearly isn't the standard for apps. How do would I refresh the list without clearing it?
This code writes just fine, without any interruptions; however, once the user has finished editing, the list is cleared and refreshed, returning the user to the top of the list. What is best practice when it comes to refreshing data, especially if the data is being edited by another user without interrupting the current user.
Writing:
protected void addStep() {
String stepID = Database.push().getKey();
step newStep = new step(recipeID, stepID, "stepImage", "","");
Database.child(stepID).setValue(newStep);
getData();
}
Adapter:
package asdasd.asd;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
/**
* This class is the class responsible for handling the lists of steps that the user will see during the creator phase.
* It works by setting an array adapter which uses the fragment_steps layout displaying it on a list within the StepActivity
* On text listeners are on each of the fields, when the user edits one of the fields, the program waits 600ms, and then uploads the
* data to the database;
* this refreshes the list TODO change the way the list refreshes in the instance of the creator; perhaps add a delay of 1 minuite before refresh, and or if a new step has been added
* A timer is responsible for this delay. The setting of data is to a specific path; being the recipe -> step -> long/shortText field.
*/
public class stepList extends ArrayAdapter<step>{
private Activity context;
private List<step> steps;
private DatabaseReference database;
private Timer timer1,timer2;
public stepList(Activity context, List<step> steps) {
super(context, R.layout.fragment_step, steps);
this.context = context;
this.steps = steps;
}
@NonNull
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//get database
database = FirebaseDatabase.getInstance().getReference("steps");
//init Layout inflater
LayoutInflater inflater = context.getLayoutInflater();
//step
View listViewItem = inflater.inflate(R.layout.fragment_step,null,true);
//step objects
final TextView descriptionText = (TextView) listViewItem.findViewById(R.id.stepRecipeShortText);
final TextView longDescriptionText = (TextView) listViewItem.findViewById(R.id.stepRecipeLongText);
ImageView stepImage = listViewItem.findViewById(R.id.stepImage);
//init step
final step step = steps.get(position);
//get stepID
final String stepID = step.getStepID();
//Set Data
descriptionText.setText(step.getStepDescription());
longDescriptionText.setText(step.getStepLongDescription());
//TODO If user has uploaded an image, then use that, else then use default
//Add listener to descriptionText so that when a user edits the fields, it is uploaded to the same step in the database
descriptionText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// user is typing: reset already started timer (if existing)
if (timer1 != null) {
timer1.cancel();
}
}
@Override
public void afterTextChanged(Editable s) {
timer1 = new Timer();
timer1.schedule(new TimerTask() {
@Override
public void run() {
String newDescriptionText = descriptionText.getText().toString();
addToDatabase(stepID,"stepDescription", newDescriptionText);
}
}, 600);
}
});
//Add listener to LongDescriptionText so that when a user edits the fields, it is uploaded to the same step in the database
longDescriptionText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// user is typing: reset already started timer (if existing)
if (timer2 != null) {
timer2.cancel();
}
}
@Override
public void afterTextChanged(Editable s) {
timer2 = new Timer();
timer2.schedule(new TimerTask() {
@Override
public void run() {
String newLongDescriptionText = longDescriptionText.getText().toString();
addToDatabase(stepID, "stepLongDescription", newLongDescriptionText);
}
}, 600);
}
});
return listViewItem;
}
//Add the data the user is entering to the database; there is a 600ms delay on the period between the user stopping typing and the data being updated.
private void addToDatabase(String id, String location, String text) {
database.child(id).child(location).setValue(text);
}
}
Getting:
public void getData() {
//receives all the recipes and adds them to the list
Database.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Clear the list
listStepsList.clear();
//Iterate through the nodes
for(DataSnapshot stepSnapshot : dataSnapshot.getChildren()){
//get recipe
step step = stepSnapshot.getValue(step.class);
//add step to list, if it is apart of the same recipe.
if(step.getRecipeID().equals(recipeID)) {
listStepsList.add(step);
}
}
//create Adapter
stepList stepAdapter = new stepList(StepActivity.this, listStepsList);
//Attatch adapter to listview
viewStepsList.setAdapter(stepAdapter);
stepAdapter.notifyDataSetChanged();
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I assume you're seeing a "big bang"/flash whenever there's a change in the database. If so, that is because you're updating the entire list, even if only a single item in the data was changed.
To improve this, you'll want to more granularly update the adapter for changes. To do so, you can attach a ChildEventListener
, which fires events at one level lower in your data structure. So say a new node is added to your list, instead of rebuilding the entire list to add the one node, you'd get a single onChildAdded
call with just the one new node. You'd then update listStepsList
instead of rebuilding it, and tell the adapter about the changes.
For an example of this, I recommend checking out FirebaseUI, as the adapters in there all use this pattern. They build from a common FirebaseArray
class that observes the database, and then have adapter classes to glue to array to the UI. For example, here's how FirebaseRecyclerAdapter
connects the changes in the database to minimal updates to the view.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments