3つのタブを持つアプリケーションがあります。
各タブには独自のレイアウトの.xmlファイルがあります。 main.xmlには独自のマップフラグメントがあります。アプリケーションが最初に起動したときに表示されるものです。
タブを切り替えるとき以外はすべてうまくいきます。 map fragmentタブに戻ろうとすると、このエラーが出ます。他のタブへの切り替えや他のタブ間の切り替えは問題なく動作します。
ここで何が間違っている可能性がありますか?
これが私のメインクラスと私のmain.xml、そして私が使う関連クラスです(一番下にエラーログもあります)
メインクラス
package com.nfc.demo;
import Android.app.ActionBar;
import Android.app.ActionBar.Tab;
import Android.app.Activity;
import Android.app.Fragment;
import Android.app.FragmentTransaction;
import Android.os.Bundle;
import Android.widget.Toast;
public class NFCDemoActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar bar = getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
bar.addTab(bar
.newTab()
.setText("Map")
.setTabListener(
new TabListener<MapFragment>(this, "map",
MapFragment.class)));
bar.addTab(bar
.newTab()
.setText("Settings")
.setTabListener(
new TabListener<SettingsFragment>(this, "settings",
SettingsFragment.class)));
bar.addTab(bar
.newTab()
.setText("About")
.setTabListener(
new TabListener<AboutFragment>(this, "about",
AboutFragment.class)));
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
}
// setContentView(R.layout.main);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
}
public static class TabListener<T extends Fragment> implements
ActionBar.TabListener {
private final Activity mActivity;
private final String mTag;
private final Class<T> mClass;
private final Bundle mArgs;
private Fragment mFragment;
public TabListener(Activity activity, String tag, Class<T> clz) {
this(activity, tag, clz, null);
}
public TabListener(Activity activity, String tag, Class<T> clz,
Bundle args) {
mActivity = activity;
mTag = tag;
mClass = clz;
mArgs = args;
// Check to see if we already have a fragment for this tab,
// probably from a previously saved state. If so, deactivate
// it, because our initial state is that a tab isn't shown.
mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
if (mFragment != null && !mFragment.isDetached()) {
FragmentTransaction ft = mActivity.getFragmentManager()
.beginTransaction();
ft.detach(mFragment);
ft.commit();
}
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (mFragment == null) {
mFragment = Fragment.instantiate(mActivity, mClass.getName(),
mArgs);
ft.add(Android.R.id.content, mFragment, mTag);
} else {
ft.attach(mFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
ft.detach(mFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT)
.show();
}
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical" >
<fragment
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/mapFragment"
Android:name="com.google.Android.gms.maps.MapFragment"
Android:layout_width="match_parent"
Android:layout_height="match_parent" />
</LinearLayout>
関連クラス(MapFragment.Java)
package com.nfc.demo;
import Android.app.Fragment;
import Android.os.Bundle;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;
public class MapFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.main, container, false);
}
public void onDestroy() {
super.onDestroy();
}
}
エラー
Android.view.InflateException: Binary XML file line #7:
Error inflating class fragment
at Android.view.LayoutInflater.createViewFromTag(LayoutInflater.Java:704)
at Android.view.LayoutInflater.rInflate(LayoutInflater.Java:746)
at Android.view.LayoutInflater.inflate(LayoutInflater.Java:489)
at Android.view.LayoutInflater.inflate(LayoutInflater.Java:396)
at com.nfc.demo.MapFragment.onCreateView(MapFragment.Java:15)
at Android.app.Fragment.performCreateView(Fragment.Java:1695)
at Android.app.FragmentManagerImpl.moveToState(FragmentManager.Java:885)
at Android.app.FragmentManagerImpl.attachFragment(FragmentManager.Java:1255)
at Android.app.BackStackRecord.run(BackStackRecord.Java:672)
at Android.app.FragmentManagerImpl.execPendingActions(FragmentManager.Java:1435)
at Android.app.FragmentManagerImpl$1.run(FragmentManager.Java:441)
at Android.os.Handler.handleCallback(Handler.Java:725)
at Android.os.Handler.dispatchMessage(Handler.Java:92)
at Android.os.Looper.loop(Looper.Java:137)
at Android.app.ActivityThread.main(ActivityThread.Java:5039)
at Java.lang.reflect.Method.invokeNative(Native Method)
at Java.lang.reflect.Method.invoke(Method.Java:511)
at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:793)
at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:560)
at dalvik.system.NativeStart.main(Native Method)
Caused by: Java.lang.IllegalArgumentException:
Binary XML file line #7: Duplicate id 0x7f040005, tag null, or
parent id 0xffffffff with another fragment for
com.google.Android.gms.maps.MapFragment
at Android.app.Activity.onCreateView(Activity.Java:4722)
at Android.view.LayoutInflater.createViewFromTag(LayoutInflater.Java:680)
... 19 more
Mattが答えを教えてくれるが、それは地図を再作成して再描画する原因となるが、それは常に望ましいとは限らない。何度も試行錯誤を重ねた結果、私には有効な解決策が見つかりました。
private static View view;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (view != null) {
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null)
parent.removeView(view);
}
try {
view = inflater.inflate(R.layout.map, container, false);
} catch (InflateException e) {
/* map is already there, just return view as it is */
}
return view;
}
良い測定のために、これはR.id.mapFragment(Android:id = "@ + id/mapFragment")を含む "map.xml"(R.layout.map)です:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/mapLayout"
Android:layout_width="match_parent"
Android:layout_height="match_parent" >
<fragment xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/mapFragment"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
class="com.google.Android.gms.maps.SupportMapFragment" />
</LinearLayout>
これが役立つことを願っていますが、悪影響がないことを保証することはできません。
編集:アプリケーションを終了して再度起動したときなど、悪影響がありました。アプリケーションは必ずしも完全にシャットダウンされているわけではないので(バックグラウンドでスリープ状態にするだけで)、以前に送信したコードはアプリケーションを再起動すると失敗します。私は、マップに出入りしてアプリケーションを終了して再起動することで、自分に合ったものにコードを更新しました。try-catchビットにはあまり満足していませんが、十分に機能するようです。 スタックトレースを見ると、マップフラグメントがFragmentManagerにあるかどうかを確認できただけで、try-catchブロックが不要で、コードが更新されたことがわかりました。
その他の編集:やっぱりtry-catchが必要だとわかりました。マップフラグメントをチェックするだけでは、結局うまくいかないことがわかりました。こんにちは。
問題はあなたがやろうとしていることが行われるべきではないということです。あなたは他の破片の中に破片を膨らませるべきではありません。 Androidの ドキュメントから :
注:レイアウトに<fragment>が含まれている場合、そのレイアウトをフラグメントに拡大することはできません。入れ子になったフラグメントは、フラグメントに動的に追加された場合にのみサポートされます。
あなたがここに提示されたハックでタスクを達成することができるかもしれない間、私はあなたがそれをしないことを強く勧めます。別のフラグメントを含むフラグメントのレイアウトを拡張しようとしたときに、これらのハックが新しい各Android OSの処理を確実に処理することは不可能です。
フラグメントを別のフラグメントに追加するためのAndroidでサポートされている唯一の方法は、子フラグメントマネージャからのトランザクションを使用することです。
XMLレイアウトを空のコンテナに変更するだけです(必要に応じてIDを追加します)。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/mapFragmentContainer"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical" >
</LinearLayout>
それからフラグメントonViewCreated(View view, @Nullable Bundle savedInstanceState)
メソッドで:
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
FragmentManager fm = getChildFragmentManager();
SupportMapFragment mapFragment = (SupportMapFragment) fm.findFragmentByTag("mapFragment");
if (mapFragment == null) {
mapFragment = new SupportMapFragment();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.mapFragmentContainer, mapFragment, "mapFragment");
ft.commit();
fm.executePendingTransactions();
}
mapFragment.getMapAsync(callback);
}
私は同じ問題を抱えていて、MapFragment
クラスのonDestroy()
メソッドでFragment
を手動で削除することでそれを解決することができました。これが機能し、XMLのIDでMapFragment
を参照するコードです。
@Override
public void onDestroyView() {
super.onDestroyView();
MapFragment f = (MapFragment) getFragmentManager()
.findFragmentById(R.id.map);
if (f != null)
getFragmentManager().beginTransaction().remove(f).commit();
}
手動でMapFragment
を削除しないと、マップビューを再作成または表示するために多くのリソースが費やされないように、ハングアップします。基礎となるMapView
を保持することはタブ間での切り替えには素晴らしいようですが、フラグメントで使用すると、同じIDを持つ新しいMapView
ごとに重複するMapFragment
が作成されます。解決策は、手動でMapFragment
を削除し、フラグメントが膨張するたびに基礎となるマップを再作成することです。
私はまた別の答えでこれに気づいた[ 1 ]。
これは私の答えです:
1、次のようにレイアウトxmlを作成します。
<FrameLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:id="@+id/map_container"
Android:layout_width="match_parent"
Android:layout_height="match_parent">
</FrameLayout>
2、Fragmentクラスで、Google Mapをプログラムで追加します。
import com.google.Android.gms.maps.GoogleMap;
import com.google.Android.gms.maps.SupportMapFragment;
import Android.app.Activity;
import Android.os.Bundle;
import Android.support.v4.app.Fragment;
import Android.support.v4.app.FragmentTransaction;
import Android.util.Log;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;
/**
* A simple {@link Android.support.v4.app.Fragment} subclass. Activities that
* contain this fragment must implement the
* {@link MapFragment.OnFragmentInteractionListener} interface to handle
* interaction events. Use the {@link MapFragment#newInstance} factory method to
* create an instance of this fragment.
*
*/
public class MapFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
private GoogleMap mMap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_map, container, false);
SupportMapFragment mMapFragment = SupportMapFragment.newInstance();
mMap = mMapFragment.getMap();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.map_container, mMapFragment).commit();
return view;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.d("Attach", "on attach");
}
@Override
public void onDetach() {
super.onDetach();
}
}
私の解決策:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_map_list, container, false);
// init
//mapFragment = (SupportMapFragment)getChildFragmentManager().findFragmentById(R.id.map);
// don't recreate fragment everytime ensure last map location/state are maintain
if (mapFragment == null) {
mapFragment = SupportMapFragment.newInstance();
mapFragment.getMapAsync(this);
}
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
// R.id.map is a layout
transaction.replace(R.id.map, mapFragment).commit();
return view;
}
SupportMapFragmentオブジェクトをグローバルに宣言する
private SupportMapFragment mapFragment;
OnCreateView()メソッドではコードの下に置きます
mapFragment = (SupportMapFragment) getChildFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
OnDestroyView()でコードの下に置く
@Override
public void onDestroyView() {
super.onDestroyView();
if (mapFragment != null)
getFragmentManager().beginTransaction().remove(mapFragment).commit();
}
あなたのxmlファイルで、コードの下に置きます
<fragment
Android:id="@+id/map"
Android:name="com.abc.Driver.fragment.FragmentHome"
class="com.google.Android.gms.maps.SupportMapFragment"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
/>
上記のコードは私の問題を解決し、それはうまく機能している
別の解決策:
if (view == null) {
view = inflater.inflate(R.layout.nearbyplaces, container, false);
}
それだけです、nullでない場合、親から削除することは不要です。
あなたのタブ処理ではreplace()
/attach()
ではなくdetach()
をお勧めします。
または、ViewPager
に切り替えます。 これは、10個のマップをホストする、タブ付きのViewPager
を表示するサンプルプロジェクト です。
理由を見つけるために今日数時間を失いました。幸いなことに、この問題はMapFragmentの実装によるものではありません。
私の実装には、2つのタブ(ビューページャーなし)があり、一方にマップがあり、もう一方にエントリのリストがあるアクションバー(タブモード)のアクティビティがあります。もちろん、タブフラグメント内でMapFragmentを使用するのは非常に単純で、マップタブに戻るたびにアプリがクラッシュすることはありませんでした。
(タブフラグメントが他のフラグメントを含むレイアウトを膨張させる場合にも同じ問題が発生します)。
1つのオプションは、MapFragmentの代わりにMapViewを使用することですが、いくらかのオーバーヘッドがあります( MapView Docs をlayout.xmlのドロップイン置換として参照し、別のオプションはrevからsupport-libraryを使用することです。11ただし、ネストされたフラグメントはレイアウトではサポートされていないため、プログラム的なアプローチを取ります。または、Matt/Vidarの回答のように、フラグメントを明示的に破棄することでプログラム的に回避します。btw:MapViewを使用しても同じ効果が得られます(オプション1) 。
しかし、実際には、タブで移動するたびにマップを失いたくはありませんでした。つまり、アクティビティを閉じたときにのみメモリに保存してクリーンアップしたいので、タブ移動中にマップを単に非表示/表示することにしました、参照してください FragmentTransaction/hide
私はすべての答えを尊重します、しかし、私はこの1つの明白な解決策を見つけました:もしnがタブの数ならば:
mViewPager.setOffscreenPageLimit(n);
例:言及された場合:
mViewPager.setOffscreenPageLimit(2);
View pagerはキューを実装しているので、そのフラグメントを削除する必要はありません。 onCreateViewは一度だけ呼び出されます。
まだこの問題に遭遇している人たちのために、タブ内のMapでこのエラーが発生しないようにするための最善の方法は、タブに使用されるフラグメント内にSupportMapFragment
をネストする代わりにSupportMapFragment
を拡張することです。
ViewPager
とFragmentPagerAdapter
を使用し、3番目のタブのSupportMapFragmentを使用してこれを機能させたところです。
これが一般的な構造です。onCreateView()
メソッドをオーバーライドする必要はなく、レイアウトxmlを拡張する必要もありません。
public class MapTabFragment extends SupportMapFragment
implements OnMapReadyCallback {
private GoogleMap mMap;
private Marker marker;
public MapTabFragment() {
}
@Override
public void onResume() {
super.onResume();
setUpMapIfNeeded();
}
private void setUpMapIfNeeded() {
if (mMap == null) {
getMapAsync(this);
}
}
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
setUpMap();
}
private void setUpMap() {
mMap.setMyLocationEnabled(true);
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
mMap.getUiSettings().setMapToolbarEnabled(false);
mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
@Override
public void onMapClick(LatLng point) {
//remove previously placed Marker
if (marker != null) {
marker.remove();
}
//place marker where user just clicked
marker = mMap.addMarker(new MarkerOptions().position(point).title("Marker")
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_Magenta)));
}
});
}
}
結果:
これは、最初の2つのタブに使用されたプレースホルダフラグメントと、3番目のタブに使用されたマップフラグメントを含みます。
public class MainActivity extends AppCompatActivity implements ActionBar.TabListener{
SectionsPagerAdapter mSectionsPagerAdapter;
ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
actionBar.addTab(actionBar.newTab().setText(mSectionsPagerAdapter.getPageTitle(i)).setTabListener(this));
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
mViewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return PlaceholderFragment.newInstance(position + 1);
case 1:
return PlaceholderFragment.newInstance(position + 1);
case 2:
return MapTabFragment.newInstance(position + 1);
}
return null;
}
@Override
public int getCount() {
// Show 3 total pages.
return 3;
}
@Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_section1).toUpperCase(l);
case 1:
return getString(R.string.title_section2).toUpperCase(l);
case 2:
return getString(R.string.title_section3).toUpperCase(l);
}
return null;
}
}
public static class PlaceholderFragment extends Fragment {
private static final String ARG_SECTION_NUMBER = "section_number";
TextView text;
public static PlaceholderFragment newInstance(int sectionNumber) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
public PlaceholderFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
text = (TextView) rootView.findViewById(R.id.section_label);
text.setText("placeholder");
return rootView;
}
}
public static class MapTabFragment extends SupportMapFragment implements
OnMapReadyCallback {
private static final String ARG_SECTION_NUMBER = "section_number";
private GoogleMap mMap;
private Marker marker;
public static MapTabFragment newInstance(int sectionNumber) {
MapTabFragment fragment = new MapTabFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
public MapTabFragment() {
}
@Override
public void onResume() {
super.onResume();
Log.d("MyMap", "onResume");
setUpMapIfNeeded();
}
private void setUpMapIfNeeded() {
if (mMap == null) {
Log.d("MyMap", "setUpMapIfNeeded");
getMapAsync(this);
}
}
@Override
public void onMapReady(GoogleMap googleMap) {
Log.d("MyMap", "onMapReady");
mMap = googleMap;
setUpMap();
}
private void setUpMap() {
mMap.setMyLocationEnabled(true);
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
mMap.getUiSettings().setMapToolbarEnabled(false);
mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
@Override
public void onMapClick(LatLng point) {
Log.d("MyMap", "MapClick");
//remove previously placed Marker
if (marker != null) {
marker.remove();
}
//place marker where user just clicked
marker = mMap.addMarker(new MarkerOptions().position(point).title("Marker")
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_Magenta)));
Log.d("MyMap", "MapClick After Add Marker");
}
});
}
}
}
以前のApp-Compat libにchild Fragmentのバグがいくつかあったと思います。私は@ Vidar Wahlbergと@ Mattの回答を試してみました。 appcompatライブラリを更新した後、私のコードは余分な努力なしで完璧に動作します。
このソリューションでは、静的変数をとる必要はありません。
Button nextBtn;
private SupportMapFragment mMapFragment;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (mRootView != null) {
ViewGroup parent = (ViewGroup) mRootView.getParent();
Utility.log(0,"removeView","mRootView not NULL");
if (parent != null) {
Utility.log(0, "removeView", "view removeViewed");
parent.removeAllViews();
}
}
else {
try {
mRootView = inflater.inflate(R.layout.dummy_fragment_layout_one, container, false);//
} catch (InflateException e) {
/* map is already there, just return view as it is */
e.printStackTrace();
}
}
return mRootView;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
FragmentManager fm = getChildFragmentManager();
SupportMapFragment mapFragment = (SupportMapFragment) fm.findFragmentById(R.id.mapView);
if (mapFragment == null) {
mapFragment = new SupportMapFragment();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.mapView, mapFragment, "mapFragment");
ft.commit();
fm.executePendingTransactions();
}
//mapFragment.getMapAsync(this);
nextBtn = (Button) view.findViewById(R.id.nextBtn);
nextBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Utility.replaceSupportFragment(getActivity(),R.id.dummyFragment,dummyFragment_2.class.getSimpleName(),null,new dummyFragment_2());
}
});
}`
あなたのmapView親レイアウトのためにid(Android:id = "@ + id/maps_dialog")を設定してみてください。私のために働きます。
私はこれをviewPagerに持っていました、そしてクラッシュはどんなフラグメントもそれ自身のタグ、重複したタグあるいは同じフラグメントのためのidを持っていなければならなかったからです。
私はパーティーに少し遅れていますが、これらの答えのどれも私の場合私を助けませんでした。私のフラグメントでは、GoogleマップをSupportMapFragmentおよびPlaceAutocompleteFragmentとして使用していました。すべての回答が問題はSupportMapFragmentが再作成およびredrawn.Butであることであるという事実を指摘したように私の問題が実際にはPlaceAutocompleteFragmentであることがわかった
それで、SupportMapFragmentおよびSupportMapFragmentが原因で、この問題に直面している人のための実用的な解決策があります。
//Global SupportMapFragment mapFragment;
mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.mapFragment);
FragmentManager fm = getChildFragmentManager();
if (mapFragment == null) {
mapFragment = SupportMapFragment.newInstance();
fm.beginTransaction().replace(R.id.mapFragment, mapFragment).commit();
fm.executePendingTransactions();
}
mapFragment.getMapAsync(this);
//Global PlaceAutocompleteFragment autocompleteFragment;
if (autocompleteFragment == null) {
autocompleteFragment = (PlaceAutocompleteFragment) getActivity().getFragmentManager().findFragmentById(R.id.place_autoCompleteFragment);
}
そしてonDestroyViewでSupportMapFragmentとSupportMapFragmentをクリアします
@Override
public void onDestroyView() {
super.onDestroyView();
if (getActivity() != null) {
Log.e("res","place dlted");
Android.app.FragmentManager fragmentManager = getActivity().getFragmentManager();
Android.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(autocompleteFragment);
fragmentTransaction.commit();
//Use commitAllowingStateLoss() if getting exception
autocompleteFragment = null;
}
}
ここで注意すべきことは、あなたのアプリは2つのケースのどちらかでひどくクラッシュするということです - -
1)Mapでフラグメントを再利用するには、Mapを表示しているフラグメントがonDestroyViewコールバックで他のフラグメントに置き換えられたときに、MapViewフラグメントを削除する必要があります。
それ以外の場合は、同じフラグメントを2回展開しようとするとcom.google.Android.gms.maps.MapFragmentエラーのためにID、タグnull、または親IDが別のフラグメントと重複します。
2)次に、app.FragmentオペレーションとAndroid.support.v4.app.Fragment APIオペレーションを混在させてはいけません。例えば、v4.app.FragmentタイプのMapView Fragmentを削除するのにAndroid.app.FragmentTransactionを使用しないでください。これを混ぜると、再びフラグメント側からクラッシュします。
MapViewを正しく使用するためのサンプルコードの一部です。
import Android.content.Context;
import Android.location.Location;
import Android.location.LocationListener;
import Android.location.LocationManager;
import Android.os.Bundle;
import Android.support.v4.app.Fragment;
import Android.util.Log;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;
import Android.widget.Toast;
import com.google.Android.gms.maps.CameraUpdateFactory;
import com.google.Android.gms.maps.GoogleMap;
import com.google.Android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.Android.gms.maps.MapFragment;
import com.google.Android.gms.maps.model.BitmapDescriptorFactory;
import com.google.Android.gms.maps.model.CameraPosition;
import com.google.Android.gms.maps.model.LatLng;
import com.google.Android.gms.maps.model.MarkerOptions;
import com.serveroverload.yago.R;
/**
* @author 663918
*
*/
public class HomeFragment extends Fragment implements LocationListener {
// Class to do operations on the Map
GoogleMap googleMap;
private LocationManager locationManager;
public static Fragment newInstance() {
return new HomeFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.home_fragment, container, false);
Bundle bdl = getArguments();
// setuping locatiomanager to perfrom location related operations
locationManager = (LocationManager) getActivity().getSystemService(
Context.LOCATION_SERVICE);
// Requesting locationmanager for location updates
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1, 1, this);
// To get map from MapFragment from layout
googleMap = ((MapFragment) getActivity().getFragmentManager()
.findFragmentById(R.id.map)).getMap();
// To change the map type to Satellite
// googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
// To show our current location in the map with dot
// googleMap.setMyLocationEnabled(true);
// To listen action whenever we click on the map
googleMap.setOnMapClickListener(new OnMapClickListener() {
@Override
public void onMapClick(LatLng latLng) {
/*
* LatLng:Class will give us selected position lattigude and
* longitude values
*/
Toast.makeText(getActivity(), latLng.toString(),
Toast.LENGTH_LONG).show();
}
});
changeMapMode(2);
// googleMap.setSatellite(true);
googleMap.setTrafficEnabled(true);
googleMap.setBuildingsEnabled(true);
googleMap.setMyLocationEnabled(true);
return v;
}
private void doZoom() {
if (googleMap != null) {
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(18.520430, 73.856744), 17));
}
}
private void changeMapMode(int mapMode) {
if (googleMap != null) {
switch (mapMode) {
case 0:
googleMap.setMapType(GoogleMap.MAP_TYPE_NONE);
break;
case 1:
googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
break;
case 2:
googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
break;
case 3:
googleMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
break;
case 4:
googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
break;
default:
break;
}
}
}
private void createMarker(double latitude, double longitude) {
// double latitude = 17.385044;
// double longitude = 78.486671;
// lets place some 10 random markers
for (int i = 0; i < 10; i++) {
// random latitude and logitude
double[] randomLocation = createRandLocation(latitude, longitude);
// Adding a marker
MarkerOptions marker = new MarkerOptions().position(
new LatLng(randomLocation[0], randomLocation[1])).title(
"Hello Maps " + i);
Log.e("Random", "> " + randomLocation[0] + ", " + randomLocation[1]);
// changing marker color
if (i == 0)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_Azure));
if (i == 1)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_BLUE));
if (i == 2)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_CYAN));
if (i == 3)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
if (i == 4)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_Magenta));
if (i == 5)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE));
if (i == 6)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_RED));
if (i == 7)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_ROSE));
if (i == 8)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_Violet));
if (i == 9)
marker.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_YELLOW));
googleMap.addMarker(marker);
// Move the camera to last position with a zoom level
if (i == 9) {
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(randomLocation[0], randomLocation[1]))
.zoom(15).build();
googleMap.animateCamera(CameraUpdateFactory
.newCameraPosition(cameraPosition));
}
}
}
/*
* creating random postion around a location for testing purpose only
*/
private double[] createRandLocation(double latitude, double longitude) {
return new double[] { latitude + ((Math.random() - 0.5) / 500),
longitude + ((Math.random() - 0.5) / 500),
150 + ((Math.random() - 0.5) * 10) };
}
@Override
public void onLocationChanged(Location location) {
if (null != googleMap) {
// To get lattitude value from location object
double latti = location.getLatitude();
// To get longitude value from location object
double longi = location.getLongitude();
// To hold lattitude and longitude values
LatLng position = new LatLng(latti, longi);
createMarker(latti, longi);
// Creating object to pass our current location to the map
MarkerOptions markerOptions = new MarkerOptions();
// To store current location in the markeroptions object
markerOptions.position(position);
// Zooming to our current location with zoom level 17.0f
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(position,
17f));
// adding markeroptions class object to the map to show our current
// location in the map with help of default marker
googleMap.addMarker(markerOptions);
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
@Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onProviderDisabled(String provider) {
// TODO Auto-generated method stub
}
@Override
public void onDestroyView() {
// TODO Auto-generated method stub
super.onDestroyView();
locationManager.removeUpdates(this);
Android.app.Fragment fragment = getActivity().getFragmentManager()
.findFragmentById(R.id.map);
if (null != fragment) {
Android.app.FragmentTransaction ft = getActivity()
.getFragmentManager().beginTransaction();
ft.remove(fragment);
ft.commit();
}
}
}
XML
<fragment
Android:id="@+id/map"
Android:name="com.google.Android.gms.maps.MapFragment"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
/>
誰かに役立つことを願っています。
入れ子になったフラグメントは現在サポートされていません。 サポートパッケージ、リビジョン11 を試してください。
レイアウトファイルでカスタムのMapFragment
クラスを参照しようとしましたか?
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical" >
<fragment
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/mapFragment"
Android:name="com.nfc.demo.MapFragment"
Android:layout_width="match_parent"
Android:layout_height="match_parent" />
</LinearLayout>
Vidar Wahlbergの回答だけを使用する場合は、他のアクティビティを開いて(たとえば)マップに戻るとエラーになります。または私の場合は他のアクティビティを開き、次に新しいアクティビティからマップをもう一度開きます(戻るボタンを使わずに)。しかし、Vidar WahlbergのソリューションとMattのソリューションを組み合わせると、例外がなくなります。
レイアウト
<com.example.ui.layout.MapWrapperLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="fill_parent"
Android:layout_height="fill_parent"
Android:id="@+id/map_relative_layout">
<RelativeLayout
Android:layout_width="fill_parent"
Android:layout_height="fill_parent"
Android:id="@+id/root">
<fragment xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/map"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
class="com.google.Android.gms.maps.SupportMapFragment" />
</RelativeLayout>
</<com.example.ui.layout.MapWrapperLayout>
断片
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setHasOptionsMenu(true);
if (view != null) {
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null){
parent.removeView(view);
}
}
try {
view = inflater.inflate(R.layout.map_view, null);
if(view!=null){
ViewGroup root = (ViewGroup) view.findViewById(R.id.root);
...
@Override
public void onDestroyView() {
super.onDestroyView();
Fragment fragment = this.getSherlockActivity().getSupportFragmentManager().findFragmentById(R.id.map);
if (fragment != null)
getFragmentManager().beginTransaction().remove(fragment).commit();
}