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
Related
I have 2 types of the widgets in the app and single ConfigActivity for setting both of the widgets. To update widgets for the first time after configuration and for the reconfigurations later I have to press START button in the fragment of ConfigActivity. Before sending intent to update widget I define the type of the widget, using getAppWidgetInfo (code is below).
Sometimes getAppWidgetInfo.provider returns null.
// Button Start (Start Widget)
buttonStart.setOnClickListener {
val appWidgetManager = AppWidgetManager.getInstance(context)
// We have a single ConfigActivity for all the types of the widgets,
// thus we have to differ widget types.
// Get string with short name of widget provider (looks like ".WidgetTodayProvider"),
// using getAppWidgetInfo.provider.shortClassName.
// Delete dot from the string, using substring(1).
val widgetProviderShortName =
appWidgetManager.getAppWidgetInfo((activity as ConfigActivity).appWidgetId)
.provider.shortClassName
.substring(1)
// Update appwidget from ConfigActivity
when (widgetProviderShortName) {
"WidgetTodayProvider" ->
intent = Intent(
ACTION_UPDATE_AFTER_SETTING_TODAY, null, context,
WidgetTodayProvider::class.java
)
"WidgetPeriodProvider" ->
intent = Intent(
ACTION_UPDATE_AFTER_SETTING_PERIOD, null, context,
WidgetPeriodProvider::class.java
)
}
intent.putExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
(activity as ConfigActivity).appWidgetId
)
activity?.sendBroadcast(intent)
activity?.setResult(RESULT_OK, (activity as ConfigActivity).resultValue)
activity?.finish()
}
Description of the error: Attempt to read from field 'android.content.ComponentName android.appwidget.AppWidgetProviderInfo.provider' on a null object reference
The documentation of getAppWidgetInfo says:
If the appWidgetId has not been bound to a provider yet, or you don't have access to that appWidgetId, null is returned.
But this doesn't explain how, why and under what conditions it's possible, when appWidgetId is not bound to provider.
Why in the most cases everything is ok, but only sometimes getAppWidgetInfo returns null? How to control this and avoid this problem?
Could somebody explain me, please?
I have a main fragment and two fragments in view pager (About/Reviews).
I call api to load data when navigation bar selected. I want to data model I have called api can be used between three fragments. I don't want to call api three times when the fragments load.
ok you want to call the api only once , that is when the activity is created right ?
ok for that initialize a int variable and set the value to 0;
int a=0;
then use condition to call your api
if(a==0)
{
//Your code To call Api
a=1;
}
So Here After U call Your Api Once "a is set to 1" which does not satisfy the condition and it does not call the api for the second time ...
but "a=0" when the your class or activity is created or called ..the api is also called
This Solution Is Given , Keeping That The Activity Is Not Recalled Or Recreated Unnecessarily ( Or The Activity Is Not Recalled/Recreated On Changing The Fragment )
when you create the fragment just pass the data.
Fragment fragment = new DemoFragment();
Bundle args = new Bundle();
args.putString("TERM", "FINEL TERM");
fragment.setArguments(args);
You can receive data from the fragment
Bundle args = getArguments();
String termStatus = args.getString("TERM")
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");
}
}
I am dealing with a certain problem in android and I struggle to find a viable solution.
The situation is as follows:-
I have 4 activities A,B,C and D and two possible paths :
Path 1 : A,B,C,D
Path 2 : A,B',D
B and B' refer to the same activity but with different views ( a recyclerview is used in here ) depending on the activity I am initially ie B or A.
What I want to do is basically follow the paths like that : A,B,C,D ( direct ) and D,C,B,A ( when back is constantly pressed ). The same with the second one : A, B', D ( direct ) and D,B',A ( when back is constantly pressed ).
I have a lot of problems when back is pressed so I know that I have to override onBackPressed. But how exactly can I figure out in which case I am ( path 1 or path 2 )?
Thanks for your help,
Try this in one.
// Add activity
public static void addActivities(String actName, Activity _activity) {
if (Config.screenStack == null)
Config.screenStack = new HashMap<String, Activity>();
if (_activity != null)
Config.screenStack.put(actName, _activity);
}
// Remove Activity
public static void removeActivity(String key) {
if (Config.screenStack != null && Config.screenStack.size() > 0) {
Activity _activity = Config.screenStack.get(key);
if (_activity != null) {
Config.screenStack.remove(key);
_activity.finish();
}
}
}
User add activities before setContentView to add into the stack.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addActivities("DemoActivity", DemoActivity.this)
setContentView(R.layout.activity_create_feed_post);
}
If you want to finish all activity when you exist from app you can see this code.
I have a ListView within my project. It has many elements, and it uses a custom adapter, since its populated dynamically from a rails server.
I want to change the content of a ListItem when the item is longpressed. In order to achieve this, I have 2 layouts inside the ListItem, with one visible and one hidden.
The issue is that when I longpress an item, the layout changes (As expected), but other ListItems are also affected, and changed in the same way. This appear to occur once for every 5 items, and I cant figure out why.
This is the LongClickListener I'm using, it is located inside de GetView method on the custom adapter:
View v = convertView;
if (v == null){
LayoutInflater vi =
(LayoutInflater)getActivity().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.list_item, null);
}
final LinearLayout placeInfo =
(LinearLayout) v.findViewById(R.id.list_item_info);
final RelativeLayout placeBrief =
(RelativeLayout)v.findViewById(R.id.list_item_brief);
v.setOnLongClickListener(new OnLongClickListener(){
#Override public boolean onLongClick(View v) {
placeInfo.setVisibility(View.GONE);
placeBrief.setVisibility(View.VISIBLE);
return false;
}});
I would appreciate any help, many thanks in advance.
ListViews recycle Views, so you only have a few views for all of your items. You're directly changing one of these view instances to switch between the info|brief. What you need is to save the status of the info|brief flag for the affected position somewhere else (e.g. a list of positions that should be "briefs" in the adapter). That way when you come back into getView() you can display the right one.