2012-01-29 96 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); 
} 

如果不删除当前页面后索引的所有页面,只有从堆栈中删除当前页面时,它可能会抛出异常。