Proper way to impelent Android AndroidViewModel and LiveData - android

I implemented LiveData in my project, but I'm a little bit confused about it. It works, but I don't know is it the proper way.
My live data class:
public class ClientLiveData extends LiveData<List<Client>> {
private Context context;
private BroadcastReceiver dataChangedReciever = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Logger.logLiveData("Data changed");
loadData();
}
};
public ClientLiveData(Context context) {
this.context = context.getApplicationContext();
loadData();
}
#Override
protected void onActive() {
super.onActive();
Logger.logLiveData("Activate live data");
LocalBroadcastManager.getInstance(context).registerReceiver(dataChangedReciever, new IntentFilter(DpnPreferences.DATA_CHANGED_FILTER));
}
#Override
protected void onInactive() {
super.onInactive();
Logger.logLiveData("Inactivate live data");
LocalBroadcastManager.getInstance(context).unregisterReceiver(dataChangedReciever);
}
private void loadData() {
new AsyncTask<Void, Void, List<Client>>() {
#Override
protected List<Client> doInBackground(Void... params) {
Logger.logLiveData("Loading data");
IClientDao clientDao = new DaoFactory().getClientDao();
List<Client> clients = clientDao.getAllClients();
Logger.logLiveData("Loading clients "+clients.size());
return clients;
}
#Override
protected void onPostExecute(List<Client> clients) {
super.onPostExecute(clients);
Logger.logLiveData("Set value for ViewModel");
setValue(clients);
}
}.execute();
}
}
My ViewModel class
public class ClientViewModel extends AndroidViewModel {
private final ClientLiveData data;
public ClientViewModel(Application application) {
super(application);
data = new ClientLiveData(application);
}
public LiveData<List<Client>> getData(){
return data;
}
}
I use it in my MainActivity like this:
clientViewModel = ViewModelProviders.of(this).get(ClientViewModel.class);
clientViewModel.getData().observe(this, new Observer<List<Client>>() {
#Override
public void onChanged(#Nullable List<Client> clients) {
Logger.logLiveData("Notify adapter dataset changed");
if (clientsAdapter != null) clientsAdapter.notifyDataSetChanged();
}
});
In my mock test case at first I delete all client from db, than I load a few clients from a raw json, save them into db.
After I can add new clients from an AlertDialog like this:
#OnClick (R.id.addClient_button)
public void onClick() {
final LinearLayout container = new LinearLayout(this);
container.setOrientation(LinearLayout.VERTICAL);
final EditText clientNameText = new EditText(this);
final EditText clientSpText = new EditText(this);
container.addView(clientNameText);
container.addView(clientSpText);
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle("Add new client")
.setView(container);
builder.setPositiveButton("Save", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
String clientName = clientNameText.getText().toString();
String clientSp = clientSpText.getText().toString();
Client client = new Client(clientName, clientSp, "", 1, "1. Rendelő", "www.obudamaganrendelo.hu");
client.save();
dialog.dismiss();
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(new Intent(DATA_CHANGED_FILTER));
}
});
builder.show();
}
As I wrote it works fine, after I add a new client from AlertDialog the adapter updates.
But it would be nice if somebody confirms this implementation is a proper way of use ViewModel and LiveData.
Thanks

Related

ViewModel onChanged() called infinitely

