私はViewPager
を使用してFragmentPagerAdapter
でFragmentを使用しようとしています。私が達成しようとしているのは、ViewPager
の最初のページにあるフラグメントを別のものと置き換えることです。
ページャは2ページで構成されています。最初のものはFirstPagerFragment
、2番目のものはSecondPagerFragment
です。最初のページのボタンをクリックしてください。 FirstPagerFragment
をNextFragmentに置き換えます。
以下に私のコードがあります。
public class FragmentPagerActivity extends FragmentActivity {
static final int NUM_ITEMS = 2;
MyAdapter mAdapter;
ViewPager mPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_pager);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
}
/**
* Pager Adapter
*/
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public Fragment getItem(int position) {
if(position == 0) {
return FirstPageFragment.newInstance();
} else {
return SecondPageFragment.newInstance();
}
}
}
/**
* Second Page FRAGMENT
*/
public static class SecondPageFragment extends Fragment {
public static SecondPageFragment newInstance() {
SecondPageFragment f = new SecondPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.second, container, false);
}
}
/**
* FIRST PAGE FRAGMENT
*/
public static class FirstPageFragment extends Fragment {
Button button;
public static FirstPageFragment newInstance() {
FirstPageFragment f = new FirstPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
View root = inflater.inflate(R.layout.first, container, false);
button = (Button) root.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
trans.addToBackStack(null);
trans.commit();
}
});
return root;
}
/**
* Next Page FRAGMENT in the First Page
*/
public static class NextFragment extends Fragment {
public static NextFragment newInstance() {
NextFragment f = new NextFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.next, container, false);
}
}
}
...そしてここでxmlファイル
fragment_pager.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:orientation="vertical" Android:padding="4dip"
Android:gravity="center_horizontal"
Android:layout_width="match_parent" Android:layout_height="match_parent">
<Android.support.v4.view.ViewPager
Android:id="@+id/pager"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:layout_weight="1">
</Android.support.v4.view.ViewPager>
</LinearLayout>
first.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/first_fragment_root_id"
Android:orientation="vertical"
Android:layout_width="match_parent"
Android:layout_height="match_parent">
<Button Android:id="@+id/button"
Android:layout_width="wrap_content" Android:layout_height="wrap_content"
Android:text="to next"/>
</LinearLayout>
今度は問題...どのIDを使うべきか
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
?
R.id.first_fragment_root_id
を使用した場合、置き換えは機能しますが、Hierarchy Viewerは以下のように奇妙な動作を示します。
はじめの状況は
交換後の状況は
おかしなことにわかるように、フラグメントを置き換えた後で、最初の図と同じ状態が表示されることを期待しています。
ViewPager
およびFragmentStatePagerAdapter
のソースコードを修正する必要のない別の解決策があり、それは作者によって使用されるFragmentPagerAdapter
基本クラスと共に機能します。
どのIDを使うべきかという著者の質問に答えることから始めたいと思います。これはコンテナのID、つまりビューページャ自体のIDです。ただし、お気づきかもしれませんが、コードでそのIDを使用しても何も起こりません。私はその理由を説明します。
まず最初に、ViewPager
がページを再作成するようにするには、アダプタの基本クラスにあるnotifyDataSetChanged()
を呼び出す必要があります。
次に、ViewPager
はgetItemPosition()
抽象メソッドを使用して、どのページを破棄し、どれを保持するかを確認します。この関数のデフォルト実装は常にPOSITION_UNCHANGED
を返します。これにより、ViewPager
は現在のページをすべて保持します。その結果、新しいページは添付されません。したがって、フラグメントの置き換えを機能させるには、アダプタ内でgetItemPosition()
をオーバーライドする必要があり、古いフラグメントを隠し引数として呼び出したときにPOSITION_NONE
を返す必要があります。
これはまた、アダプタは常に、どのフラグメントを位置0、FirstPageFragment
、またはNextFragment
に表示する必要があるかを認識する必要があることを意味します。これを行う1つの方法は、FirstPageFragment
を作成するときにリスナーを提供することです。これは、フラグメントを切り替えるときに呼び出されます。フラグメントアダプタがすべてのフラグメントスイッチを処理してViewPager
とFragmentManager
を呼び出すようにするには、これは良いことだと思います。
3番目に、FragmentPagerAdapter
は、使用されたフラグメントをその位置から派生した名前でキャッシュします。そのため、位置0にフラグメントがあった場合は、クラスが新しい場合でも置換されません。 2つの解決策がありますが、最も簡単な方法はFragmentTransaction
のremove()
関数を使用することです。これにより、そのタグも削除されます。
それはたくさんのテキストでした、ここにあなたのケースでうまくいくはずのコードがあります:
public class MyAdapter extends FragmentPagerAdapter
{
static final int NUM_ITEMS = 2;
private final FragmentManager mFragmentManager;
private Fragment mFragmentAtPos0;
public MyAdapter(FragmentManager fm)
{
super(fm);
mFragmentManager = fm;
}
@Override
public Fragment getItem(int position)
{
if (position == 0)
{
if (mFragmentAtPos0 == null)
{
mFragmentAtPos0 = FirstPageFragment.newInstance(new FirstPageFragmentListener()
{
public void onSwitchToNextFragment()
{
mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();
mFragmentAtPos0 = NextFragment.newInstance();
notifyDataSetChanged();
}
});
}
return mFragmentAtPos0;
}
else
return SecondPageFragment.newInstance();
}
@Override
public int getCount()
{
return NUM_ITEMS;
}
@Override
public int getItemPosition(Object object)
{
if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
return POSITION_NONE;
return POSITION_UNCHANGED;
}
}
public interface FirstPageFragmentListener
{
void onSwitchToNextFragment();
}
これが誰にでも役立つことを願っています!
2012年11月13日の時点で、ViewPagerでフラグメントを修復することはずっと簡単になったようです。グーグルは入れ子になったフラグメントをサポートするアンドロイド4.2をリリースしました、そしてそれはまた新しい---でサポートされています アンドロイドサポートライブラリv11 だからこれは1.6に戻ってずっとうまくいくでしょう
GetChildFragmentManagerを使用する以外は、フラグメントを置き換える通常の方法と非常によく似ています。ユーザーが戻るボタンをクリックしたときの 入れ子になったフラグメントのバックスタックはポップされません 以外はうまくいくようです。そのリンクされた質問の解決策に従って、あなたは手動でフラグメントの子マネージャ上でpopBackStackImmediate()を呼び出す必要があります。そのため、ViewPagerの現在のフラグメントを取得し、それに対してgetChildFragmentManager()。popBackStackImmediate()を呼び出すViewPagerアクティビティのonBackPressed()をオーバーライドする必要があります。
現在表示されているフラグメントを取得するのも少し厄介です。 汚れた "Android:switcher:VIEWPAGER_ID:INDEX"ソリューション しかし、ViewPagerのすべてのフラグメントを自分で追跡することもできます。 2番目の解決策 このページの 。
ユーザーが行をクリックしたときにViewPagerに詳細ビューが表示され、戻るボタンが機能している状態で、4つのListViewを持つViewPager用のコードがあります。説明を簡潔にするために関連コードだけを含めようとしました。GitHubに完全なアプリをアップロードする場合は、コメントを残してください。
HomeActivity.Java
public class HomeActivity extends SherlockFragmentActivity {
FragmentAdapter mAdapter;
ViewPager mPager;
TabPageIndicator mIndicator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mAdapter = new FragmentAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (TabPageIndicator)findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
}
// This the important bit to make sure the back button works when you're nesting fragments. Very hacky, all it takes is some Google engineer to change that ViewPager view tag to break this in a future Android update.
@Override
public void onBackPressed() {
Fragment fragment = (Fragment) getSupportFragmentManager().findFragmentByTag("Android:switcher:" + R.id.pager + ":"+mPager.getCurrentItem());
if (fragment != null) // could be null if not instantiated yet
{
if (fragment.getView() != null) {
// Pop the backstack on the ChildManager if there is any. If not, close this activity as normal.
if (!fragment.getChildFragmentManager().popBackStackImmediate()) {
finish();
}
}
}
}
class FragmentAdapter extends FragmentPagerAdapter {
public FragmentAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return ListProductsFragment.newInstance();
case 1:
return ListActiveSubstancesFragment.newInstance();
case 2:
return ListProductFunctionsFragment.newInstance();
case 3:
return ListCropsFragment.newInstance();
default:
return null;
}
}
@Override
public int getCount() {
return 4;
}
}
}
リスト製品フラグメント.Java
public class ListProductsFragment extends SherlockFragment {
private ListView list;
public static ListProductsFragment newInstance() {
ListProductsFragment f = new ListProductsFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View V = inflater.inflate(R.layout.list, container, false);
list = (ListView)V.findViewById(Android.R.id.list);
list.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// This is important bit
Fragment productDetailFragment = FragmentProductDetail.newInstance();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.addToBackStack(null);
transaction.replace(R.id.products_list_linear, productDetailFragment).commit();
}
});
return V;
}
}
@wizeの答えに基づいていて、それは私が部分的に望んでいたことを達成することができたので、私は部分的に欲しいものを達成することができました。私はそれを少し彼のコードを少し修正することで達成しました。
これはFragmentPagerAdapterになります。
public static class MyAdapter extends FragmentPagerAdapter {
private final class CalendarPageListener implements
CalendarPageFragmentListener {
public void onSwitchToNextFragment() {
mFragmentManager.beginTransaction().remove(mFragmentAtPos0)
.commit();
if (mFragmentAtPos0 instanceof FirstFragment){
mFragmentAtPos0 = NextFragment.newInstance(listener);
}else{ // Instance of NextFragment
mFragmentAtPos0 = FirstFragment.newInstance(listener);
}
notifyDataSetChanged();
}
}
CalendarPageListener listener = new CalendarPageListener();;
private Fragment mFragmentAtPos0;
private FragmentManager mFragmentManager;
public MyAdapter(FragmentManager fm) {
super(fm);
mFragmentManager = fm;
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public int getItemPosition(Object object) {
if (object instanceof FirstFragment && mFragmentAtPos0 instanceof NextFragment)
return POSITION_NONE;
if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstFragment)
return POSITION_NONE;
return POSITION_UNCHANGED;
}
@Override
public Fragment getItem(int position) {
if (position == 0)
return Portada.newInstance();
if (position == 1) { // Position where you want to replace fragments
if (mFragmentAtPos0 == null) {
mFragmentAtPos0 = FirstFragment.newInstance(listener);
}
return mFragmentAtPos0;
}
if (position == 2)
return Clasificacion.newInstance();
if (position == 3)
return Informacion.newInstance();
return null;
}
}
public interface CalendarPageFragmentListener {
void onSwitchToNextFragment();
}
置き換えを実行するには、単にCalendarPageFragmentListener
型の静的フィールドを定義し、対応するフラグメントのnewInstance
メソッドで初期化して、FirstFragment.pageListener.onSwitchToNextFragment()
またはNextFragment.pageListener.onSwitchToNextFragment()
をそれぞれ呼び出します。
私は解決策を実装しました:
これを実現するための秘訣は次のとおりです。
アダプタコードは次のとおりです。
public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
/** The sherlock fragment activity. */
private final SherlockFragmentActivity mActivity;
/** The action bar. */
private final ActionBar mActionBar;
/** The pager. */
private final ViewPager mPager;
/** The tabs. */
private List<TabInfo> mTabs = new LinkedList<TabInfo>();
/** The total number of tabs. */
private int TOTAL_TABS;
private Map<Integer, Stack<TabInfo>> history = new HashMap<Integer, Stack<TabInfo>>();
/**
* Creates a new instance.
*
* @param activity the activity
* @param pager the pager
*/
public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) {
super(activity.getSupportFragmentManager());
activity.getSupportFragmentManager();
this.mActivity = activity;
this.mActionBar = activity.getSupportActionBar();
this.mPager = pager;
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
}
/**
* Adds the tab.
*
* @param image the image
* @param fragmentClass the class
* @param args the arguments
*/
public void addTab(final Drawable image, final Class fragmentClass, final Bundle args) {
final TabInfo tabInfo = new TabInfo(fragmentClass, args);
final ActionBar.Tab tab = mActionBar.newTab();
tab.setTabListener(this);
tab.setTag(tabInfo);
tab.setIcon(image);
mTabs.add(tabInfo);
mActionBar.addTab(tab);
notifyDataSetChanged();
}
@Override
public Fragment getItem(final int position) {
final TabInfo tabInfo = mTabs.get(position);
return Fragment.instantiate(mActivity, tabInfo.fragmentClass.getName(), tabInfo.args);
}
@Override
public int getItemPosition(final Object object) {
/* Get the current position. */
int position = mActionBar.getSelectedTab().getPosition();
/* The default value. */
int pos = POSITION_NONE;
if (history.get(position).isEmpty()) {
return POSITION_NONE;
}
/* Checks if the object exists in current history. */
for (Stack<TabInfo> stack : history.values()) {
TabInfo c = stack.peek();
if (c.fragmentClass.getName().equals(object.getClass().getName())) {
pos = POSITION_UNCHANGED;
break;
}
}
return pos;
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageSelected(int position) {
mActionBar.setSelectedNavigationItem(position);
}
@Override
public void onTabSelected(final ActionBar.Tab tab, final FragmentTransaction ft) {
TabInfo tabInfo = (TabInfo) tab.getTag();
for (int i = 0; i < mTabs.size(); i++) {
if (mTabs.get(i).equals(tabInfo)) {
mPager.setCurrentItem(i);
}
}
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
public void replace(final int position, final Class fragmentClass, final Bundle args) {
/* Save the fragment to the history. */
mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();
/* Update the tabs. */
updateTabs(new TabInfo(fragmentClass, args), position);
/* Updates the history. */
history.get(position).Push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));
notifyDataSetChanged();
}
/**
* Updates the tabs.
*
* @param tabInfo
* the new tab info
* @param position
* the position
*/
private void updateTabs(final TabInfo tabInfo, final int position) {
mTabs.remove(position);
mTabs.add(position, tabInfo);
mActionBar.getTabAt(position).setTag(tabInfo);
}
/**
* Creates the history using the current state.
*/
public void createHistory() {
int position = 0;
TOTAL_TABS = mTabs.size();
for (TabInfo mTab : mTabs) {
if (history.get(position) == null) {
history.put(position, new Stack<TabInfo>());
}
history.get(position).Push(new TabInfo(mTab.fragmentClass, mTab.args));
position++;
}
}
/**
* Called on back
*/
public void back() {
int position = mActionBar.getSelectedTab().getPosition();
if (!historyIsEmpty(position)) {
/* In case there is not any other item in the history, then finalize the activity. */
if (isLastItemInHistory(position)) {
mActivity.finish();
}
final TabInfo currentTabInfo = getPrevious(position);
mTabs.clear();
for (int i = 0; i < TOTAL_TABS; i++) {
if (i == position) {
mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
} else {
TabInfo otherTabInfo = history.get(i).peek();
mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
}
}
}
mActionBar.selectTab(mActionBar.getTabAt(position));
notifyDataSetChanged();
}
/**
* Returns if the history is empty.
*
* @param position
* the position
* @return the flag if empty
*/
private boolean historyIsEmpty(final int position) {
return history == null || history.isEmpty() || history.get(position).isEmpty();
}
private boolean isLastItemInHistory(final int position) {
return history.get(position).size() == 1;
}
/**
* Returns the previous state by the position provided.
*
* @param position
* the position
* @return the tab info
*/
private TabInfo getPrevious(final int position) {
TabInfo currentTabInfo = history.get(position).pop();
if (!history.get(position).isEmpty()) {
currentTabInfo = history.get(position).peek();
}
return currentTabInfo;
}
/** The tab info class */
private static class TabInfo {
/** The fragment class. */
public Class fragmentClass;
/** The args.*/
public Bundle args;
/**
* Creates a new instance.
*
* @param fragmentClass
* the fragment class
* @param args
* the args
*/
public TabInfo(Class fragmentClass, Bundle args) {
this.fragmentClass = fragmentClass;
this.args = args;
}
@Override
public boolean equals(final Object o) {
return this.fragmentClass.getName().equals(o.getClass().getName());
}
@Override
public int hashCode() {
return fragmentClass.getName() != null ? fragmentClass.getName().hashCode() : 0;
}
@Override
public String toString() {
return "TabInfo{" +
"fragmentClass=" + fragmentClass +
'}';
}
}
最初にすべてのタブを追加するときは、初期履歴を作成するためにcreateHistory()メソッドを呼び出す必要があります。
public void createHistory() {
int position = 0;
TOTAL_TABS = mTabs.size();
for (TabInfo mTab : mTabs) {
if (history.get(position) == null) {
history.put(position, new Stack<TabInfo>());
}
history.get(position).Push(new TabInfo(mTab.fragmentClass, mTab.args));
position++;
}
}
フラグメントを特定のタブに置き換えるたびに、次のように呼び出します。replace(最後のint型位置、最後のClass fragmentClass、最後のBundle引数)
/* Save the fragment to the history. */
mActivity.getSupportFragmentManager().beginTransaction().addToBackStack(null).commit();
/* Update the tabs. */
updateTabs(new TabInfo(fragmentClass, args), position);
/* Updates the history. */
history.get(position).Push(new TabInfo(mTabs.get(position).fragmentClass, mTabs.get(position).args));
notifyDataSetChanged();
押された状態でback()メソッドを呼び出す必要があります。
public void back() {
int position = mActionBar.getSelectedTab().getPosition();
if (!historyIsEmpty(position)) {
/* In case there is not any other item in the history, then finalize the activity. */
if (isLastItemInHistory(position)) {
mActivity.finish();
}
final TabInfo currentTabInfo = getPrevious(position);
mTabs.clear();
for (int i = 0; i < TOTAL_TABS; i++) {
if (i == position) {
mTabs.add(new TabInfo(currentTabInfo.fragmentClass, currentTabInfo.args));
} else {
TabInfo otherTabInfo = history.get(i).peek();
mTabs.add(new TabInfo(otherTabInfo.fragmentClass, otherTabInfo.args));
}
}
}
mActionBar.selectTab(mActionBar.getTabAt(position));
notifyDataSetChanged();
}
解決策はシャーロックのアクションバーとスワイプジェスチャーで動作します。
tl; dr:ホストされているコンテンツの置換を担当し、(ブラウザのように)後方ナビゲーション履歴を追跡するHostフラグメントを使用します。
あなたのユースケースは固定量のタブで構成されているので、私の解決策はうまくいきます:ViewPagerをカスタムクラスHostFragment
のインスタンスで満たすことです。ホストフラグメントを置き換えるには、メソッドhostfragment.replaceFragment()
を呼び出します。
public void replaceFragment(Fragment fragment, boolean addToBackstack) {
if (addToBackstack) {
getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).addToBackStack(null).commit();
} else {
getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).commit();
}
}
そのメソッドがすることはすべて、フレームレイアウトをid R.id.hosted_fragment
でメソッドに提供されたフラグメントで置き換えることです。
GitHubの詳細と完全な動作例については、私の このトピックに関するチュートリアル を確認してください。
提示された解決策のいくつかは部分的に問題を解決するのに私を大いに助けましたが、場合によっては断片的なコンテンツの代わりに予期しない例外とブラックページコンテンツを生み出した解決策に欠けている1つの重要なことがまだあります。
つまり、FragmentPagerAdapterクラスはキャッシュされたフラグメントを格納するためにアイテムIDを使用していますFragmentManager。このため、eを返すようにgetItemId(int position)メソッドもオーバーライドする必要があります。 g。最上位ページの場合はposition、詳細ページの場合は100 + positionです。そうでなければ、以前に作成されたトップレベルのフラグメントが、詳細レベルのフラグメントの代わりにキャッシュから返されます。
さらに、ViewPagerを使用してFragmentページを使用し、RadioGroupを使用してタブボタンを使用してタブのようなアクティビティを実装する方法の例を紹介します。最上位ページの詳細ページへの置き換えおよび戻るボタンもサポートします。この実装は1レベルのバックスタッキング(アイテムリスト - アイテム詳細)のみをサポートしますが、マルチレベルバックスタッキングの実装は簡単です。この例は、eに切り替えたときにNullPointerExceptionをスローすることを除けば、通常の場合は非常にうまく機能します。 g。 2ページ目、最初のページのフラグメントを変更し(表示されていない間)、最初のページに戻ります。解決したら、この問題の解決策を投稿します。
public class TabsActivity extends FragmentActivity {
public static final int PAGE_COUNT = 3;
public static final int FIRST_PAGE = 0;
public static final int SECOND_PAGE = 1;
public static final int THIRD_PAGE = 2;
/**
* Opens a new inferior page at specified tab position and adds the current page into back
* stack.
*/
public void startPage(int position, Fragment content) {
// Replace page adapter fragment at position.
mPagerAdapter.start(position, content);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialize basic layout.
this.setContentView(R.layout.tabs_activity);
// Add tab fragments to view pager.
{
// Create fragments adapter.
mPagerAdapter = new PagerAdapter(pager);
ViewPager pager = (ViewPager) super.findViewById(R.id.tabs_view_pager);
pager.setAdapter(mPagerAdapter);
// Update active tab in tab bar when page changes.
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int index, float value, int nextIndex) {
// Not used.
}
@Override
public void onPageSelected(int index) {
RadioGroup tabs_radio_group = (RadioGroup) TabsActivity.this.findViewById(
R.id.tabs_radio_group);
switch (index) {
case 0: {
tabs_radio_group.check(R.id.first_radio_button);
}
break;
case 1: {
tabs_radio_group.check(R.id.second_radio_button);
}
break;
case 2: {
tabs_radio_group.check(R.id.third_radio_button);
}
break;
}
}
@Override
public void onPageScrollStateChanged(int index) {
// Not used.
}
});
}
// Set "tabs" radio group on checked change listener that changes the displayed page.
RadioGroup radio_group = (RadioGroup) this.findViewById(R.id.tabs_radio_group);
radio_group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int id) {
// Get view pager representing tabs.
ViewPager view_pager = (ViewPager) TabsActivity.this.findViewById(R.id.tabs_view_pager);
if (view_pager == null) {
return;
}
// Change the active page.
switch (id) {
case R.id.first_radio_button: {
view_pager.setCurrentItem(FIRST_PAGE);
}
break;
case R.id.second_radio_button: {
view_pager.setCurrentItem(SECOND_PAGE);
}
break;
case R.id.third_radio_button: {
view_pager.setCurrentItem(THIRD_PAGE);
}
break;
}
});
}
}
@Override
public void onBackPressed() {
if (!mPagerAdapter.back()) {
super.onBackPressed();
}
}
/**
* Serves the fragments when paging.
*/
private class PagerAdapter extends FragmentPagerAdapter {
public PagerAdapter(ViewPager container) {
super(TabsActivity.this.getSupportFragmentManager());
mContainer = container;
mFragmentManager = TabsActivity.this.getSupportFragmentManager();
// Prepare "empty" list of fragments.
mFragments = new ArrayList<Fragment>(){};
mBackFragments = new ArrayList<Fragment>(){};
for (int i = 0; i < PAGE_COUNT; i++) {
mFragments.add(null);
mBackFragments.add(null);
}
}
/**
* Replaces the view pager fragment at specified position.
*/
public void replace(int position, Fragment fragment) {
// Get currently active fragment.
Fragment old_fragment = mFragments.get(position);
if (old_fragment == null) {
return;
}
// Replace the fragment using transaction and in underlaying array list.
// NOTE .addToBackStack(null) doesn't work
this.startUpdate(mContainer);
mFragmentManager.beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.remove(old_fragment).add(mContainer.getId(), fragment)
.commit();
mFragments.set(position, fragment);
this.notifyDataSetChanged();
this.finishUpdate(mContainer);
}
/**
* Replaces the fragment at specified position and stores the current fragment to back stack
* so it can be restored by #back().
*/
public void start(int position, Fragment fragment) {
// Remember current fragment.
mBackFragments.set(position, mFragments.get(position));
// Replace the displayed fragment.
this.replace(position, fragment);
}
/**
* Replaces the current fragment by fragment stored in back stack. Does nothing and returns
* false if no fragment is back-stacked.
*/
public boolean back() {
int position = mContainer.getCurrentItem();
Fragment fragment = mBackFragments.get(position);
if (fragment == null) {
// Nothing to go back.
return false;
}
// Restore the remembered fragment and remove it from back fragments.
this.replace(position, fragment);
mBackFragments.set(position, null);
return true;
}
/**
* Returns fragment of a page at specified position.
*/
@Override
public Fragment getItem(int position) {
// If fragment not yet initialized, create its instance.
if (mFragments.get(position) == null) {
switch (position) {
case FIRST_PAGE: {
mFragments.set(FIRST_PAGE, new DefaultFirstFragment());
}
break;
case SECOND_PAGE: {
mFragments.set(SECOND_PAGE, new DefaultSecondFragment());
}
break;
case THIRD_PAGE: {
mFragments.set(THIRD_PAGE, new DefaultThirdFragment());
}
break;
}
}
// Return fragment instance at requested position.
return mFragments.get(position);
}
/**
* Custom item ID resolution. Needed for proper page fragment caching.
* @see FragmentPagerAdapter#getItemId(int).
*/
@Override
public long getItemId(int position) {
// Fragments from second level page hierarchy have their ID raised above 100. This is
// important to FragmentPagerAdapter because it is caching fragments to FragmentManager with
// this item ID key.
Fragment item = mFragments.get(position);
if (item != null) {
if ((item instanceof NewFirstFragment) || (item instanceof NewSecondFragment) ||
(item instanceof NewThirdFragment)) {
return 100 + position;
}
}
return position;
}
/**
* Returns number of pages.
*/
@Override
public int getCount() {
return mFragments.size();
}
@Override
public int getItemPosition(Object object)
{
int position = POSITION_UNCHANGED;
if ((object instanceof DefaultFirstFragment) || (object instanceof NewFirstFragment)) {
if (object.getClass() != mFragments.get(FIRST_PAGE).getClass()) {
position = POSITION_NONE;
}
}
if ((object instanceof DefaultSecondragment) || (object instanceof NewSecondFragment)) {
if (object.getClass() != mFragments.get(SECOND_PAGE).getClass()) {
position = POSITION_NONE;
}
}
if ((object instanceof DefaultThirdFragment) || (object instanceof NewThirdFragment)) {
if (object.getClass() != mFragments.get(THIRD_PAGE).getClass()) {
position = POSITION_NONE;
}
}
return position;
}
private ViewPager mContainer;
private FragmentManager mFragmentManager;
/**
* List of page fragments.
*/
private List<Fragment> mFragments;
/**
* List of page fragments to return to in onBack();
*/
private List<Fragment> mBackFragments;
}
/**
* Tab fragments adapter.
*/
private PagerAdapter mPagerAdapter;
}
インデックス2と3のために3つの要素と2つのサブ要素を持つViewPagerを作成しました。
私はStackOverFlowからの以前の質問と回答からの助けを借りてこれを実装しました、そしてここにリンクがあります。
ViewPager
内のフラグメントを置き換えるには、ViewPager
、PagerAdapter
およびFragmentStatePagerAdapter
クラスのソースコードをプロジェクトに移動して、次のコードを追加します。
ViewPager
に入力します。
public void notifyItemChanged(Object oldItem, Object newItem) {
if (mItems != null) {
for (ItemInfo itemInfo : mItems) {
if (itemInfo.object.equals(oldItem)) {
itemInfo.object = newItem;
}
}
}
invalidate();
}
fragmentStatePagerAdapterに:
public void replaceFragmetns(ViewPager container, Fragment oldFragment, Fragment newFragment) {
startUpdate(container);
// remove old fragment
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
int position = getFragmentPosition(oldFragment);
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, null);
mFragments.set(position, null);
mCurTransaction.remove(oldFragment);
// add new fragment
while (mFragments.size() <= position) {
mFragments.add(null);
}
mFragments.set(position, newFragment);
mCurTransaction.add(container.getId(), newFragment);
finishUpdate(container);
// ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
handleGetItemInbalidated(container, oldFragment, newFragment);
container.notifyItemChanged(oldFragment, newFragment);
}
protected abstract void handleGetItemInbalidated(View container, Fragment oldFragment, Fragment newFragment);
protected abstract int getFragmentPosition(Fragment fragment);
handleGetItemInvalidated()
は、次回のgetItem()
の呼び出し後にnewFragmentを返すことを保証しますgetFragmentPosition()
は、アダプタ内のフラグメントの位置を返すことを保証します。
さて、フラグメント呼び出しを置き換えるために
mAdapter.replaceFragmetns(mViewPager, oldFragment, newFragment);
あなたがサンプルプロジェクトに興味があるなら、ソースを私に尋ねてください。
AndroidTeamのソリューションとうまく動作しますが、FrgmentTransaction.addToBackStack(null)
のように戻る機能が必要であることがわかりました。しかしこれを追加するだけではViewPagerに通知せずにFragmentが置き換えられるだけです。提供されているソリューションとこのマイナーな機能強化を組み合わせることで、アクティビティのonBackPressed()
メソッドをオーバーライドするだけで前の状態に戻ることができます。最大の欠点は、一度に1つずつ戻るだけなので、複数のクリックが発生する可能性があることです。
private ArrayList<Fragment> bFragments = new ArrayList<Fragment>();
private ArrayList<Integer> bPosition = new ArrayList<Integer>();
public void replaceFragmentsWithBackOut(ViewPager container, Fragment oldFragment, Fragment newFragment) {
startUpdate(container);
// remove old fragment
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
int position = getFragmentPosition(oldFragment);
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
//Add Fragment to Back List
bFragments.add(oldFragment);
//Add Pager Position to Back List
bPosition.add(position);
mSavedState.set(position, null);
mFragments.set(position, null);
mCurTransaction.remove(oldFragment);
// add new fragment
while (mFragments.size() <= position) {
mFragments.add(null);
}
mFragments.set(position, newFragment);
mCurTransaction.add(container.getId(), newFragment);
finishUpdate(container);
// ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
handleGetItemInvalidated(container, oldFragment, newFragment);
container.notifyItemChanged(oldFragment, newFragment);
}
public boolean popBackImmediate(ViewPager container){
int bFragSize = bFragments.size();
int bPosSize = bPosition.size();
if(bFragSize>0 && bPosSize>0){
if(bFragSize==bPosSize){
int last = bFragSize-1;
int position = bPosition.get(last);
//Returns Fragment Currently at this position
Fragment replacedFragment = mFragments.get(position);
Fragment originalFragment = bFragments.get(last);
this.replaceFragments(container, replacedFragment, originalFragment);
bPosition.remove(last);
bFragments.remove(last);
return true;
}
}
return false;
}
これが誰かに役立つことを願っています。
またgetFragmentPosition()
に関してはgetItem()
とは逆になります。どのフラグメントがどこに行くのかを知っているので、正しい位置を確実に返すようにしてください。これは例です。
@Override
protected int getFragmentPosition(Fragment fragment) {
if(fragment.equals(originalFragment1)){
return 0;
}
if(fragment.equals(replacementFragment1)){
return 0;
}
if(fragment.equals(Fragment2)){
return 1;
}
return -1;
}
私はStacksを使った解決策も作りました。それはもっとモジュラーアプローチですので、あなたのFragmentPagerAdapter
でそれぞれのFragmentとDetail Fragmentを指定する必要はありません。それは私がGoogle Demo Appから正しければ派生するActionbarSherlockからの例の上に構築されています。
/**
* This is a helper class that implements the management of tabs and all
* details of connecting a ViewPager with associated TabHost. It relies on a
* trick. Normally a tab Host has a simple API for supplying a View or
* Intent that each tab will show. This is not sufficient for switching
* between pages. So instead we make the content part of the tab Host
* 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
* view to show as the tab content. It listens to changes in tabs, and takes
* care of switch to the correct paged in the ViewPager whenever the selected
* tab changes.
*
* Changed to support more Layers of fragments on each Tab.
* by sebnapi (2012)
*
*/
public class TabsAdapter extends FragmentPagerAdapter
implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
private final Context mContext;
private final TabHost mTabHost;
private final ViewPager mViewPager;
private ArrayList<String> mTabTags = new ArrayList<String>();
private HashMap<String, Stack<TabInfo>> mTabStackMap = new HashMap<String, Stack<TabInfo>>();
static final class TabInfo {
public final String tag;
public final Class<?> clss;
public Bundle args;
TabInfo(String _tag, Class<?> _class, Bundle _args) {
tag = _tag;
clss = _class;
args = _args;
}
}
static class DummyTabFactory implements TabHost.TabContentFactory {
private final Context mContext;
public DummyTabFactory(Context context) {
mContext = context;
}
@Override
public View createTabContent(String tag) {
View v = new View(mContext);
v.setMinimumWidth(0);
v.setMinimumHeight(0);
return v;
}
}
public interface SaveStateBundle{
public Bundle onRemoveFragment(Bundle outState);
}
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mTabHost = tabHost;
mViewPager = pager;
mTabHost.setOnTabChangedListener(this);
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
/**
* Add a Tab which will have Fragment Stack. Add Fragments on this Stack by using
* addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args)
* The Stack will hold always the default Fragment u add here.
*
* DON'T ADD Tabs with same tag, it's not beeing checked and results in unexpected
* beahvior.
*
* @param tabSpec
* @param clss
* @param args
*/
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args){
Stack<TabInfo> tabStack = new Stack<TabInfo>();
tabSpec.setContent(new DummyTabFactory(mContext));
mTabHost.addTab(tabSpec);
String tag = tabSpec.getTag();
TabInfo info = new TabInfo(tag, clss, args);
mTabTags.add(tag); // to know the position of the tab tag
tabStack.add(info);
mTabStackMap.put(tag, tabStack);
notifyDataSetChanged();
}
/**
* Will add the Fragment to Tab with the Tag _tag. Provide the Class of the Fragment
* it will be instantiated by this object. Proivde _args for your Fragment.
*
* @param fm
* @param _tag
* @param _class
* @param _args
*/
public void addFragment(FragmentManager fm, String _tag, Class<?> _class, Bundle _args){
TabInfo info = new TabInfo(_tag, _class, _args);
Stack<TabInfo> tabStack = mTabStackMap.get(_tag);
Fragment frag = fm.findFragmentByTag("Android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
if(frag instanceof SaveStateBundle){
Bundle b = new Bundle();
((SaveStateBundle) frag).onRemoveFragment(b);
tabStack.peek().args = b;
}
tabStack.add(info);
FragmentTransaction ft = fm.beginTransaction();
ft.remove(frag).commit();
notifyDataSetChanged();
}
/**
* Will pop the Fragment added to the Tab with the Tag _tag
*
* @param fm
* @param _tag
* @return
*/
public boolean popFragment(FragmentManager fm, String _tag){
Stack<TabInfo> tabStack = mTabStackMap.get(_tag);
if(tabStack.size()>1){
tabStack.pop();
Fragment frag = fm.findFragmentByTag("Android:switcher:" + mViewPager.getId() + ":" + mTabTags.indexOf(_tag));
FragmentTransaction ft = fm.beginTransaction();
ft.remove(frag).commit();
notifyDataSetChanged();
return true;
}
return false;
}
public boolean back(FragmentManager fm) {
int position = mViewPager.getCurrentItem();
return popFragment(fm, mTabTags.get(position));
}
@Override
public int getCount() {
return mTabStackMap.size();
}
@Override
public int getItemPosition(Object object) {
ArrayList<Class<?>> positionNoneHack = new ArrayList<Class<?>>();
for(Stack<TabInfo> tabStack: mTabStackMap.values()){
positionNoneHack.add(tabStack.peek().clss);
} // if the object class lies on top of our stacks, we return default
if(positionNoneHack.contains(object.getClass())){
return POSITION_UNCHANGED;
}
return POSITION_NONE;
}
@Override
public Fragment getItem(int position) {
Stack<TabInfo> tabStack = mTabStackMap.get(mTabTags.get(position));
TabInfo info = tabStack.peek();
return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
@Override
public void onTabChanged(String tabId) {
int position = mTabHost.getCurrentTab();
mViewPager.setCurrentItem(position);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// Unfortunately when TabHost changes the current tab, it kindly
// also takes care of putting focus on it when not in touch mode.
// The jerk.
// This hack tries to prevent this from pulling focus out of our
// ViewPager.
TabWidget widget = mTabHost.getTabWidget();
int oldFocusability = widget.getDescendantFocusability();
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mTabHost.setCurrentTab(position);
widget.setDescendantFocusability(oldFocusability);
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
これをMainActivityの戻るボタン機能に追加します。
@Override
public void onBackPressed() {
if (!mTabsAdapter.back(getSupportFragmentManager())) {
super.onBackPressed();
}
}
Uが好きならフラグメント状態を保存するそれが取り除かれたとき。あなたのFragmentがあなたのセーブ状態を持つバンドルを返す関数SaveStateBundle
を実装するようにしましょう。 this.getArguments()
によるインスタンス化後のバンドルを入手してください。
あなたはインスタンス化このようなタブをすることができます:
mTabsAdapter.addTab(mTabHost.newTabSpec("firstTabTag").setIndicator("First Tab Title"),
FirstFragmentActivity.FirstFragmentFragment.class, null);
tab Stackの上にFragmentを追加したい場合も同様です。 重要:2つのタブの上に同じクラスのインスタンスが2つある場合はうまくいきません。私は一緒にこの解決策を素早くしました、それで私はそれで少しの経験も提供しないでそれを共有することができるだけです。
onCreateView
メソッドでは、container
は実際にはViewPager
インスタンスです。
だから、ただ呼び出すだけ
ViewPager vpViewPager = (ViewPager) container;
vpViewPager.setCurrentItem(1);
ViewPager
の現在のフラグメントを変更します。
ビューページャでフラグメントを置き換えるのはかなり複雑ですが、それは非常に可能であり、とても滑らかに見えます。まず、ビューページャ自体にフラグメントの削除と追加を処理させる必要があります。何が起こっているかというと、SearchFragment内のフラグメントを置き換えると、viewpagerはそのフラグメントビューを保持したままになります。あなたがそれを置き換えようとするとSearchFragmentが削除されるので、あなたは空白のページになってしまいます。
解決策は、ビューページャの外側で行われた変更を処理するリスナをビューページャの内側に作成することです。最初にこのコードをアダプタの一番下に追加します。
public interface nextFragmentListener {
public void fragment0Changed(String newFragmentIdentification);
}
それから、フラグメントを変更したいときのリスナーになるプライベートクラスをあなたのviewpagerに作成する必要があります。例えば、あなたはこのようなものを追加することができます。作成したばかりのインタフェースが実装されていることに注意してください。そのため、このメソッドを呼び出すたびに、下のクラスの内部でコードが実行されます。
private final class fragmentChangeListener implements nextFragmentListener {
@Override
public void fragment0Changed(String fragment) {
//I will explain the purpose of fragment0 in a moment
fragment0 = fragment;
manager.beginTransaction().remove(fragAt0).commit();
switch (fragment){
case "searchFragment":
fragAt0 = SearchFragment.newInstance(listener);
break;
case "searchResultFragment":
fragAt0 = Fragment_Table.newInstance(listener);
break;
}
notifyDataSetChanged();
}
ここで指摘しなければならないことが2つあります。
NewInstance(listener)コンストラクターに配置されているリスナーに注目してください。これらは、fragment0Changed(String newFragmentIdentification) `を呼び出す方法です。次のコードは、フラグメント内にリスナーを作成する方法を示しています。
static nextFragmentListener listenerSearch;
public static Fragment_Journals newInstance(nextFragmentListener listener){
listenerSearch = listener;
return new Fragment_Journals();
}
あなたはあなたのonPostExecute
の中で変更を呼び出すことができます
private class SearchAsyncTask extends AsyncTask<Void, Void, Void>{
protected Void doInBackground(Void... params){
.
.//some more operation
.
}
protected void onPostExecute(Void param){
listenerSearch.fragment0Changed("searchResultFragment");
}
}
これはあなたのviewpagerの内側のコードが新しいsearchResultFragmentになるために位置0 fragAt0であなたのフラグメントを切り替えるためにトリガーするでしょう。それが機能するようになる前にviewpagerに追加する必要があるだろう2つのより小さな部分があります。
一つはviewpagerのgetItem overrideメソッドにあります。
@Override
public Fragment getItem(int index) {
switch (index) {
case 0:
//this is where it will "remember" which fragment you have just selected. the key is to set a static String fragment at the top of your page that will hold the position that you had just selected.
if(fragAt0 == null){
switch(fragment0){
case "searchFragment":
fragAt0 = FragmentSearch.newInstance(listener);
break;
case "searchResultsFragment":
fragAt0 = FragmentSearchResults.newInstance(listener);
break;
}
}
return fragAt0;
case 1:
// Games fragment activity
return new CreateFragment();
}
この最後の部分がなければ、空白のページが表示されます。不器用ですが、それはviewPagerの重要な部分です。 viewpagerのgetItemPositionメソッドをオーバーライドする必要があります。通常、このメソッドはPOSITION_UNCHANGEDを返します。これはviewpagerにすべてを同一に保つように指示するため、getItemは新しいフラグメントをページに配置するために呼び出されることはありません。これはあなたができることの例です。
public int getItemPosition(Object object)
{
//object is the current fragment displayed at position 0.
if(object instanceof SearchFragment && fragAt0 instanceof SearchResultFragment){
return POSITION_NONE;
//this condition is for when you press back
}else if{(object instanceof SearchResultFragment && fragAt0 instanceof SearchFragment){
return POSITION_NONE;
}
return POSITION_UNCHANGED
}
私が言ったように、コードは非常に複雑になりますが、あなたは基本的にあなたの状況のためにカスタムアダプタを作成しなければなりません。私が述べたことはフラグメントを変更することを可能にするでしょう。私が辛抱強く思うようにすべてを浸すのにはおそらく長い時間がかかるでしょうが、それはすべて理にかなっているでしょう。それは本当に滑らかに見えるアプリケーションを作ることができるのでそれは完全に時間をかける価値があります。
これが戻るボタンを扱うためのナゲットです。これをMainActivityの中に入れます
public void onBackPressed() {
if(mViewPager.getCurrentItem() == 0) {
if(pagerAdapter.getItem(0) instanceof FragmentSearchResults){
((Fragment_Table) pagerAdapter.getItem(0)).backPressed();
}else if (pagerAdapter.getItem(0) instanceof FragmentSearch) {
finish();
}
}
あなたは、fragment0changedを呼び出すFragmentSearchResultsの中にbackPressed()と呼ばれるメソッドを作成する必要があります。これは、前に示したコードと並行して、戻るボタンを押すことを処理します。ビューページャを変更するためのあなたのコードで頑張ってください。それは多くの作業を必要とします、そして私が見つけた限りでは、少しの適応もありません。私が言ったように、あなたは基本的にカスタムビューページャアダプタを作成していて、リスナを使って必要な変更のすべてをそれに処理させています
私は@ wizeと@ mdelolmoで答えをたどり、私は溶液。ありがとうトン。しかし、私はメモリ消費量を改善するためにこれらの解決策を少し調整しました。
私が観察した問題
それらは置き換えられたFragment
のインスタンスを保存します。私の場合、それはMapView
を保持するフラグメントであり、私はそのコストが高いと思いました。それで、私はFragment
自体の代わりにFragmentPagerPositionChanged (POSITION_NONE or POSITION_UNCHANGED)
を維持しています。
これが私の実装です。
public static class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
private SwitchFragListener mSwitchFragListener;
private Switch mToggle;
private int pagerAdapterPosChanged = POSITION_UNCHANGED;
private static final int TOGGLE_ENABLE_POS = 2;
public DemoCollectionPagerAdapter(FragmentManager fm, Switch toggle) {
super(fm);
mToggle = toggle;
mSwitchFragListener = new SwitchFragListener();
mToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mSwitchFragListener.onSwitchToNextFragment();
}
});
}
@Override
public Fragment getItem(int i) {
switch (i)
{
case TOGGLE_ENABLE_POS:
if(mToggle.isChecked())
{
return TabReplaceFragment.getInstance();
}else
{
return DemoTab2Fragment.getInstance(i);
}
default:
return DemoTabFragment.getInstance(i);
}
}
@Override
public int getCount() {
return 5;
}
@Override
public CharSequence getPageTitle(int position) {
return "Tab " + (position + 1);
}
@Override
public int getItemPosition(Object object) {
// This check make sures getItem() is called only for the required Fragment
if (object instanceof TabReplaceFragment
|| object instanceof DemoTab2Fragment)
return pagerAdapterPosChanged;
return POSITION_UNCHANGED;
}
/**
* Switch fragments Interface implementation
*/
private final class SwitchFragListener implements
SwitchFragInterface {
SwitchFragListener() {}
public void onSwitchToNextFragment() {
pagerAdapterPosChanged = POSITION_NONE;
notifyDataSetChanged();
}
}
/**
* Interface to switch frags
*/
private interface SwitchFragInterface{
void onSwitchToNextFragment();
}
}
デモリンクはこちら.. https://youtu.be/l_62uhKkLyM
デモ目的で、位置2に2つのフラグメントTabReplaceFragment
とDemoTab2Fragment
を使用しました。他のすべてのケースではDemoTabFragment
インスタンスを使っています。
説明:
ActivityからSwitch
をDemoCollectionPagerAdapter
に渡しています。このスイッチの状態に基づいて、正しいフラグメントを表示します。切り替えチェックが変更されたら、SwitchFragListener
のonSwitchToNextFragment
メソッドを呼び出します。ここでは、pagerAdapterPosChanged
変数の値をPOSITION_NONE
に変更します。 POSITION_NONE について詳しく調べてください。これはgetItemを無効にし、私は向こうに正しいフラグメントをインスタンス化する論理を持っています。説明が少し面倒な場合は申し訳ありません。
元のアイディアを提供してくれた@wizeと@mdelolmoに感謝します。
これが役に立つことを願っています。 :)
この実装に問題があるかどうかを教えてください。それは私のプロジェクトにとって大いに役立つでしょう。
私はwizeに似たようなことをしていますが、私の答えではあなたが望むときはいつでも2つの断片の間で変わることができます。そして賢明な答えで、私はスクリーンの向きをそのように変更するときいくつかの問題を抱えています。これはPagerAdapterです。
public class MyAdapter extends FragmentPagerAdapter
{
static final int NUM_ITEMS = 2;
private final FragmentManager mFragmentManager;
private Fragment mFragmentAtPos0;
private Map<Integer, String> mFragmentTags;
private boolean isNextFragment=false;
public MyAdapter(FragmentManager fm)
{
super(fm);
mFragmentManager = fm;
mFragmentTags = new HashMap<Integer, String>();
}
@Override
public Fragment getItem(int position)
{
if (position == 0)
{
if (isPager) {
mFragmentAtPos0 = new FirstPageFragment();
} else {
mFragmentAtPos0 = new NextFragment();
}
return mFragmentAtPos0;
}
else
return SecondPageFragment.newInstance();
}
@Override
public int getCount()
{
return NUM_ITEMS;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object obj = super.instantiateItem(container, position);
if (obj instanceof Fragment) {
// record the fragment tag here.
Fragment f = (Fragment) obj;
String tag = f.getTag();
mFragmentTags.put(position, tag);
}
return obj;
}
public void onChange(boolean isNextFragment) {
if (mFragmentAtPos0 == null)
mFragmentAtPos0 = getFragment(0);
if (mFragmentAtPos0 != null)
mFragmentManager.beginTransaction().remove(mFragmentAtPos0).commit();
if (!isNextFragment) {
mFragmentAtFlashcards = new FirstPageFragment();
} else {
mFragmentAtFlashcards = new NextFragment();
}
notifyDataSetChanged();
}
@Override
public int getItemPosition(Object object)
{
if (object instanceof FirstPageFragment && mFragmentAtPos0 instanceof NextFragment)
return POSITION_NONE;
if (object instanceof NextFragment && mFragmentAtPos0 instanceof FirstPageFragment)
return POSITION_NONE;
return POSITION_UNCHANGED;
}
public Fragment getFragment(int position) {
String tag = mFragmentTags.get(position);
if (tag == null)
return null;
return mFragmentManager.findFragmentByTag(tag);
}
}
アタッチするときにフラグメントに追加するためにアダプタコンテナアクティビティに実装したリスナです。これがアクティビティです。
public class PagerContainerActivity extends AppCompatActivity implements ChangeFragmentListener {
//...
@Override
public void onChange(boolean isNextFragment) {
if (pagerAdapter != null)
pagerAdapter.onChange(isNextFragment);
}
//...
}
それから呼び出しをアタッチするときリスナーを置くフラグメントの中で:
public class FirstPageFragment extends Fragment{
private ChangeFragmentListener changeFragmentListener;
//...
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
changeFragmentListener = ((PagerContainerActivity) activity);
}
@Override
public void onDetach() {
super.onDetach();
changeFragmentListener = null;
}
//...
//in the on click to change the fragment
changeFragmentListener.onChange(true);
//...
}
そして最後にリスナー:
public interface changeFragmentListener {
void onChange(boolean isNextFragment);
}
私はあなたが途中で新しいフラグメントを追加したり、現在のフラグメントを交換したい場合でもうまく動作する簡単な解決策を見つけました。私の解決策では、あなたはgetItemId()
をオーバーライドすべきです。デフォルトでは配置されていません。
そこにあります:
public class DynamicPagerAdapter extends FragmentPagerAdapter {
private ArrayList<Page> mPages = new ArrayList<Page>();
private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
public DynamicPagerAdapter(FragmentManager fm) {
super(fm);
}
public void replacePage(int position, Page page) {
mPages.set(position, page);
notifyDataSetChanged();
}
public void setPages(ArrayList<Page> pages) {
mPages = pages;
notifyDataSetChanged();
}
@Override
public Fragment getItem(int position) {
if (mPages.get(position).mPageType == PageType.FIRST) {
return FirstFragment.newInstance(mPages.get(position));
} else {
return SecondFragment.newInstance(mPages.get(position));
}
}
@Override
public int getCount() {
return mPages.size();
}
@Override
public long getItemId(int position) {
// return unique id
return mPages.get(position).getId();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
while (mFragments.size() <= position) {
mFragments.add(null);
}
mFragments.set(position, fragment);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
mFragments.set(position, null);
}
@Override
public int getItemPosition(Object object) {
PagerFragment pagerFragment = (PagerFragment) object;
Page page = pagerFragment.getPage();
int position = mFragments.indexOf(pagerFragment);
if (page.equals(mPages.get(position))) {
return POSITION_UNCHANGED;
} else {
return POSITION_NONE;
}
}
}
Notice:この例では、FirstFragment
およびSecondFragment
は、メソッドgetPage()
を持つ抽象クラスPageFragmentを拡張します。
これがこの問題に対する私の比較的簡単な解決策です。後者はまだそれらのインスタンスを保持しながら前者はあなたのために未使用のフラグメントを削除するので、この解決策への鍵はFragmentStatePagerAdapter
の代わりにFragmentPagerAdapter
を使用することです。 2つ目は、getItem()でPOSITION_NONE
を使用することです。私は自分のフラグメントを追跡するために簡単なリストを使いました。私の要件は、フラグメントのリスト全体を一度に新しいリストに置き換えることでしたが、以下は個々のフラグメントを置き換えるように簡単に変更できます。
public class MyFragmentAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragmentList = new ArrayList<Fragment>();
private List<String> tabTitleList = new ArrayList<String>();
public MyFragmentAdapter(FragmentManager fm) {
super(fm);
}
public void addFragments(List<Fragment> fragments, List<String> titles) {
fragmentList.clear();
tabTitleList.clear();
fragmentList.addAll(fragments);
tabTitleList.addAll(titles);
notifyDataSetChanged();
}
@Override
public int getItemPosition(Object object) {
if (fragmentList.contains(object)) {
return POSITION_UNCHANGED;
}
return POSITION_NONE;
}
@Override
public Fragment getItem(int item) {
if (item >= fragmentList.size()) {
return null;
}
return fragmentList.get(item);
}
@Override
public int getCount() {
return fragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return tabTitleList.get(position);
}
}
研究の後、私は短いコードで解決策を見つけました。まず最初にfragmentにpublicインスタンスを作成し、方向が変わってもフラグメントが再作成されない場合はonSaveInstanceStateにある自分のフラグメントを削除します。
@Override
public void onSaveInstanceState(Bundle outState) {
if (null != mCalFragment) {
FragmentTransaction bt = getChildFragmentManager().beginTransaction();
bt.remove(mFragment);
bt.commit();
}
super.onSaveInstanceState(outState);
}
これが私のやり方です。
まず最初に、ボタンクリックviewPager
イベントを実装したいfragment
タブ内にRoot_fragment
を追加します。例
@Override
public Fragment getItem(int position) {
if(position==0)
return RootTabFragment.newInstance();
else
return SecondPagerFragment.newInstance();
}
まず第一に、フラグメントの変更のためにRootTabFragment
にFragmentLayout
を含める必要があります。
<FrameLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:id="@+id/root_frame"
Android:layout_width="match_parent"
Android:layout_height="match_parent">
</FrameLayout>
次に、RootTabFragment
onCreateView
内に、あなたのfragmentChange
に対してFirstPagerFragment
を実装します。
getChildFragmentManager().beginTransaction().replace(R.id.root_frame, FirstPagerFragment.newInstance()).commit();
その後、onClick
内のボタンにFirstPagerFragment
イベントを実装して、フラグメントの変更をもう一度そのようにします。
getChildFragmentManager().beginTransaction().replace(R.id.root_frame, NextFragment.newInstance()).commit();
これが君の手助けになることを願っている。