私が開発しているアプリケーションでは、フラグメントを含むViewPagerを使用しており、各フラグメントは、ViewPager内の他のすべてのフラグメントとは独立して独自のメニューを作成します。
問題は、デフォルトでViewPagerによって初期化された(つまり、初期状態にある)フラグメントのアイテムがアクションアイテムメニューに入力されていない場合があることです。さらに悪いことに、この問題は断続的にしか発生しません。 ViewPagerを十分にスワイプして、フラグメントが自動的に再初期化されるようにした場合、スワイプして戻ると、メニューが正しく表示されます。
アクティビティコード:
package net.solarnz.apps.fragmentsample;
import Android.app.Activity;
import Android.app.Fragment;
import Android.app.FragmentManager;
import Android.os.Bundle;
import Android.support.v13.app.FragmentStatePagerAdapter;
import Android.support.v4.view.ViewPager;
public class FragmentSampleActivity extends Activity {
private ViewPagerAdapter mViewPagerAdapter;
private ViewPager mViewPager;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (mViewPagerAdapter == null) {
mViewPagerAdapter = new ViewPagerAdapter(getFragmentManager());
}
mViewPager = (ViewPager) findViewById(R.id.log_pager);
mViewPager.setAdapter(mViewPagerAdapter);
mViewPager.setCurrentItem(0);
}
private class ViewPagerAdapter extends FragmentStatePagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return 8;
}
@Override
public Fragment getItem(int position) {
Fragment f = Fragment1.newInstance(position);
// f.setRetainInstance(true);
f.setHasOptionsMenu(true);
return f;
}
}
}
フラグメントコード:
package net.solarnz.apps.fragmentsample;
import Android.app.Fragment;
import Android.os.Bundle;
import Android.view.Menu;
import Android.view.MenuInflater;
public class Fragment1 extends Fragment {
int mNum;
static Fragment newInstance(int num) {
Fragment1 f = new Fragment1();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mNum = getArguments() != null ? getArguments().getInt("num") : 0;
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_list, menu);
}
}
レイアウト:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="fill_parent"
Android:layout_height="fill_parent" >
<Android.support.v4.view.ViewPager
Android:id="@+id/log_pager"
Android:layout_width="match_parent"
Android:layout_height="match_parent" />
</LinearLayout>
メニュー:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:Android="http://schemas.Android.com/apk/res/Android">
<item Android:id="@+id/menu_refresh"
Android:title="Refresh"
Android:icon="@Android:drawable/ic_delete"
Android:showAsAction="ifRoom|withText" />
</menu>
入力されているアクションメニュー: http://i.stack.imgur.com/QFMDd.png
アクションメニューが入力されていません: http://i.stack.imgur.com/sH5Pp.png
これを読む必要があります(xcolw ...)
実験によると、根本的な原因は、キューに入れられたジョブを処理するためにメインスレッドで中断することなく複数のinvalidateOptionsMenuが呼び出されることであるようです。推測-これは、メニュー作成の重要な部分が投稿を介して延期され、実行されるまでアクションバーが悪い状態のままである場合に問題になります。
明らかではない、これが発生する可能性のあるいくつかのスポットがあります。
同じアイテムに対してviewPager.setCurrentItemを複数回呼び出す
アクティビティのonCreateでviewPager.setCurrentItemを呼び出します。 setCurrentItemにより、オプションメニューが無効になり、その直後にアクティビティのオプションメニューが無効になります。
それぞれについて私が見つけた回避策
ViewPager.setCurrentItemへの呼び出しを保護します
if (viewPager.getCurrentItem() != position)
viewPager.setCurrentItem(position);
OnCreateでviewPager.setCurrentItemの呼び出しを延期します
public void onCreate(...) {
...
view.post(new Runnable() {
public void run() {
// guarded viewPager.setCurrentItem
}
}
}
これらの変更後、ビューページャー内のオプションメニューは期待どおりに機能するようです。誰かがこれにもっと光を当てることができることを願っています。
簡単な答えは、ViewPagerのフラグメント内でメニューを使用しないことです。フラグメント内でメニューを使用する必要がある場合は、親アクティビティのonCreateOptionsMenuメソッドを使用してメニューをロードすることをお勧めします。明らかに、表示するメニューを決定できる必要があります。
クラスリフレクションを使用することでこれを達成することができました。また、ページを切り替えるたびに、invalidateOptionsMenuメソッドを使用する必要があります。 ViewPagerがページを変更するときにこれを呼び出すには、OnPageChangeListenerが必要です。
私も同じ問題を抱えていました。私の場合、2つのフラグメントを含むviewpagerを使用した1つのアクティビティがあり、すべてのフラグメントが独自のアクションメニューを膨らませますが、フラグメントアクションメニューは表示されません。
ポケットベルアダプタコードを表示する
public class ScreensAdapter extends FragmentPagerAdapter {
public TrackerScreensAdapter(Context context, FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return 2;
}
public Fragment getItem(int position) {
Fragment fragment = null;
switch (position){
case 0:
fragment = new Fragment1();
break;
case 1:
fragment = new Fragment2();
break;
}
return fragment;
}
}
作成時のアクティビティ
screensAdapter = new ScreensAdapter(this, getFragmentManager());
viewPager.setAdapter(screensAdapter);
このように、私のviewPagerには2つのフラグメントがあり、すべてのフラグメントはonActivityCreatedで独自のタスクを実行し、データを取得し、取得したデータに基づいてレイアウトを描画します。また、すべてのフラグメントにはonCreateOptionsMenuがあります
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
MyTask task = new MyTask();
task.setTaskListener(this);
task.execute();
}
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_menu, menu);
}
この問題を解決し、フラグメントメニューが表示されない理由を理解するために何度も費やしました。私が必要としていたのは
screenAdapter = new ScreenAdapter(this, getFragmentManager());
viewPager.post(new Runnable() {
public void run() {
viewPager.setAdapter(screenAdapter);
}
});
まず、FragmentPagerAdapterのサブクラスにメソッドを作成して、現在のフラグメントを取得します
public SherlockFragment getFragment() {
return currentFragment;
}
@Override
public void onTabSelected(final Tab tab, FragmentTransaction ft) {
((SherlockFragmentActivity) mContext).invalidateOptionsMenu();
Fragment f = ((SherlockFragmentActivity) mContext)
.getSupportFragmentManager().findFragmentByTag(
makeFragmentName(tab.getPosition()));
currentFragment=(SherlockFragment) f;
}
MainActivityの以下のメソッドをオーバーライドするようになりました
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (mTabsAdapter.getPositionOfTabSelected() != 0) {
menu.add("Read").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add("Write").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add("Clear").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add("Factory data reset").setShowAsAction(
MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
return super.onCreateOptionsMenu(menu);
}
次に、onOptionItemSelectedをアクティビティからフラグメントに呼び出します
@Override
public boolean onOptionsItemSelected(MenuItem item) {
mTabsAdapter.getFragment().onOptionsItemSelected(item);
return super.onOptionsItemSelected(item);
}
HorizontalScrollViewを使用してタブを表示しているときに、アクションバーアイテムでこの問題が発生していましたが、PagerTitleStripに変更すると、問題は解決しました。
おそらく、この情報は他の誰かを助けることができます。
私の場合、問題の根本原因を、FragmentStatePagerAdapterのバグが Fragment#setMenuVisibility をfalseに呼び出し、状態を復元するときに正しくtrueに戻せなかったことが原因であると考えています。
回避策:
FragmentStatePagerAdapterの代わりにFragmentPagerAdapterを使用する
FragmentStatePagerAdapterを使用する必要がある場合は、アダプターサブクラスで次のようにsetPrimaryItemをオーバーライドします。
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
//This is a workaround for a bug in FragmentStatePagerAdapter
Fragment currentItem = getItem(position);
if (currentItem != null) {
currentItem.setMenuVisibility(true);
currentItem.setUserVisibleHint(true);
}
}
ViewPager内のフラグメントによって割り当てられたアクションバーアイコンがonPause()で消えるという非常によく似た問題を解決しました。フラグメントが表示に戻り、ユーザーが左または右にスワイプすると、それらは再び表示されますが、すぐには表示されません。解決策は、フラグメントのonResume()メソッドのPagerAdapterでnotifyDataSetChanged()を呼び出すことでした。
@Override
public void onResume() {
mPagerAdapter.notifyDataSetChanged();
}