I would like to see DialogFragment after pressing the button, I have two code snippets:
First:
if (view.equals(b1)) {
Fragment2 fr2 = new Fragment2();
fr2.show(manager, "addCity");
}
I don't understand why this tag is in the show () method, since it has no effect on program change.
Second:
Fragment fr = manager.findFragmentByTag("addCity");
if (view.equals(b1)) {
if (fr != null) {
manager.beginTransaction().remove(fr).commit();
}
Fragment2 fr2 = new Fragment2();
fr2.show(manager, "addCity");
}
In the second example, I don't understand what this line of code is for:
Fragment fr = manager.findFragmentByTag("addCity");
Since the reference variable fr will always be null because there is currently no fragment under the name of such a tag.
In addition, why does this condition appear, since just the previous change fr will always be null, so this if will never come true.
if (fr != null) {
manager.beginTransaction().remove(fr).commit();
}
When you use show(manager, "addCity"), then second parameter is the tag for the Fragment. By using findFragmentByTag() with the same tag, you're looking to see if the DialogFragment already exists and, if it does (fr != null), then remove it.
This is very defensive code, probably made in an attempt to avoid users very, very quickly double tapping the button. However, because it doesn't use showNow() (instead of show()), it actually doesn't do a good job at this because show() is asynchronous.
In general, you don't need this code at all - just call show() without any of the ceremony, using whatever tag you want (the tag only matters if you're later trying to use findFragmentByTag() to retrieve your DialogFragment after the fact).
But if you do want to be defensive and avoid even the extremely rare chance that the user opens up two dialogs, then you need to
1) Use showNow() instead of show() so that the FragmentManager is immediately updated, ensuring that findFragmentByTag() actually does return the Fragment in that case
2) Instead of removing and then calling show() again, just don't call show() if it already being shown - you're just doing extra work.
This would mean your code would look like
if (view.equals(b1)) {
Fragment existingDialog = manager.findFragmentByTag("addCity");
// Only add a new dialog if it isn't already present.
if (existingDialog == null) {
Fragment2 fr2 = new Fragment2();
fr2.showAll(manager, "addCity");
}
}
Related
Hi I am a beginner to programming. I am trying to learn Android development from udacity.
Cursor cur = someFunction();
if(cur != null){
Intent intent = new Intent(getActivity(), two.class);
intent.setData(cur.getLong(2));
startActivity(intent)
}
Now in two class.
Intent intent = getActivity.getIntent();
if(intent != null){
mString = intent.getDataString();
}
if(null != mString){
mTextView.setText(mString);
}
I was just wondering why do we require so many ifs in this code. The if in first piece of code is fine.
When the second activity was called from first activity. Why do we need to check if the intent is null? Since it is the only entry point to the second activity.
And why do we again need to check if the string is null before assigning it to Text View?
Just because you see it in some code, that doesn't mean it's required.
The check for null on the first condition isn't necessary. The code here looks like it's being very paranoid that getIntent() might return null.
In the second condition, there is nothing that's telling the compiler that there is a guarantee that the return string will definitely not be null. In fact, the documentation states that it may return null, so it's safe to always check. You can put a null string into a TextView, but that isn't necessarily what's desired. It's hard to tell without context.
Because if you don't check if something is null and try to invoke a method on it, it will cause a NullPointerException. That's why only after you've confirmed that the Intent is not null, you can call the method
intent.getDataString();
I'm using a fragment with two primary views that have setVisibility() to show or hide based on the results of an AsyncTask used to search for data online.
For example, here is the method to switch between Views:
private void switchView()
{
Log.d(LOG_TAG, "switchView(): show = " + show);
mListView.setVisibility(show ? View.VISIBLE : View.GONE);
searchView.setVisibility(show ? View.GONE : View.VISIBLE);
mCompanyArrayAdapter.notifyDataSetChanged();
}
I'm taking this approach so that when the AsyncTask is complete it can parse the data into wrapper classes and create an ArrayList of these objects for use in the ListView adapter. (If there is another way to pass custom classes to another fragment, I would be open to using that.)
Once result == true from Async it hides the searchView and shows the mListView. However on rotate, the screen returns back to the searchView instead of continuing to display the mListView results.
I'm confused by the Log output that shows what I believe to two calls to onCreateView from the same fragment (DiscoverFragment), seen here:
10-17 10:29:21.872 6603-6603/nz.co.exium.panther D/DiscoverFragment﹕ onCreateView: savedinstancestate is null = false
10-17 10:29:21.877 6603-6603/nz.co.exium.panther D/AbsListView﹕ Get MotionRecognitionManager
10-17 10:29:21.882 6603-6603/nz.co.exium.panther D/DiscoverFragment﹕ onCreateView: savedinstancestate is null = true
So the first shows that the savedInstanceState is found != null and sets the boolean value appropriately but then another onCreateView() is called where savedInstanceState is null and sets it back to false. Why the double onCreateView() after rotate?
Thanks for the help.
As requested, code that replaces the fragments in the MainActivity (using a FrameLayout for the content_frame and position == DrawerLayout position item).:
getFragmentManager().beginTransaction()
.replace(R.id.content_frame, Fragment.instantiate(
getApplicationContext(), mClasses[position]))
.commit();
From my MainActivity.onCreate():
if (savedInstanceState == null) {
selectItem(0);
} else if (placeID == null || placeID.isEmpty()) {
selectItem(savedInstanceState.getInt(SELECTED_KEY));
} else {
selectItem(2);
}
I already perform a check for a previous savedInstanceState, if null set the default Fragment. If the variable placeID (from a BroadcastReceiver) is null/empty then restore previous state. Else, go to the Fragment that will display the Notifications data.
Fragments are automatically recreated on rotation as part of restoring the state. Therefore you should only call selectItem() when savedInstanceState == null - otherwise you'll get your restored fragment (with its restored state), then immediately replace it with a brand new instance (without the restored state).
So, what happens here is this:
Your activity is created, and one fragment is added to it
You rotate your device
Your activity gets recreated (along with the fragment previously added), and in onCreate, you add a new fragment, which means you have two fragments added in your activity
the easiest way to deal with this is to perform a check in your activity:
if (savedInstanceState == null){ // add fragment }
the other method is to prevent activity recreation by specifying config changes in your AndroidManifest.xml
Sorry I can't come up with a better question title because it's pretty hard to describe...
I was inspecting Android's source (4.4 KK), the View class in particular and this showed up:
// .... lots of stuff....
AttachInfo mAttachInfo;
// .... lots of stuff....
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
}
That's the View.post(Runnable) that we all love to use whenever we need something to run on the UI thread.
The thing I don't understand here is why do they create another local reference of attachInfo?
Why don't they do it like:
if (mAttachInfo != null) {
return mAttachInfo.mHandler.post(action);
}
Other than making attachInfo immutable in the method scope to prevent bugs(even thought they can still accidentally access mAttachInfo), I don't think there's any reason to do this.
Another possibility would be to shorten names, but I don't think it's worth shorting 1 character.
Is this a design pattern?
EDIT:
further inspecting the source reveals that they use this "pattern" in many places:
public void postInvalidateOnAnimation() {
// We try only with the AttachInfo because there's no point in invalidating
// if we are not attached to our window
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateOnAnimation(this);
}
}
Basically, they use it in almost every postXXXXXX() method.
EDIT2:
#CommonsWare pointed out that it might be used for anonymous inner class in previous versions, I checked the source of 1.5(Cupcake) ~ 2.3.3(Gingerbread) and this is what post() looks like
public boolean post(Runnable action) {
Handler handler;
if (mAttachInfo != null) {
handler = mAttachInfo.mHandler;
} else {
// Assume that post will succeed later
ViewRoot.getRunQueue().post(action);
return true;
}
return handler.post(action);
}
I still don't see why....
Bear in mind that post() may be called from background threads at the same time as mAttachInfo is updated on the UI thread. The code that was used up to Android 2.3.3 could throw an NPE if mAttachInfo was set to null after the if (mAttachInfo != null) check and before mAttachInfo.mHandler was accessed.
The current code avoids the NPE by taking a snapshot of mAttachInfo that doesn't change for the lifetime of the method.
The local variable doesn't strictly need to be final, but declaring it final makes it extra clear that it won't become null after the if (attachInfo != null) check.
I'm using https://github.com/etsy/AndroidStaggeredGrid/ in my project. Everything works fine, except I can't scroll it to specific position/item at will.
What I've tried so far (everything I could think of, enclosed in one piece of code for you to look at):
final StaggeredGridView gv = (StaggeredGridView) ma.findViewById(R.id.photosList);
gv.setChoiceMode(StaggeredGridView.CHOICE_MODE_SINGLE);
gv.setFocusable(true);
gv.clearFocus();
gv.post( new Runnable() {
#Override
public void run() {
// #1 approach
gv.requestFocusFromTouch();
gv.smoothScrollToPosition(index);
// #2 approach
gv.setSelection(index);
View v = gv.getChildAt(index);
if (v != null) {
// #3 approach
v.requestFocus();
}
//v.requestFocusFromTouch();
Utils.log("scroll to " + index);
gv.requestLayout();
}
});
Nothing seems to work and I'm begining to think this view has some kind of bug related to my issue. Is there someone who can help me in the matter?
I found that I wasn't able to scroll to a specific location, however, I did find that I was able to restore the state of the gridview. In my application I had to implement a custom back stack mechanism, and you can pass the state which you can get from:
Parcelable state = mgridView.onSaveInstanceState();
The state is a Parcelable, so i was able to putParcelable in an intent, and then retrieve it and restore the state as needed, like so
if(state != null) {
mgridView.onRestoreInstanceState(state);
}
Upon restoring the state, it restores the previous position in the grid exactly.
Hope this helps anyone else facing this issue!
I have an Edittext and a CheckedTextView in a listview item (the layout implements Checkable). When I click on the EditText, the bindView() method of the CursorAdapter calls ListView.setItemChecked() (I do this to restore CheckMarks that may have to be restored after clearing a filter). However, ListView.setItemChecked() seems to immediately take away the focus (or something else happening here?) from the EditText I just clicked, so now I can't use the EditText anymore.
I there a way to call ListView.setItemChecked() inside bindView() without such a sideeffect? I already tried calling ListView.setItemChecked() after adapter.getFilter().filter() (outside of bindView()) but it seems in this case the new item list is not available just yet, because the filtering is done in a background thread.
Any suggestions?
Found the answer: use FilterListener class like this
adapter.getFilter().filter("", new FilterListener() {
#Override
public void onFilterComplete(int count) {
for (int i = 0; i < adapter.getCount(); i++) {
if (selected.contains((int) adapter.getItemId(i)))
listView.setItemChecked(i, true);
}
}
});
EDIT: strictly speaking this is not an answer because I still don't know why setItemChecked can't be used in bindView() without intering with the edittext