2012-01-29 95 views
6

(如果有人需要更多的信息,或者更好的描述讓我知道)Viewpager:如果網頁遭到移除接下來的幾頁內容獲取的內容已刪除的網頁

你好,我包括viewPagerLibrary從這裏:http://viewpagerindicator.com/#introduction今天我的項目。

不,我得到一個非常奇怪的問題: 如果我添加一個網站或網頁(讓我們在接下來的幾行稱它爲網站),並再次刪除它一切都很好。但是,如果我嘗試添加不同的頁面(這些頁面是實現BaseFragment類的不同Fragements),則會顯示第一頁的內容。 同樣的事情發生,如果我添加幾個頁面,並刪除這些頁面之間的中間。已刪除頁面之後的頁面現在顯示已刪除的頁面內容。

這個bug的一個例子: 現在的問題是。如果我在FragmentB之後添加FragmentA,那麼我刪除FragmentA,FragmentB獲取FragmentA的視圖/內容。奇怪的是,對象是正確的(所以適配器返回正確的對象),標題也是正確的。

在我的主要創建我的傳呼機,指示器和適配器這樣:

Cfg.mAdapter = new FragmentAdapter(getSupportFragmentManager()); 

    Cfg.mPager = (ViewPager)findViewById(R.id.pager); 
    Cfg.mPager.setAdapter(Cfg.mAdapter); 

    Cfg.mIndicator = (TabPageIndicator)findViewById(R.id.indicator); 
    Cfg.mIndicator.setViewPager(Cfg.mPager); 

    //We set this on the indicator, NOT the pager 
    Cfg.mIndicator.setOnPageChangeListener(TabHelper.onPageChangeListener); 

(CFG的是一個靜態的文件來存儲使用那些東西)

我BaseFragment如下所示:

public class BaseFragment extends Fragment{ 

    public static int FILE_FRAGMENT = 0; 
    public static int FTP_FRAGMENT = 1; 
    public static int ADDFTP_FRAGMENT = 2; 
    public static int PREVIEW_FRAGMENT = 3; 
    public static int CSS_FRAGMENT = 4; 
    public static int BOOKS_FRAGMENT = 5; 
    public static int SNIPPETS_FRAGMENT = 6; 

    //private int id; 
    private int typ; 
    private String title; 

    public int getTyp() { 
     return typ; 
    } 

    public void setTyp(int typ) { 
     this.typ = typ; 
    } 

    public String getTitle() { 
     return title; 
    } 

    public void setTitle(String title) { 
     this.title = title; 
    } 
} 

一個片段是這樣的(我想其他片段沒有什麼區別):

public class FtpFragment extends BaseFragment { 
    private static RowLayout rowLayout_view; 

    public FtpFragment() { 
    } 

    @Override 
    public void onActivityCreated(Bundle savedInstanceState) { 
     super.onActivityCreated(savedInstanceState); 
     init_data(); 
    } 

    public static void init_data() 
    { 
     //Remove child for update 
     rowLayout_view.removeAllViews(); 

     List<FtpData> ftps = FtpStorage.getInstance().getFtps(); 

     if (ftps != null) { 
      for (FtpData f : ftps) { 
       View inflatedView; 
       inflatedView = View.inflate(Cfg.ctx, R.layout.ftp, null); 
       inflatedView.setOnClickListener(button_ftp_listener); 
       inflatedView.setOnLongClickListener(button_ftp_longClickListener); 
       inflatedView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, Converter.convertFromDPtoPixel(160.0f))); 
       inflatedView.setTag(f); 
       inflatedView.findViewById(R.id.book_imageview).setBackgroundDrawable(
           Cfg.ctx.getResources().getDrawable(R.drawable.nopreview)); 


       ((TextView) inflatedView.findViewById(R.id.book_textview)).setText(f.nickname); 

       rowLayout_view.addView(inflatedView); 
      } 
     } 
    } 

    @Override 

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    View v = inflater.inflate(R.layout.fragment_ftp, container, false); 
    rowLayout_view = (RowLayout) v.findViewById(R.id.rowLayout_ftps); 
    return v; 
} 

@Override 
public String getTitle() { 
    return "FTPs"; 
} 

@Override 
public int getTyp() { 
    return BaseFragment.FTP_FRAGMENT; 
} 

@Override 
public void setTyp(int typ) { 
    super.setTyp(typ); 
} 

}

要刪除或添加頁面我稱之爲:

public static void addNewTab(BaseFragment fragment) 
{ 
    Cfg.mAdapter.addItem(fragment); 
    Cfg.mPager.setCurrentItem(Cfg.mAdapter.getCount()); 
    Cfg.mIndicator.notifyDataSetChanged(); 
} 

