I'm using Searchview to filter through firebase entries displayed in a Recyclerview. I want the Recyclerview to be gone when a search is not made, which is working fine until I go back from the the searched item to the main activity. The recyclerview list is visible again after a search is complete. I want it to always be hidden whenever a search is not made. Here is the relevant code:
msearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String mQuery) {
/*while (query.length() > 4) {
mRecyclerView.setVisibility(View.VISIBLE);
} */
if (!(mQuery == null || mQuery.trim().isEmpty())) {
mRecyclerView.setVisibility(View.VISIBLE);
}
else {
mRecyclerView.setVisibility(View.GONE);
}
firebaseSearch(mQuery);
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
//filter as typed
firebaseSearch(newText);
return true;
}
});
return super.onCreateOptionsMenu(menu);
}
Let's imagine that you have 2 activities: A and B.
After you click on item on activity A, you go to activity B, but this time, acitivity A is not completely destroyed. So the state of input text and recylcerView will remain. After you go back from activity B to activity A, you better perform following action to clear the search view:
msearchView.setQuery("",false);
msearchView.setIconified(true);
Since the searchView is cleared, onQueryTextChange() will be executed. You can check the condition here.
A few things to consider, I think that just checking query != null might not always be correct. It might be empty or in theory blank. I would personally use something like:
if (query == null || query.trim().isEmpty()) {
// No query made
}
Next, it's possible I didn't quite understand, I believe you have Activity A where the user searches. Once they search you show the recycler view. Then they click on an item and you jump to Activity B. When the user navigates back to A, the list is still full. If this is right you have to remember that A isn't necessarily destroyed when B is visible on top of it. If the phone you're on has low memory the OS might call onDestroy and have to recreate it when you navigate back. Anyway... a simple fix here is to hide the RecycleView when you navigate to B.
Related
I am kinda new to Android programming. I am building a simple application and I have a "follow-unfollow" concept on it. What I simply want is, if the current user follows the user he/she is exploring, I want him/her to see "unfollow" button. If not following, there should be a "follow" button. On my UserProfileActivity class I have a method called onPrepareOptionsMenu() and inside this method I can set the buttons.
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem follow = menu.findItem(R.id.action_follow);
MenuItem unfollow = menu.findItem(R.id.action_unfollow);
Bundle bundle = this.getIntent().getExtras();
if(isFollowing(bundle.getString("userid")) == true){
follow.setVisible(false);
unfollow.setVisible(true);
}
else{
follow.setVisible(true);
unfollow.setVisible(false);
}
return super.onPrepareOptionsMenu(menu);
}
Also, I have another method called isFollowing() and it returns a boolean "true" if current user follows the other user, it returns "false" if not. It is the simplest way that I have thought to solve this issue.
public boolean isFollowing(String userID){
isFollowingResult = false;
firebaseDatabase = FirebaseDatabase.getInstance();
databaseReference = firebaseDatabase.getReference();
final DatabaseReference followingData = databaseReference.child("followingData");
followingData.child(currentUser.getUid()).child(userID).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists())
isFollowingResult = true;
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return isFollowingResult;
}
When I run this, and when I click on a user's profile onPrepareOptionsMenu() method is called and inside it isFollowing() method is called. The problem is, it does not wait for the isFollowing() method to run and finish running and it immediately sees it as "false" and always shows "follow" button. Any suggestion would be appreciated.
Thanks.
Make follow and unfollow the fields of the class. Then change their visibility in onDataChange(DataSnapshot) method.
There are multiple issues with the code.
Firstly, your isFollowing() function is setting up the listener, on the data field, but it'll only get called when the data changes. In this case, you may only want to read the data once:
https://firebase.google.com/docs/database/android/read-and-write#read_data_once
Secondly, the use of a listener implies asynchronicity. Meaning, you'll need to wait until you get the callback later in order to get the value you want.
The ideal solution in order to maintain responsiveness of your app is to maintain a local "copy" of the value in your database with a listener that constantly updates that value. That way, you can query the state of your variable quickly (since it's stored/replicated locally) and still be up to date with your database (with the listener).
This will also prevent each "read" from going all the way to the service and back and also remove the need for your UI to wait to render correctly (accurately).
Cannot find a direct answer to this so i'll ask here.
If I have one activity which starts with Fragment A attached and then when a button on A is pressed it replaces fragment A with fragment B. A is added to back stack. If the back button is pressed it will go from B back to A but I want to know does the fragment B get destroyed? because technically the activity is not so do I need to explicitly remove fragment B even when the back stack is pop?
Thank you for reading
According to the Android API, it is destroyed, it is stopped.
http://developer.android.com/guide/components/fragments.html#Lifecycle
Handling The Fragment Lifecycle
The most significant difference in lifecycle between an activity and a fragment is how one is stored in its respective back stack. An activity is placed into a back stack of activities that's managed by the system when it's stopped, by default (so that the user can navigate back to it with the Back button, as discussed in Tasks and Back Stack). However, a fragment is placed into a back stack managed by the host activity only when you explicitly request that the instance be saved by calling addToBackStack() during a transaction that removes the fragment.
Thanks Syed Ahmed Jamil, see his comment below.
Nope, it is not destroyed, its stopped
its states go like this
(while interacting)
1.) onAttach(Activity) called once the fragment is associated with its activity.
2.) onCreate(Bundle) called to do initial creation of the fragment.
3.) onCreateView(LayoutInflater, ViewGroup, Bundle)
4.) onActivityCreated(Bundle)
5.) onViewStateRestored(Bundle)
6.) onStart()
7.) onResume()
while not interacting
1.) onPause()
2.) onStop()
3.) onDestroyView()
4.) onDestroy()
5.)onDetach()
One back stack of fragment manager means one fragments operation you planed.
FragmentManagerImpl holds a field "mBackStack" deals with such thing.
According to Android API:
"... Before you call commit(), however, you might want to call addToBackStack(), in order to add the transaction to a back stack of fragment transactions. This back stack is managed by the activity and allows the user to return to the previous fragment state, by pressing the Back button."
Let's see what happens in Activity.onBackPressed():
/**
* Called when the activity has detected the user's press of the back
* key. The default implementation simply finishes the current activity,
* but you can override this to do whatever you want.
*/
public void onBackPressed() {
if (mActionBar != null && mActionBar.collapseActionView()) {
return;
}
FragmentManager fragmentManager = mFragments.getFragmentManager();
if (fragmentManager.isStateSaved() || !fragmentManager.popBackStackImmediate()) {
finishAfterTransition();
}
}
If the activity is instance of FragmentActivity, the fields "final FragmentController mFragments" holding an instance of FragmentManagerImpl would call to popBackStackImmediate().
popBackStackImmediate() removes the top one of "mBackStack" and adds it to the "mTmpRecords", a executing used back stack buffer, and marks "mTmpIsPop" that it needed to operate as pop out action.
What does "is pop" mean?
It means the your fragments operations recorded in this one back stack will be executed reversely.
To see executeOps() and executePopOps() of BackStackRecord to find out more:
void executeOps() {
final int numOps = mOps.size();
for (int opNum = 0; opNum < numOps; opNum++) {
final Op op = mOps.get(opNum);
final Fragment f = op.fragment;
if (f != null) {
f.setNextTransition(mTransition, mTransitionStyle);
}
switch (op.cmd) {
case OP_ADD:
f.setNextAnim(op.enterAnim);
mManager.addFragment(f, false);
break;
and
void executePopOps(boolean moveToState) {
for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) {
final Op op = mOps.get(opNum);
Fragment f = op.fragment;
if (f != null) {
f.setNextTransition(FragmentManagerImpl.reverseTransit(mTransition),
mTransitionStyle);
}
switch (op.cmd) {
case OP_ADD:
f.setNextAnim(op.popExitAnim);
mManager.removeFragment(f);
break;
Firstly you create a back stack after beginTransaction(), then add several fragments operation after add(), remove(), ..., and commit() it. FragmentManagerImpl will execute your fragments plan by calling its executeOps()
So maybe you can see fragment A is hided, fragment B shows as your plan on screen.
In the case you press the back button, activity callback function calls to popBackStackImmediate(), FragmentManagerImpl will execute reversely your fragments plan by calling its executePopOps().
So maybe you can see fragment A shows, fragment B is hided as a back button action instinctively.
I am looking for something like an Item Changed Event or Item Count Changed Event for JavaFX ListView control or lets just say in general for any collection type control.
It is because, I have some Buttons, that I want to be enabled only when there is at least one item in ListView otherwise that Button should be in disabled state.
It is my guess that perhaps adding a ChangeListener to the ListView control. would that be a right approach.
Any suggestions how can we achieve this.
The JavaFX Listview provides a method with the signature
public final ObservableList<T> getItems()
You can add a listener to the observable list which will be called whenever items are added to or removed from the ListView.
aListView.getItems().addListener(new ListChangeListener() {
#Override
public void onChanged(ListChangeListener.Change change) {
System.out.println("Detected a change! ");
}
});
Similar functionality is also provided by the other 'collection' controls.
Add a binding based on the ListView items using Bindings.isEmpty
button.disableProperty().bind(Bindings.isEmpty(listView.getItems()));
This will also work when someone calls ListView#setItems and completely changes the observable list:
InvalidationListener updateScrollBarListener = ...
aListView.itemsProperty().addListener((obs, old, current) -> {
if(old != null) {
old.removeListener(updateScrollBarListener);
}
if(current != null) {
current.addListener(updateScrollBarListener);
}
});
I've been looking for a way to to make this a bit shorter (perhaps with Bindings) but haven't found it so far.
I have an activity in Android that takes the result of a web service and puts it into an adapter to display a list of items. I've created a listener interface for this called OnLoadCompleteListener and my listener for this activity looks like
mListener = new OnLoadCompleteListener() {
#Override
public void onPostExecute(RootWebService service) {
if(service instanceof WebService) {
//gets the number of items
int total = ((WebService) service).getCount();
for(int i=0; i<total; ++i) {
//Log.d("ITEM", "inserting " + i);
Item item = ((WebService) service).get(i);
//our adapter class automatically receives items
mAdapter.addItem(item);
}
}
}
};
Now this is the bizarre bit: the listener is getting hit, total is being set to 12 (the number of items I asked for) and then the for loop is being bypassed altogether. The adapter remains empty and the screen displays pretty much nothing.
However, if the commented out Log.d(...) call is made active, the loop works.
Why? The callback is being run from the UI thread, the getCount() method is returning the correct value, and this adapter has worked before. What on earth is happening?
By the way, mAdapter is an instance of a subclass of Adapter I wrote for the purpose, and the addItem(Item) method looks like this:
public synchronized void addItem(Item item) {
mItems.add(item);
notifyDataSetChanged();
}
mItems is exactly what you would expect.
I believe you need to do something like invalidate or something along those lines which pretty much just refreshes the data in the adapter.
I think its this.
myListView.getAdapter().notifyDataSetChanged();
If that doesn't work it could be...
myListView.invalidate();
I was learning menu inflater and the tutorial I am following says we should return false in this function. However when I return true, I didn't notice any change or difference. So the question is:
What should I return and why?
Thanks
If you want the normal processing to happen, return false. Otherwise, return true.
See Documentation.
By default, when you return false, Android calls the Runnable associated with the item or runs the Intent which you can set using setIntent(...) on the MenuItem. If you don't want this to happen, you should return true.
Say you create the MenuItem as follows.
MenuItem menu1 = new MenuItem(this);
menu1.setIntent(myIntent);
Here myIntent is what you want to do when the menu item is being clicked. For ex: say your menu item launches GMail app to send an email, with the text on a text view on your activity.
In your onOptionsItemSelected() callback, you can check the value of the text view, and return false if the text view is not empty (you have something in the text box, you can fire the Intent to GMail) otherwise show a message box saying "Please type a message first" and return true, so Android won't fire the Intent.
public boolean onOptionsItemSelected (MenuItem item) {
if (textView.getText().trim().equals("")){
// show the message dialog
return true;
}
else {
// we have some message. We can let android know that
// it is safe to fire the intent.
return false;
}
}
Hope this helps...
Happy Coding.
if you handle the event return true, otherwise return false