I calculate percent when checkbox on recyclerView item is checked or unchecked. It works fine but when I add a new item to recyclerView sometimes onChanged() is called infinitely and UI freezes.
In MainActivity
#Override
public void onCheckBoxCheckListener(final TaskEntry taskEntry, final boolean isChecked) {
AppExecutors.getInstance().diskIO().execute(new Runnable() {
#Override
public void run() {
taskEntry.setChecked(isChecked);
mDb.taskDao().updateTask(taskEntry);
}
});
}
private void setupViewModel() {
MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class);
viewModel.getTasks().observe(this, new Observer<List<TaskEntry>>() {
#Override
public void onChanged(List<TaskEntry> taskEntries) {
calculatePercent(taskEntries);
mprogressBar.setProgress((int)mTotalProgressPercent);
mProgressValue.setText((int)mTotalProgressPercent + " %");
//this gets logged infinity when a new item is added
Log.d("setupVM", " called TotalPercent = " + (int)mTotalProgressPercent );
mAdapter.setTasks(taskEntries);
}
});
}
private void calculatePercent(List<TaskEntry> taskEntries) {
int countChecked = 0;
for(TaskEntry i: taskEntries){
if(i.isChecked()) countChecked++;
}
mTotalProgressPercent = (double)countChecked/taskEntries.size() *100;
}
MainViewModel
public class MainViewModel extends AndroidViewModel {
private LiveData<List<TaskEntry>> tasks;
public MainViewModel(#NonNull Application application){
super(application);
AppDatabase database = AppDatabase.getInstance(this.getApplication());
tasks = database.taskDao().loadAllTasks();
}
public LiveData<List<TaskEntry>> getTasks() {
return tasks;
}
When new Item (task) is added using another activity, onSaveButtonclicked method is used
public void onSaveButtonClicked() {
String description = mEditText.getText().toString();
int priority = getPriorityFromViews();
Date date = new Date();
final TaskEntry taskEntry = new TaskEntry(description, priority, date, false );
AppExecutors.getInstance().diskIO().execute(new Runnable() {
#Override
public void run() {
if(mTaskId == DEFAULT_TASK_ID){
mDb.taskDao().insertTask(taskEntry);
}else {
taskEntry.setId(mTaskId); //for updating any task, works fine
mDb.taskDao().updateTask(taskEntry);
}
finish(); // return to main activity
}
});
}
I solved this issue. I just used onClickListener on Checkbox instead of OnCheckedChangeListener. OnCheckedChanged() was getting called again and again in a loop.
But now it works fine with onClickListener.

Android dialog will not bring text to underlying fragment

I am currently attempting to have a custom dialog on a fragment to bring text back to the fragment. I have the dialog setup but when I click on the button to bring up the dialog I get this error.
java.lang.ClassCastException: com.example.android.app.MainActivity#4c01a76must implement StatusDialogListener
I then found this stack post talking about the issue
From reading the error and the stack post it seems that the issue is that I have to implement the dialog class onto the activity that is connected to the fragment.
So I implemented the listener into my main activity
public class MainActivity extends AppCompatActivity implements ... Status_Dialog.StatusDialogListner
After implementing it, I got the error below:
Class 'MainActivity' must either be declared abstract or implement abstract method 'applyText(String)' in 'StatusDialogListner
Which I fixed by adding the code below to the Activity:
#Override
public void applyText(String status) {
}
This allows the app to run and display the dialog with no errors.
The issue is now my adapter will not get the text from the dialog so I believe it is to do with the fact that I also have an applyText in my fragment and main activity when I ran the debugger applytext in the fragment was never called. I am lost at this point
Code
Dialog
public class Status_Dialog extends AppCompatDialogFragment {
private EditText editTextStatus;
private StatusDialogListner listner;
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.layout_status_dialog, null);
editTextStatus = view.findViewById(R.id.new_status);
builder.setView(view)
.setTitle("Change Status")
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
}
})
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
String status = editTextStatus.getText().toString();
listner.applyText(status);
}
});
return builder.create();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
listner = (StatusDialogListner) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "must implement StatusDialogListener");
}
}
public interface StatusDialogListner {
void applyText(String status);
}
}
Fragment
public class profile_fragment extends Fragment implements Status_Dialog.StatusDialogListner {
private static final int GALLERY_PICK = 1;
private DatabaseReference mUserDatabase;
//Android Layout
private FirebaseUser mCurrentUser;
private CircleImageView mDisplayImage;
private TextView mName;
private TextView mStatus;
private Button mStatusBtn;
private Button mImageBtn;
private ProgressDialog mProgressDialog;
private StorageReference mImageStorage;
private FirebaseFirestore db = FirebaseFirestore.getInstance();
private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthListner;
private static final String KEY_NAME = "name";
private static final String KEY_STATUS = "status";
private static final String KEY_IMAGE = "image";
private static final String TAG = "user_profile";
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_profile_fragment, container, false);
mDisplayImage = view.findViewById(R.id.profile_picture);
mName = view.findViewById(R.id.profile_user_name);
mStatus = view.findViewById(R.id.profile_user_status);
mStatusBtn = view.findViewById(R.id.profile_change_status_btn);
mImageBtn = view.findViewById(R.id.profile_change_image_btn);
mImageStorage = FirebaseStorage.getInstance().getReference();
mCurrentUser = FirebaseAuth.getInstance().getCurrentUser();
final String current_uid = mCurrentUser.getUid();
DocumentReference mUsersDB = db.collection("Users").document(current_uid);
// FirebaseUser currentFirebaseUser = FirebaseAuth.getInstance().getCurrentUser() ;
// Toast.makeText(this, "ttt" + currentFirebaseUser.getUid(), Toast.LENGTH_SHORT).show();
mStatusBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openDialog();
/*
String status_value = mStatus.getText().toString();
Intent status_intent = new Intent(getActivity(), change_status.class);
status_intent.putExtra("status_value", status_value);
startActivity(status_intent);
*/
}
});
mImageBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent galleryIntent = new Intent();
galleryIntent.setType("image/*");
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(galleryIntent, "Select Image"), GALLERY_PICK);
/*
CropImage.activity()
.setGuidelines(CropImageView.Guidelines.ON)
.start(SettingsActivity.this);
*/
}
});
mUsersDB.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists()) {
String name = documentSnapshot.getString(KEY_NAME);
mName.setText(name);
String status = documentSnapshot.getString(KEY_STATUS);
mStatus.setText(status);
FirebaseUser currentFirebaseUser = FirebaseAuth.getInstance().getCurrentUser();
} else {
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});
return view;
}
public void openDialog(){
Status_Dialog status_dialog = new Status_Dialog();
status_dialog.show(getActivity().getSupportFragmentManager(), "TEST?");
}
#Override
public void applyText(String status) {
mStatus.setText(status);
}
...
Problem is here:
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
listner = (StatusDialogListner) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "must implement StatusDialogListener");
}
}
Note that listner is set during the onAttach and it is receiving the instance of Context. In other words, it is receiving the instance of the host activity (That's why you had to implement the interface in the MainActivity)
I think you can update your code as follows:
1) Remove this from the dialog. This way, your activity no longer must implement StatusDialogListener
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
listner = (StatusDialogListner) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "must implement StatusDialogListener");
}
}
2) Update this on Dialog:
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
if(listner != null) { // ADD THIS NULL CHECK SINCE LISTNER MAY BE NULL
String status = editTextStatus.getText().toString();
listner.applyText(status);
}
}
});
3) Add this to the dialog:
public void setListener(StatusDialogListner newListener) {
this.listner = newListener;
}
4) Update this on the fragment:
public void openDialog(){
Status_Dialog status_dialog = new Status_Dialog();
status_dialog.setListener(this); // ADD THIS NEW LINE
status_dialog.show(getActivity().getSupportFragmentManager(), "TEST?");
}
5) Remove below code from MainActivity
// Remove the implementation of Status_Dialog.StatusDialogListner from MainActivity
// And remove the method below:
#Override
public void applyText(String status) {
}