public static void deleteActTab() 
{ 
    Cfg.mAdapter.removeItem(Cfg.mAdapter.getActPage()); 
    Cfg.mIndicator.notifyDataSetChanged(); 
} 

這就是適配器:

public class FragmentAdapter extends FragmentPagerAdapter implements TitleProvider{ 
    public List<BaseFragment> fragments = new LinkedList<BaseFragment>(); 

    private int actPage; 

    public FragmentAdapter(FragmentManager fm) { 
     super(fm); 
    } 


    public void setActPage(int actPage) { 
     Lg.d("setActPage: " + actPage + " : " + fragments.get(actPage).toString()); 
     this.actPage = actPage; 
    } 

    public void addItem(BaseFragment fragment) 
    { 
     Lg.d("addItem: " + fragment.toString()); 
     fragments.add(fragment); 
    } 

    public void removeItem(int index) 
    { 
     if(index < getCount()){ 
      Lg.d("RemoveItem: " + index + " : " + fragments.get(index).toString()); 
      fragments.remove(index); 
     } 
    } 

    public BaseFragment getActFragment() 
    { 
     return getItem(getActPage()); 
    } 

    public int getActPage() { 
     return actPage; 
    } 

    @Override 
    public BaseFragment getItem(int position) { 
     if(position < getCount()) 
     { 
      Lg.v("getItem: " + fragments.get(position)); 
      return fragments.get(position); 
     } 
     else 
      return null; 
    } 

    @Override 
    public int getCount() { 
     return fragments.size(); 
    } 

    @Override 
    public String getTitle(int position) { 
     Lg.v("Get Title: " + fragments.get(position).getTitle()); 
     return fragments.get(position).getTitle(); 
    } 

} 

是的,我希望有人能幫助我。

如果我忘了什麼讓我知道。

由於提前, 邁克

+0

任何有關這裏提到的問題的建議http://stackoverflow.com/q/25159181/2624806 – CoDe 2014-08-06 12:56:24

回答

2

好吧,我現在用黑客的方式解決了我的問題,但是它的工作;)。如果有人能改善我的解決方案,請告訴我。對於我的新解決方案,我現在使用CustomFragmentStatePagerAdapter,但它不會像保存狀態那樣保存狀態,並將所有片段存儲在列表中。這可能會導致內存問題,如果用戶有超過50個片段,就像普通的FragmentPagerAdapter一樣。如果有人能夠在不刪除我的修補程序的情況下將狀態信息添加回我的解決方案,那將會很棒。謝謝。

因此,這裏是我的CustomFragmentStatePagerAdapter.java

package com.tundem.webLab.Adapter; 

import java.util.ArrayList; 

import android.os.Bundle; 
import android.os.Parcelable; 
import android.support.v4.app.Fragment; 
import android.support.v4.app.FragmentManager; 
import android.support.v4.app.FragmentTransaction; 
import android.support.v4.view.PagerAdapter; 
import android.util.Log; 
import android.view.View; 
import android.view.ViewGroup; 

