Viewpager: при удалении страницы содержимое следующих страниц получает содержимое удаленных страниц - PullRequest
6 голосов
/ 30 января 2012

(Если кому-то нужна дополнительная информация или лучшее описание, дайте мне знать)

Здравствуйте, я включил viewPagerLibrary отсюда: http://viewpagerindicator.com/#introduction сегодня в мой проект.

Нет, у меня действительно странная проблема: если я добавляю сайт или страницу (назовем это сайтом в следующих нескольких строках) и удаляю их снова, все в порядке.Но если я пытаюсь добавить другую страницу (эти страницы - разные Fragements, в которых реализован класс BaseFragment), отображается содержимое первой страницы.То же самое происходит, если я добавляю несколько страниц и удаляю одну промежуточную между этими страницами.Страница, которая была после удаленной страницы, теперь отображает содержимое удаленных страниц.

Пример этой ошибки: Проблема теперь в.Если я добавляю FragmentA после этого FragmentB, то я удаляю 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();
    }

}

Да, я надеюсь, что кто-то может мне помочь.

Если я что-то забыл, дайте мне знать.

Заранее спасибо, Майк

Ответы [ 3 ]

2 голосов
/ 04 февраля 2012

Хорошо, теперь я решил свою проблему хакерским способом, но да, это работает;). Если кто-то может улучшить мое решение, пожалуйста, дайте мне знать. Для моего нового решения я теперь использую CustomFragmentStatePagerAdapter, но он не сохраняет состояние, как должно, и сохраняет все фрагменты в списке. Это может вызвать проблемы с памятью, если у пользователя более 50 фрагментов, как у обычного FragmentPagerAdapter. Было бы здорово, если бы кто-то мог добавить State-thing обратно в мое решение, не удаляя мои исправления. Спасибо.

Итак, вот мое 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 (Класс, который обрабатывает все операции вкладки, такие как удаление, добавление, ...)

    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. вещь. Я сохраняю ссылку на эти объекты в классе 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 голосов
/ 30 ноября 2015

Чтобы избежать этой проблемы, вы должны удалить указанный фрагмент из backstack .Каждый раз, когда вы удаляете фрагмент из своего списка, он остается в 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);
}

Если вы не удалите все страницы, проиндексированные после текущей страницы, и удалите только текущую страницу из backstack , это может привести кбросить исключение.

0 голосов
/ 02 февраля 2012

В вашем ответе (от 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, чтобы проверить использование памяти, если эти утечки памяти.

...