I'll try to describe my situation:
I have MyFragment extends Fragment with overriding onCreateView().
In MyFragment.java
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.my_fragment, null);
//some manipulation with view
return view;
in my_fragment.xml
<LinearLayout
.....
>
<com.example.widgets.MyMapWidget
android:id="#+id/my_map"
android:layout_width="match_parent"
android:layout_height="300dp"
/>
</LinearLayout>
in MyMapWidget.java
public class MyMapWidget extends LinearLayout {
public MyMapWidget(final Context context, AttributeSet attrs) {
super(context, attrs);
((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.widget_map, this, true);
...
}
in widget_map.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<!-- Some another views-->
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/my_map_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment"
/>
<!-- Some another views-->
</LinearLayout>
When I first time show MyFragment - all works great. But if then I show another fragment (with cleaning back stack) and then MyFragment again - I got Inflate Exception
Stacktrase
android.view.InflateException: Binary XML file line #151: Error inflating class <unknown>
at android.view.LayoutInflater.createView(LayoutInflater.java:606)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:680)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:739)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:742)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:742)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:742)
at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
at `com.***.****.fragments.MyFragment.onCreateView(MyFragment.java:78)`
Line #78 in MyFragment.java (method onCreateView)
View view = inflater.inflate(R.layout.my_fragment, null);
Removing MyMapWidget from my_fragment.xml solved problem. But I have some questions:
Why it happens?
Can I show map like this?
May be you know another way how present map like part of the own Fragment?
NOTE: I checked answer on similar question, but can't solve my situation.
You can have the map in it's own fragment like this:
public class GoogleMapFragment extends MapFragment {
private static final String SUPPORT_MAP_BUNDLE_KEY = "MapOptions";
public GoogleMapFragment() {
mCallback = null;
}
public static interface OnGoogleMapFragmentListener {
void onMapReady(GoogleMap map);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
}
public static GoogleMapFragment newInstance() {
return new GoogleMapFragment();
}
public static GoogleMapFragment newInstance(GoogleMapOptions options) {
Bundle arguments = new Bundle();
arguments.putParcelable(SUPPORT_MAP_BUNDLE_KEY, options);
GoogleMapFragment fragment = new GoogleMapFragment();
fragment.setArguments(arguments);
return fragment;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallback = (OnGoogleMapFragmentListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(getActivity().getClass().getName()
+ " must implement OnGoogleMapFragmentListener");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (mCallback != null) {
mCallback.onMapReady(getMap());
}
return view;
}
private OnGoogleMapFragmentListener mCallback;
}
And add it to your activity like this:
// create a new map
mapsFragment = GoogleMapFragment.newInstance();
// Then we add it using a FragmentTransaction.
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(android.R.id.content, mapsFragment, FRAGMENT_MAP_TAG);
fragmentTransaction.commit();
Let your activity implement OnGoogleMapFragmentListener, then when the map is added and ready the following method will be called:
#Override
public void onMapReady(GoogleMap map) {
//add markers or whatever
}
Hope this is useful for you to get a bit more control over your mapfragment.
I remember facing a similar issue a time ago. What worked for me is programmatically creating the map inside the fragment onCreateView method. Something like.-
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState, R.layout.fragment_layout);
setupMap();
return view;
}
private void setupMap() {
mapFragment = new SupportMapFragment();
ParentActivity activity = getParentActivity();
if (activity != null) {
FragmentTransaction fragmentTransaction = activity.getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.wrapperMapPlaces, mapFragment);
fragmentTransaction.commit();
}
}
I found the way, how solve it:
Detect event where MyFragment destroyed their view (onDestroyView)
In this method get instance of the MyMapWidget and call destroyMap.
myMapWidget.destroyMap will be:
public void destroyMap(FragmentManager fm){
fm.beginTransaction().remove(mMapFragment).commit();
}
Just remove fragment with map, when parent fragment destroed their view.
Related
I'm now following a viewpager guide in
http://developer.android.com/training/implementing-navigation/lateral.html
And I met a serious problem.
As I wrote in title, I tried to put the Google map v2 in each fragment of viewpager and I met this error. (Two ScreenSlidePageFragment gets generated at once and it inflates map for each them.)
Caused by: java.lang.IllegalArgumentException: Binary XML file line #8: Duplicate id 0x7f0a0059, tag null, or parent id 0xffffffff with another fragment for com.google.android.gms.maps.MapFragment
at android.app.Activity.onCreateView(Activity.java:4835)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:689)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
at com.maurat.trackuz.ScreenSlidePageFragment.onCreateView(ScreenSlidePageFragment.java:39)
I think because this viewpager loads 2 pages at once at the first time and the map fragment's id gets duplicated.
ScreenSlidePagerAdapter
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Log.d(TAG, "getItem " + position);
return new ScreenSlidePageFragment();
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
ScreenSlidePageFragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
Log.d(TAG, "onCreateView");
ViewGroup view = (ViewGroup) inflater.inflate(
R.layout.fragment_screen_slide_page,
container,
false);
return view;
}
fragment_screen_slide_page.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#C7F464">
<fragment
android:name="com.google.android.gms.maps.MapFragment"
android:id="#+id/map_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
So I've found and tried several solution but didn't found the solution for now.
https://stackoverflow.com/a/14929151/1122517
https://stackoverflow.com/a/14695397/1122517
Now I'm finding a solution such like making a fragment programmatically.
Please help me.
Ok, I found a solution and it's maybe workaround but work perfectly.
What I wanted to make is a viewpager having map fragment.
First of all I made a basic of viewpager reference to below site
Using ViewPager for Screen Slides
and make a fragment class which to be a parent of 2 nested fragments
A fragment class for 2 nested fragments
public class TPageParentFragment extends Fragment {
private int position = -1;
/* Constructor */
public TPageParentFragment(int position) {
this.position = position;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.fragment_screen_slide_page, container, false);
return rootView;
}
#Override
public void onResume() {
Log.d(TAG, "onResume : " + this.position);
super.onResume();
// Here I add 2 nested fragments
FragmentManager mFragmentManager = getChildFragmentManager();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
Fragment mapFragment = new TMapFragment();
Fragment mapInfoFragment = new TMapInfoFragment();
fragmentTransaction.add(R.id.fragContainer1, mapFragment);
fragmentTransaction.add(R.id.fragContainer2, mapInfoFragment).commit();
}
}
And the xml of this parent fragment is
fragment_screen_slide_page.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#C7F464">
<FrameLayout
android:id="#+id/fragContainer1"
android:layout_width="fill_parent"
android:layout_height="300dp"/>
<FrameLayout
android:id="#+id/fragContainer2"
android:layout_below="#+id/fragContainer1"
android:layout_width="fill_parent"
android:layout_height="500dp"/>
</RelativeLayout>
All of these FrameLayout will be changed with 2 nested fragments.
And 2 nested fragments are
TMapFragment
import com.google.android.gms.maps.SupportMapFragment;
public class TMapFragment extends SupportMapFragment {
public TMapFragment(){}
#Override
public View onCreateView(LayoutInflater arg0, ViewGroup arg1, Bundle arg2) {
Log.d(TAG, "onCreateView");
View v = super.onCreateView(arg0, arg1, arg2);
initMap();
return v;
}
private void initMap() {
Log.d(TAG, "initMap");
}
}
and
TMapInfoFragment
public class TMapInfoFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.fragment_mapinfofragment, container, false);
return rootView;
}
}
Surely you need to add a xml for fragment_mapinfofragment
I want to make an empty DialogFragment with a LinearLayout and then change fragments inside the LinearLayout. For example, a login where the first fragment is 3 button (facebook, google+, email login) and when somebody pressed email then the 2. fragment has a layout with EditTexts if Google or Facebook was pressed then the other fragment appears with a ProgressBar.
this is my empty dialog layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:background="#drawable/dialog_background">
<LinearLayout
android:id="#+id/testFragmentController"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_margin="15dp"></LinearLayout>
</RelativeLayout>
And this is the first fragment's code (I am using android annotations):
#EFragment(R.layout.dialog)
public class FragmentGeneralDialog extends ClickDialogFragment {
#ViewById
LinearLayout testFragmentController;
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
this.setStyle(R.style.Dialog_No_Border, getTheme());
return dialog;
}
#AfterViews
void afterViews() {
loadActivity();
GlobalData.setFragmentContainer(testFragmentController);
activity.loadMenuFragment(FragmentSocialDialog_.class, new SlideLeftAnimation());
}
}
loadMenuFragments(...) is this:
public <T extends Fragment> T loadMenuFragment(final Class<T> cls,
final IAnimation a) {
T fragment = null;
try {
fragment = cls.newInstance();
} catch (Exception ex) {
throw new RuntimeException("Fragment " + cls.toString()
+ " has no empty constructor");
}
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
if (a != null) {
transaction.setCustomAnimations(a.getInId(), a.getOutId(), a.getInId(),
a.getOutId());
}
transaction.replace(R.id.testFragmentController, fragment);
try {
transaction.commit();
} catch (Exception e) {
}
return fragment;
}
You need to get the childFragmentManager from the dialogfragment link, then from the child fragments you can change the fragments via getParentFragment().getChildFragmentManager()
I found a better way than getParentFragment. You can in parent fragment add public static FragmentManager and use it in nested fragment:
In parent:
public class SelectProductDialogFragment extends DialogFragment {
public static FragmentManager fragmentManager;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dialog_add_product,container,false);
fragmentManager = getChildFragmentManager();
return v;
}
}
In child: for example for replace transaction:
ProductsGridFragment productsGridFragment = new ProductsGridFragment();
FragmentTransaction transaction = SelectProductDialogFragment.fragmentManager.beginTransaction()
.replace(R.id.dialog_order_container,productsGridFragment)
.addToBackStack(null);
transaction.commit();
I've got a FragmentActivity which show the correct fragment, using a ViewPager. I added a Fragment StatePagerAdapter to the ViewPager. If I push a button at a Fragment, it should be recreated (onCreateView should be called again). The fragments are articles and if the user is offline, I'll show him "please check your networkconnection" and a button to reload. The button called a method in the FragmentActivity, but I don't know how implement it. Maybe I can destroy and recreate the current Fragment?
here the important part of the FragmentActivity:
private ArrayList<Fragment> fragments;
protected void onCreate(Bundle savedInstanceState)
{
context = this;
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_article_page_slider);
...
pages = datahandler.getCount(...);
viewPager = (ViewPager) findViewById(R.id.articlePager);
PagerAdapter pagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager());
viewPager.setAdapter(pagerAdapter);
viewPager.setCurrentItem(startPosition);
fragments = new ArrayList<Fragment>(pages);
}
public void reloadData(View v)
{
// TODO
((ArticlePageFragment)fragments.get(viewPager.getCurrentItem())).refresh();
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter
{
public ScreenSlidePagerAdapter(FragmentManager fm)
{
super(fm);
}
#Override
public Fragment getItem(int position)
{
Fragment fragment = new ArticlePageFragment();
Bundle args = new Bundle();
...
fragment.setArguments(args);
fragments.add(position, fragment);
return fragment;
}
public int getCount()
{
return pages;
}
}
and the Fragment itself:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
activity = getActivity();
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.fragment_article_page, container, false);
CacheDataHandler cdh = new CacheDataHandler(activity);
cdh.open();
String htmlData = cdh.loadData();
rootView.findViewById(R.id.btn_reload_article_data).setVisibility(View.GONE);
webView = ((WebView) rootView.findViewById(R.id.articleFragment));
refresh();
...
return rootView;
}
public void refresh()
{
...
if (htmlData == null)
{
rootView.findViewById(R.id.btn_reload_article_data).setVisibility(View.VISIBLE);
webView.loadData(defaultdata,"text/html", "UTF-8");
}
else
{
webView.loadData(htmlData,"text/html", "UTF-8");
}
}
and the fragment_article_page.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<WebView
android:id="#+id/articleFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<Button
android:id="#+id/btn_reload_article_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:onClick="reloadData"
android:text="#string/btn_txt_reload_data"
android:visibility="gone" />
</RelativeLayout>
EDIT
added a private ArrayList<Fragment> fragments in FragmentActivity and transfer the code for manipulating the view in an extra refreshMethod, look ahead. All works fine, if I only click on the first or second Fragment (index 0 and 1). Otherwise I get an IndexOutOfBoundsException. How to initialize and fill the ArrayList?
My app has a list and detail view, where I allow the user to swipe between the different detail views, using a ViewPager in portrait. In landscape, I show both fragments on the screen.
I'm having an issue where, when the device is rotated, the icons are duplicating themselves over and over in the Actionbar. I've learned that I'm probably creating new fragments whenever the device is rotated and I should be checking if the fragment is in the FragmentManager and then create a new instance if it isn't.
I am able to do that easily using the landscape layout, however, I'm not sure how to check if the fragment exists in portrait mode, using the ViewPager. Do I do that in getItem() of the FragmentStatePagerAdapter?
This is the code I'm using:
public class Main extends SherlockFragmentActivity
{
private static List<Integer> mIds;
#Override
public void onCreate(final Bundle icicle)
{
super.onCreate(icicle);
setContentView(R.layout.main);
mViewPager = (ViewPager)findViewById(R.id.viewpager); //view pager exists, so we are using the portait layout
if (mViewPager != null)
{
mIds = new ArrayList<Integer>();
mIds.add(0);
mIds.add(1);
mIds.add(2);
}
else //in landscape
{
ListFragment lf = (ListFragment)getSupportFragmentManager().findFragmentById(R.id.fragmentList);
if (lf == null)
lf = new ListFragment();
DetailFragment df = (DetailFragment)getSupportFragmentManager().findFragmentById(R.id.fragmentDetail);
if (df == null)
{
df = new DetailFragment();
df.setArguments(getIntent().getExtras());
}
getSupportFragmentManager().beginTransaction().add(R.id.fragmentList, lf).commit();
getSupportFragmentManager().beginTransaction().add(R.id.fragmentDetail, df).commit();
}
}
private static class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
//can't use getSupportFragmentManager().findFragmentById() here because I get a "Cannot make a static reference to the non-static method" error
if (index == 0)
return ListFragment.newInstance();
else
return DetailFragment.newInstance(mIds.get(index-1));
}
#Override
public int getCount() {
return 4;
}
}
}
Main Layout (portrait)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_height="match_parent"
android:layout_width="match_parent"
/>
</RelativeLayout>
Main Layout (landscape)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:weightSum="3"
>
<FrameLayout
android:id="#+id/fragmentList"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="fill_parent"
/>
<FrameLayout
android:id="#+id/fragmentDetail"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="fill_parent"
/>
</LinearLayout>
List Fragment
public class ListFragment extends SherlockListFragment implements DialogKeywordAddListener
{
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
public static ListFragment newInstance() {
return new ListFragment();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.listing, container, false);
}
}
Detail Fragment
public class DetailFragment extends SherlockListFragment
{
private int mId;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
public static DetailFragment newInstance() {
DetailFragment df = new DetailFragment();
Bundle bundle = new Bundle();
bundle.putInt("id", id);
df.setArguments(bundle);
return df;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (getArguments() != null)
mId = getArguments().getInt("id");
return inflater.inflate(R.layout.detail, container, false);
}
}
Well, this question's solution seemed to work for me. Just setting:
super.onCreate(null);
in onCreate of the Activity solved my fragment issue.
I want to show a Button and a PieChart below in a tab.
Thats the code I worked with: https://github.com/JakeWharton/ActionBarSherlock/blob/master/samples/fragments/src/com/actionbarsherlock/sample/fragments/FragmentTabs.java
Instead of the CountingFragment etc. I implemented my own.
I'm not sure what to
My xml:
fragment_graph_categories.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_graph_categories"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/button_filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/button_filter" />
</LinearLayout>
And that's my "Tab" attached to the TabManger
public class GraphCategories extends SherlockFragmentActivity {
GraphCategoriesFragment mCategoriesFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_graph_categories);
if (savedInstanceState == null) {
// Do first time initialization -- add initial fragment.
mCategoriesFragment = GraphCategoriesFragment.newInstance(1);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragment_graph_categories, mCategoriesFragment).commit();
} else {
//mStackLevel = savedInstanceState.getInt("level");
}
}
#Override
protected void onResume() {
if(mCategoriesFragment != null) {
//mCategoriesFragment.onResumeRedraw();
}
}
//notice Inner class
public static class GraphCategoriesFragment extends SherlockFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//View mChartView = inflater.inflate(R.layout.hello_world, container, false);
View rlayout = inflater.inflate(R.layout.fragment_graph_categories, container, false);
//? rlayout = new LinearLayout(getActivity());
return rlayout;
}
}
}
But im not sure what to inflate at the fragments onCreateView or if I need a FrameLayout in my xml?
I tried some options but don't figured it out.