LiveData query is not being run the second time I initialise it

I have a list of different mines. Each mine has a list of blocks.
I have the mines in a spinner and the blocks in a recyclerview.
I want to display the different lists of blocks whenever the user changes the mine in the mine spinner
I am using Firebase in the backend as my database.
When I change the mine in the spinner, I update the block list by creating a new MutableLiveData which I've extended in a class called FirebaseQueryLiveData
The first time that I initialise the FirebaseQueryLiveData with the quesry containing the mine name, all the events inside it fire. However, after that, I call it and nothing fires. It breaks in the constructor if I have a breakpoint there, but it never reaches the run() method, onActive() method or the onDataChanged in the ValueEventListener.
I have done some research, and I have seen suggestions to replace the LiveData with MutableLiveData. I've done this, and it doesn't seem to make a difference.
Can anyone see anything in the code which I might be missing? I'm not very familiar with the android architecture components and I got the FirebaseQueryLiveData class from another helpful website with a tutorial, so I'm battling to understand where I have gone wrong.
I have done some research, and I have seen suggestions to replace the LiveData with MutableLiveData. I've done this, and it doesn't seem to make a difference.
public class BlockListActivityViewModel extends ViewModel {
private static DatabaseReference blockOutlineRef; // = FirebaseDatabase.getInstance().getReference(FireBasePaths.BLOCKOUTLINE.getPath("Therisa"));
private static DatabaseReference mineListRef;
private FirebaseQueryLiveData blockOutlineLiveDataQuery = null;
private LiveData<BlockOutlineList> blockOutlineLiveData = null;
private MediatorLiveData<String> selectedBlockNameMutableLiveData;
private MediatorLiveData<ArrayList<String>> mineListMutableLiveData;
public BlockListActivityViewModel() {
User loggedInUser = UserSingleton.getInstance();
setUpFirebasePersistance();
setupMineLiveData(loggedInUser);
// setupBlockOutlineListLiveData();
}
private void setupBlockOutlineListLiveData(String mineName) {
if (mineName != "") {
blockOutlineRef = FirebaseDatabase.getInstance().getReference(FireBasePaths.BLOCKOUTLINE.getPath(mineName));
blockOutlineLiveDataQuery = new FirebaseQueryLiveData(blockOutlineRef);
blockOutlineLiveData = Transformations.map(blockOutlineLiveDataQuery, new BlockOutlineHashMapDeserialiser());
}
}
private void setupMineLiveData(User user) {
ArrayList<String> mineNames = new ArrayList<>();
if (user != null) {
if (user.getWriteMines() != null) {
for (String mineName : user.getWriteMines().values()) {
mineNames.add(mineName);
}
}
}
setMineListMutableLiveData(mineNames);
if (mineNames.size() > 0) {
updateMineLiveData(mineNames.get(0));
}
}
public void updateMineLiveData(String mineName) {
SelectedMineSingleton.setMineName(mineName);
setupBlockOutlineListLiveData(SelectedMineSingleton.getInstance());
}
public void setUpFirebasePersistance() {
int i = 0;
// FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
private MutableLiveData<NamedBlockOutline> selectedBlockOutlineMutableLiveData;
public MutableLiveData<NamedBlockOutline> getSelectedBlockOutlineMutableLiveData() {
if (selectedBlockOutlineMutableLiveData == null) {
selectedBlockOutlineMutableLiveData = new MutableLiveData<>();
}
return selectedBlockOutlineMutableLiveData;
}
public void setSelectedBlockOutlineMutableLiveData(NamedBlockOutline namedBlockOutline) {
getSelectedBlockOutlineMutableLiveData().postValue(namedBlockOutline);
}
public MediatorLiveData<String> getSelectedBlockNameMutableLiveData() {
if (selectedBlockNameMutableLiveData == null)
selectedBlockNameMutableLiveData = new MediatorLiveData<>();
return selectedBlockNameMutableLiveData;
}
public void setSelectedBlockNameMutableLiveData(String blockName) {
selectedBlockNameMutableLiveData.postValue(blockName);
}
public MediatorLiveData<ArrayList<String>> getMineListMutableLiveData() {
if (mineListMutableLiveData == null)
mineListMutableLiveData = new MediatorLiveData<>();
return mineListMutableLiveData;
}
public void setMineListMutableLiveData(ArrayList<String> mineListString) {
getMineListMutableLiveData().postValue(mineListString);
}
private class BlockOutlineHashMapDeserialiser implements Function<DataSnapshot, BlockOutlineList>, android.arch.core.util.Function<DataSnapshot, BlockOutlineList> {
#Override
public BlockOutlineList apply(DataSnapshot dataSnapshot) {
BlockOutlineList blockOutlineList = new BlockOutlineList();
HashMap<String, NamedBlockOutline> blockOutlineStringHashMap = new HashMap<>();
for (DataSnapshot childData : dataSnapshot.getChildren()) {
NamedBlockOutline thisNamedOutline = new NamedBlockOutline();
HashMap<String, Object> blockOutlinePointHeader = (HashMap<String, Object>) childData.getValue();
HashMap<String, BlockPoint> blockOutlinePoints = (HashMap<String, BlockPoint>) blockOutlinePointHeader.get("blockOutlinePoints");
thisNamedOutline.setBlockName(childData.getKey());
thisNamedOutline.setBlockOutlinePoints(blockOutlinePoints);
blockOutlineStringHashMap.put(childData.getKey(), thisNamedOutline);
}
blockOutlineList.setBlockOutlineHashMap(blockOutlineStringHashMap);
return blockOutlineList;
}
}
#NonNull
public LiveData<BlockOutlineList> getBlockOutlineLiveData() {
return blockOutlineLiveData;
}
}
LiveData
public class FirebaseQueryLiveData extends MutableLiveData<DataSnapshot> {
private static final String LOG_TAG = "FirebaseQueryLiveData";
private final Query query;
private final MyValueEventListener listener = new MyValueEventListener();
private boolean listenerRemovePending = false;
private final Handler handler = new Handler();
public FirebaseQueryLiveData(Query query) {
this.query = query;
}
public FirebaseQueryLiveData(DatabaseReference ref) {
this.query = ref;
}
private final Runnable removeListener = new Runnable() {
#Override
public void run() {
query.removeEventListener(listener);
listenerRemovePending = false;
Log.d(LOG_TAG, "run");
}
};
#Override
protected void onActive() {
super.onActive();
if (listenerRemovePending) {
handler.removeCallbacks(removeListener);
Log.d(LOG_TAG, "listenerRemovePending");
}
else {
query.addValueEventListener(listener);
Log.d(LOG_TAG, "addValueEventListener");
}
listenerRemovePending = false;
Log.d(LOG_TAG, "listenerRemovePending");
}
#Override
protected void onInactive() {
super.onInactive();
// Listener removal is schedule on a two second delay
handler.postDelayed(removeListener, 4000);
listenerRemovePending = true;
Log.d(LOG_TAG, "listenerRemovePending");
}
private class MyValueEventListener implements ValueEventListener {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
setValue(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(LOG_TAG, "Can't listen to query " + query, databaseError.toException());
}
}
}

Firebase Retrieve Data

I'm trying to display from data from my Firebase database show in Firebase Recyclerview , This my Database
And i try use the same as tutorial code from
Firebase UI
I'm try and get this result
I'm fairly new to Android and programming in general, so any help would be appreciated. Here is the relevant code.
PostlistFragment
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mActivity = getActivity();
final Dialog mDialog = new Dialog(mActivity, R.style.NewDialog);
mDialog.addContentView(
new ProgressBar(mActivity),
new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
);
mDialog.setCancelable(true);
mDialog.show();
// Set up Layout Manager, reverse layout
LinearLayoutManager mManager = new LinearLayoutManager(mActivity);
mManager.setReverseLayout(true);
mManager.setStackFromEnd(true);
mRecycler.setLayoutManager(mManager);
// Set up FirebaseRecyclerAdapter with the Query
Query postsQuery = getQuery(mDatabase);
mAdapter = new FirebaseRecyclerAdapter<PostMainboard, MainboardViewHolder>(PostMainboard.class, R.layout.mainboard_list, MainboardViewHolder.class, postsQuery) {
#Override
public void onDataChanged() {
super.onDataChanged();
mDialog.dismiss();
}
#Override
protected void populateViewHolder(final MainboardViewHolder viewHolder, final PostMainboard model, final int position) {
final DatabaseReference postRef = getRef(position);
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mActivity, MainboardDetailActivity.class);
intent.putExtra(MainboardDetailActivity.EXTRA_POST_KEY, postRef.getKey());
startActivity(intent);
}
});
}
};
mRecycler.setAdapter(mAdapter);
}
#Override
public void onDestroy() {
super.onDestroy();
if (mAdapter != null) {
mAdapter.cleanup();
}
}
public abstract Query getQuery(DatabaseReference databaseReference);
And result of my detail activity got same not show every one
Here My Detail Activity Code
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_mainboard_detail);
mBodyView = (TextView)findViewById(R.id.post_detail);
mAuthorView = (TextView)findViewById(R.id.txt_author);
mTitleView = (TextView)findViewById(R.id.post_topic);
mDateView = (TextView)findViewById(R.id.post_date_time);
mCommentsRecycler = (RecyclerView) findViewById(R.id.recycler_comments);
mCommentsRecycler.setLayoutManager(new LinearLayoutManager(this));
mCommentField = (EditText) findViewById(R.id.comment_field);
Button mCommentButton = (Button) findViewById(R.id.button_post_comment);
mCommentButton.setOnClickListener(this);
// Get post key from intent
String mPostKey = getIntent().getStringExtra(EXTRA_POST_KEY);
if (mPostKey == null) {
throw new IllegalArgumentException("Must pass EXTRA_POST_KEY");
}
// Initialize Database
mPostReference = FirebaseDatabase.getInstance().getReference().child("mainboard").child(mPostKey);
mCommentsReference = FirebaseDatabase.getInstance().getReference().child("cm-mainboard").child(mPostKey);
}
#Override
public void onStart() {
super.onStart();
// Add value event listener to the post
ValueEventListener postListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Post object and use the values to update the UI
PostMainboard post = dataSnapshot.getValue(PostMainboard.class);
User user = dataSnapshot.getValue(User.class);
mAuthorView.setText(user.uid);
mTitleView.setText(post.postTopic);
mBodyView.setText(post.postDetail);
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
Toast.makeText(MainboardDetailActivity.this, "Failed to load post.", Toast.LENGTH_SHORT).show();
}
};
mPostReference.addValueEventListener(postListener);
// Keep copy of post listener so we can remove it when app stops
mPostListener = postListener;
// Listen for comments
mAdapter = new CommentAdapter(this, mCommentsReference);
mCommentsRecycler.setAdapter(mAdapter);
}
#Override
public void onStop() {
super.onStop();
if (mPostListener != null) {
mPostReference.removeEventListener(mPostListener);
}
mAdapter.cleanupListener();
}
Post Class
public class PostMainboard{
public String uid;
public String auther;
public String postTopic;
public String postDetail;
public String postImageUrl;
public String postID;
private String postlatlon;
public long timeCreated;
public PostMainboard(){
}
public PostMainboard(String uid, String auther , String postTopic , String postDetail,long timeCreated)
{
this.uid = uid;
this.auther = auther;
this.postTopic = postTopic;
this.postDetail = postDetail;
this.postImageUrl = postImageUrl;
this.postID = postID;
this.timeCreated = timeCreated;
}
#Exclude
public Map<String, Object> toMap() {
HashMap<String, Object> result = new HashMap<>();
result.put("mb_id", uid);
result.put("mb_auther" , auther);
result.put("mb_title", postTopic);
result.put("mb_body", postDetail);
result.put("mb_create", timeCreated);
return result;
}
User class
public class User {
public String uid;
public String user_fname;
private String user_lname;
private String user_idcard;
private String email;
private String user_phone;
public User(){
}
public User(String uid ,String user_fname, String user_lname, String user_idcard, String email, String user_phone) {
this.uid = uid;
this.user_fname = user_fname;
this.user_lname = user_lname;
this.user_idcard = user_idcard;
this.email = email;
this.user_phone = user_phone;
}
ViewHolder
public class MainboardViewHolder extends RecyclerView.ViewHolder {
private TextView authorView;
private TextView bodyView;
private TextView titleView;
private TextView dateView;
public MainboardViewHolder(View itemView) {
super(itemView);
authorView = (TextView)itemView.findViewById(R.id.txt_author);
bodyView = (TextView)itemView.findViewById(R.id.post_detail);
titleView = (TextView)itemView.findViewById(R.id.post_topic);
dateView = (TextView)itemView.findViewById(R.id.post_date_time);
}
public void bindToPost (PostMainboard postMainboard)
{
authorView.setText(postMainboard.auther);
bodyView.setText(postMainboard.postDetail);
titleView.setText(postMainboard.postTopic);
dateView.setText((int) postMainboard.timeCreated);
}
Mainboard fragment
public class MainboardFragment extends PostListFragment{
public MainboardFragment() {
// Required empty public constructor
}
#Override
public Query getQuery(DatabaseReference databaseReference) {
return databaseReference.child("mainboard").orderByKey();
}
It looks like you have forgotten to populate the ViewHolder (MainboardViewHolder).
In "PostlistFragment" under "populateViewHolder" you need to call viewHolder.bindToPost(model) to bind the data to your textviews and so forth.
Try this:
#Override
protected void populateViewHolder(final MainboardViewHolder viewHolder, final PostMainboard model, final int position) {
final DatabaseReference postRef = getRef(position);
viewHolder.bindToPost(model);
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(mActivity, MainboardDetailActivity.class);
intent.putExtra(MainboardDetailActivity.EXTRA_POST_KEY, postRef.getKey());
startActivity(intent);
}
});
}
Also, your getQuery method is abstract and empty. It should probably look like this:
#Override
public Query getQuery(DatabaseReference databaseReference) {
return databaseReference.getReference("mainboard").orderByKey();
}
I'm saying "probably" because I can't see how you have initialized mDatabase. In the future, please paste complete code if you wan't better and faster answers.

Android ViewModel LiveData update view on button click

I am following this tutorial to learn ViewModel and LiveData. In my case, instead of getting data from network, I am simply generating random string on button click and trying to update a textview. The problem is that the textview does not get updated when the data is changed by button click, but only gets updated when orientation is toggled.
Activity Class (extends LifecycleActivity)
public class PScreen extends BaseActivity {
#Override protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.places_screen);
final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
viewModel.init();
viewModel.getUser().observe(this, new Observer<User>() {
#Override public void onChanged(#Nullable User user) {
((TextView) findViewById(R.id.name)).setText(user.getName());
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
final MutableLiveData<User> data = new MutableLiveData<>();
User user = new User();
user.setName(String.valueOf(Math.random() * 1000));
data.postValue(user);
viewModel.setUser(data); // Why it does not call observe()
}
});
}
}
ViewModel Class
package timsina.prabin.tripoptimizer.model;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.ViewModel;
public class UserModel extends ViewModel {
private LiveData<User> user;
public void init() {
if (this.getUser() != null) {
return;
}
this.user = new LiveData<User>() {
#Override protected void setValue(User value) {
value.setName("Fresh New Name");
super.setValue(value);
}
};
}
public LiveData<User> getUser() {
return user;
}
public void setUser(LiveData<User> user) {
this.user = user;
}
}
You are creating a new LiveData instance each time! You are not supposed to do that. If you do that all previous observers will be ignored.
In this case you could replace your setUSer(LiveData<User>) method on your ViewModel to setUser(User u) (taking a User instead of a LiveData) and then do user.setValue(u) inside it.
Of course, will have to initialize the LiveData member in your ViewModel class, like this:
final private LiveData<User> user = new MutableLiveData<>();
It will work then because it will notify the existing observers.
I was somehow able to resolve this by using MutableLiveData instead of LiveData.
Model class
private MutableLiveData<User> user2;
public void init() {
if (user2 == null) {
user2 = new MutableLiveData<>();
}
}
public MutableLiveData<User> getUser2() {
return user2;
}
public void setUser2(final User user) {
user2.postValue(user);
}
Activity
viewModel.getUser2().observe(this, new Observer<User>() {
#Override public void onChanged(#Nullable User user) {
((TextView) findViewById(R.id.name)).setText(user.getName());
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
User user = new User();
viewModel.getUser().postValue(user);
}
});
You replace the reference to the object inside UserModel, try to swap the lines of code
data.postValue(user);
viewModel.setUser(data); // Why it does not call observe()
replace on
viewModel.setUser(data); // Why it does not call observe()
data.postValue(user);
Try to modify your code as #niqueco mentioned, set your updated method inside setUser() method and change your onclick() listener in the activity to send the new user data info only. Other works the LiveData will help u.
public class UserModel extends ViewModel {
private LiveData<User> user;
public void init() {
if (this.getUser() != null) {
return;
}
this.user = new LiveData<User>() {
#Override protected void setValue(User value) {
value.setName("Fresh New Name");
super.setValue(value);
}
};
}
public LiveData<User> getUser() {
return user;
}
public void setUser(LiveData<User> user) {
this.user.setValue(user); //the live data will help u push data
}
}
Activity Class
public class PScreen extends BaseActivity {
#Override protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.places_screen);
final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
viewModel.init();
viewModel.getUser().observe(this, new Observer<User>() {
#Override public void onChanged(#Nullable User user) {
((TextView) findViewById(R.id.name)).setText(user.getName());
}
});
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
//final MutableLiveData<User> data = new MutableLiveData<>();
User user = new User();
user.setName(String.valueOf(Math.random() * 1000));
//data.postValue(user);
viewModel.setUser(user); // Why it does not call observe()
}
});
}
}

Categories

Resources