public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter { 
    private static final String TAG = "FragmentStatePagerAdapter"; 
    private static final boolean DEBUG = false; 

    private final FragmentManager mFragmentManager; 
    private FragmentTransaction mCurTransaction = null; 

    public ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>(); 
    public ArrayList<Fragment> mFragments = new ArrayList<Fragment>(); 
    private Fragment mCurrentPrimaryItem = null; 

    public CustomFragmentStatePagerAdapter(FragmentManager fm) { 
     mFragmentManager = fm; 
    } 

    /** 
    * Return the Fragment associated with a specified position. 
    */ 
    public abstract Fragment getItem(int position); 

    @Override 
    public void startUpdate(ViewGroup container) {} 

    @Override 
    public Object instantiateItem(ViewGroup container, int position) { 
     // If we already have this item instantiated, there is nothing 
     // to do. This can happen when we are restoring the entire pager 
     // from its saved state, where the fragment manager has already 
     // taken care of restoring the fragments we previously had instantiated. 

     // DONE Remove of the add process of the old stuff 
     /* if (mFragments.size() > position) { Fragment f = mFragments.get(position); if (f != null) { return f; } } */ 

     if (mCurTransaction == null) { 
      mCurTransaction = mFragmentManager.beginTransaction(); 
     } 

     Fragment fragment = getItem(position); 
     if (DEBUG) 
      Log.v(TAG, "Adding item #" + position + ": f=" + fragment); 
     if (mSavedState.size() > position) { 
      Fragment.SavedState fss = mSavedState.get(position); 
      if (fss != null) { 
       try // DONE: Try Catch 
       { 
        fragment.setInitialSavedState(fss); 
       } catch (Exception ex) { 
        // Schon aktiv (kA was das heißt xD) 
       } 
      } 
     } 
     while (mFragments.size() <= position) { 
      mFragments.add(null); 
     } 
     fragment.setMenuVisibility(false); 
     mFragments.set(position, fragment); 
     mCurTransaction.add(container.getId(), fragment); 

     return fragment; 
    } 

    @Override 
    public void destroyItem(ViewGroup container, int position, Object object) { 
     Fragment fragment = (Fragment) object; 

     if (mCurTransaction == null) { 
      mCurTransaction = mFragmentManager.beginTransaction(); 
     } 
     mCurTransaction.remove(fragment); 

     /*if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object + " v=" + ((Fragment) 
     * object).getView()); while (mSavedState.size() <= position) { mSavedState.add(null); } mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment)); 
     * mFragments.set(position, null); mCurTransaction.remove(fragment); */ 
    } 

    @Override 
    public void setPrimaryItem(ViewGroup container, int position, Object object) { 
     Fragment fragment = (Fragment) object; 
     if (fragment != mCurrentPrimaryItem) { 
      if (mCurrentPrimaryItem != null) { 
       mCurrentPrimaryItem.setMenuVisibility(false); 
      } 
      if (fragment != null) { 
       fragment.setMenuVisibility(true); 
      } 
      mCurrentPrimaryItem = fragment; 
     } 
    } 

    @Override 
    public void finishUpdate(ViewGroup container) { 
     if (mCurTransaction != null) { 
      mCurTransaction.commitAllowingStateLoss(); 
      mCurTransaction = null; 
      mFragmentManager.executePendingTransactions(); 
     } 
    } 

    @Override 
    public boolean isViewFromObject(View view, Object object) { 
     return ((Fragment) object).getView() == view; 
    } 

    @Override 
    public Parcelable saveState() { 
     Bundle state = null; 
     if (mSavedState.size() > 0) { 
      state = new Bundle(); 
      Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()]; 
      mSavedState.toArray(fss); 
      state.putParcelableArray("states", fss); 
     } 
     for (int i = 0; i < mFragments.size(); i++) { 
      Fragment f = mFragments.get(i); 
      if (f != null) { 
       if (state == null) { 
        state = new Bundle(); 
       } 
       String key = "f" + i; 
       mFragmentManager.putFragment(state, key, f); 
      } 
     } 
     return state; 
    } 

    @Override 
    public void restoreState(Parcelable state, ClassLoader loader) { 
     if (state != null) { 
      Bundle bundle = (Bundle) state; 
      bundle.setClassLoader(loader); 
      Parcelable[] fss = bundle.getParcelableArray("states"); 
      mSavedState.clear(); 
      mFragments.clear(); 
      if (fss != null) { 
       for (int i = 0; i < fss.length; i++) { 
        mSavedState.add((Fragment.SavedState) fss[i]); 
       } 
      } 
      Iterable<String> keys = bundle.keySet(); 
      for (String key : keys) { 
       if (key.startsWith("f")) { 
        int index = Integer.parseInt(key.substring(1)); 
        Fragment f = mFragmentManager.getFragment(bundle, key); 
        if (f != null) { 
         while (mFragments.size() <= index) { 
          mFragments.add(null); 
         } 
         f.setMenuVisibility(false); 
         mFragments.set(index, f); 
        } else { 
         Log.w(TAG, "Bad fragment at key " + key); 
        } 
       } 
      } 
     } 
    } 
} 

這裏是我的正常FragmentAdapter.java

package com.tundem.webLab.Adapter; 

import java.util.LinkedList; 
import java.util.List; 

import android.support.v4.app.FragmentManager; 

import com.tundem.webLab.fragments.BaseFragment; 
import com.viewpagerindicator.TitleProvider; 

public class FragmentAdapter extends CustomFragmentStatePagerAdapter implements TitleProvider { 
    public List<BaseFragment> fragments = new LinkedList<BaseFragment>(); 

    private int actPage; 

    public FragmentAdapter(FragmentManager fm) { 
     super(fm); 
    } 

    public void setActPage(int actPage) { 
     this.actPage = actPage; 
    } 

    public void addItem(BaseFragment fragment) { 
     // TODO if exists don't open/change to that tab 
     fragments.add(fragment); 
    } 

    public BaseFragment getActFragment() { 
     return getItem(getActPage()); 
    } 

    public int getActPage() { 
     return actPage; 
    } 

    @Override 
    public BaseFragment getItem(int position) { 
     if (position < getCount()) { 
      return fragments.get(position); 
     } else 
      return null; 
    } 

    @Override 
    public int getCount() { 
     return fragments.size(); 
    } 

    @Override 
    public String getTitle(int position) { 
     return fragments.get(position).getTitle(); 
    } 

    @Override 
    public int getItemPosition(Object object) { 
     return POSITION_NONE; 
    } 
} 

這也是我刪除片段的方式。 (我知道這僅僅是.remove())。可以自由地改進我的解決方案,你也可以在適配器的某個地方添加此代碼,所以是的。這取決於試圖實現這一點的用戶。我在TabHelper.java使用(它處理如刪除所有選項卡操作的類,添加...)的CFG的

int act = Cfg.mPager.getCurrentItem(); 
    Cfg.mPager.removeAllViews(); 
    Cfg.mAdapter.mFragments.remove(act); 
    try { 
     Cfg.mAdapter.mSavedState.remove(act); 
    } catch (Exception ex) {/* Already removed */} 
    try { 
     Cfg.mAdapter.fragments.remove(act); 
    } catch (Exception ex) {/* Already removed */} 

    Cfg.mAdapter.notifyDataSetChanged(); 
    Cfg.mIndicator.notifyDataSetChanged(); 

描述。事情。我將這些對象的引用保存在cfg中,因此我可以隨時使用它們而不需要特殊的Factory.java ...

是的。我希望我能夠幫助。隨意改善這一點,但讓我知道,所以我也可以提高我的代碼。

謝謝。

如果我錯過任何代碼讓我知道。


我的舊回答也有效,但只有當你有不同的片段。 FileFragment,WebFragment,...如果您使用兩次這些片段類型之一,則不適用。

我現在僞裝了它。這是一個非常骯髒的解決方案,我仍然在尋找更好的解決方案。請幫忙。

我改變了代碼,在那裏我刪除選項卡,這一個:

public static void deleteActTab() 
     { 
      //We set this on the indicator, NOT the pager 
      int act = Cfg.mPager.getCurrentItem(); 
      Cfg.mAdapter.removeItem(act); 
      List<BaseFragment> frags = new LinkedList<BaseFragment>(); 
      frags = Cfg.mAdapter.fragments; 

      Cfg.mPager = (ViewPager)Cfg.act.findViewById(R.id.pager); 
      Cfg.mPager.setAdapter(Cfg.mAdapter); 
      Cfg.mIndicator.setViewPager(Cfg.mPager); 

      Cfg.mAdapter.fragments = frags; 

      if(act > 0) 
      { 
       Cfg.mPager.setCurrentItem(act-1); 
       Cfg.mIndicator.setCurrentItem(act-1); 
      } 

      Cfg.mIndicator.notifyDataSetChanged(); 
     } 

如果有人能改善這個代碼,讓我知道。如果有人能告訴我們這個問題的真正答案。請在此處添加。有許多人遇到過這個問題。我爲解決問題的人增加了50的聲望。我也可以爲解決問題的人捐款。

感謝

0

在你的答案(由mikepenz),則無需再次設置適配器。你可以調用notifyDataSetChanged。

public static void deleteActTab(){ 

     //We set this on the indicator, NOT the pager 
     int act = Cfg.mPager.getCurrentItem(); 
     Cfg.mAdapter.removeItem(act);   


     if(act > 0) 
     { 
      Cfg.mPager.setCurrentItem(act-1); 
      Cfg.mIndicator.setCurrentItem(act-1); 
     } 
     //Also add conditions to check if there are any remaining fragments 


     Cfg.mIndicator.notifyDataSetChanged(); 
} 

你有沒有考慮使用HashMap<Integer, Fragment>ArrayAdapter<Fragment>以提高性能或已經在使用它? 另外,爲什麼你在BaseFragment中使用靜態方法?請考慮使用MAT或logcat來檢查內存使用情況,如果這些泄漏內存。

+0

就像你可以看到我的原始問題,我做了它就像你在這裏提到它。但這件事不起作用,因爲內容不會更新正確。似乎mPager沒有正確管理片段。你能給出一個更高級的示例代碼,說明你對HashMap或ArrayAdapter的意義嗎?請記住,我使用擴展我的BaseFragment的不同片段。 – mikepenz 2012-02-01 20:19:14

0

爲了避免這個問題,你必須從堆棧中刪除指定的片段。每次從列表中刪除片段時,它都會保留在backstack中,因此內容將保留。在從列表中刪除片段之前,您必須使用FragmentTransaction刪除頁面。然後代碼可能是這樣的。

public void removePage(int currentPage) { 
    for (int i = pageFragmentList.size() - 1; i >= currentPage; i--) { 
     ((MainActivity) context).getSupportFragmentManager().beginTransaction() 
       .remove(pageFragmentList.get(i)).commit(); 
    } 
    pageFragmentList.remove(currentPage); 
} 

如果不刪除當前頁面後索引的所有頁面,只有從堆棧中刪除當前頁面時,它可能會拋出異常。