更新FragmentStatePagerAdapter中的视图/幻灯片(片段) - 简单的重新排序

时间:2013-06-18 16:39:30

标签: android fragmentpageradapter fragmentstatepageradapter

我尝试使用扩展的FragmentStatePagerAdapter和扩展的FragmentPagerAdapter来实现我正在尝试的东西,但似乎都无法工作。我从我的服务器发送一个JSON对象,以填充Android客户端上的对象模型。在第一次加载时,一切都很完美,我可以从头到尾轻扫页面。这是我的对象模型:

ContestEntriesModel.java

public class ContestEntriesModel {
    public int ContestId;
    public String ContestName;
    public ContestEntryModel[] entries;
}

如您所见,有一个ContestEntryModel数组。每个模型都是这样的:

ContestEntryModel.java

public class ContestEntryModel {
    public long ContestEntryId;
    public int UserId;
    public String FullName;
    public int Comments;
    public string ImageUrl;
}

如上所述,当第一个数据有效负载从服务器下来时,我在ContestEntryModel []数组中为每个项目填充一个片段,其中包含来自该模型的数据(包括来自Internet资源的图像(ImageUrl)),以及这很有效。我可以滑动,直到我的手指流血,这一切在世界上都很好。

我正在尝试从服务器发送另一个有效负载,该服务器具有不同顺序的数组中的项目(我最终也会添加新项目,但一次只能添加一个步骤)。我已经在StackOverflow上搜索了一个解决方案,并在昨天发布了一个问题,让我接近我需要的地方(感谢Reinier),但我仍然在努力让我的所有碎片重新排序并拥有适配器完全刷新新订单。适配器似乎缓存了一些幻灯片(与我正在查看的幻灯片相邻的幻灯片)并且它们不会“刷新”,直到我将一些图像滑过它们然后返回。昨天的麻烦是当新数据从我工作的服务器上下来时,当前查看的幻灯片没有“刷新”...等等。假设我的数组中有7个项目,我在第一次加载时滑动到#3(索引2)。然后我从服务器推送新数据,这只是反向排序数组。然后,当前查看的幻灯片(幻灯片#3)应移动到索引位置4,我应该只能向右滑动2次(索引位置5和6)。相反,正在发生的是它正在记住该幻灯片的当前索引位置(索引2)并将其保留在那里。如上所述,幻灯片#2(索引1)和幻灯片#4(索引3)也在维护。我预期的行为是那些幻灯片应该反转幻灯片#3(索引2)周围的位置。

这是我的PagerAdapter的实现(这是FragmentStatePagerAdapter而不是FragmentPagerAdapter的版本:

ScreenSlidePagerAdapter:

public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
    public ContestEntriesModel entries;

    public ScreenSlidePagerAdapter(FragmentManager fm, ContestEntriesModel Entries) {
        super(fm);
        this.entries = Entries;
    }

    public long getItemId(int position) {
        return entries.entries[position].ContestEntryId;

    }

    @Override
    public int getItemPosition(Object object) {
        android.support.v4.app.Fragment f = (android.support.v4.app.Fragment)object;
        for(int i = 0; i < getCount(); i++){

            android.support.v4.app.Fragment fragment = getItem(i);
            if(f.equals(fragment)){
                //Log.d("currentPosition",Integer.toString(i));
                return i;
            }
        }
        return POSITION_NONE;
    }


    @Override
    public android.support.v4.app.Fragment getItem(int position) {

        long entryId = getItemId(position);
        //Log.d("EntryIds",Integer.toString(entryId));
        if(mItems.get(entryId) != null) {
            return mItems.get(entryId);
        }
        Fragment f = ScreenSlidePageFragment.create(position);
        mItems.put(entryId, f);
        return f;
    }

    @Override
    public int getCount() {
        return NUM_PAGES;
    }
}

目前我正在为我的适配器设置一个静态属性(我在其他一些“外部”类中使用它),如下所示:

MainActivity:

    public class MainActivity extends FragmentActivity {

        public static int NUM_PAGES = 0;
        public static ViewPager mPager;
        public static PagerAdapter mPagerAdapter;
        public static ContestEntriesModel Entries;
        public static HashMap<Long, android.support.v4.app.Fragment> mItems = new HashMap<Long, android.support.v4.app.Fragment>();
        public static Long CurrentEntryId;


        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.entries_layout);
            GetEntriesTask task = new GetEntriesTask(43);
            task.execute();
        }


        public void LoadEntries()
        {
            mPager = (ViewPager) findViewById(R.id.pager);
            mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager(), Entries);
            mPager.setAdapter(mPagerAdapter);
            mPager.setPageTransformer(true, new MyPageTransformer());
            mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
                @Override
                public void onPageSelected(int position) {
                    ContestEntries.CurrentEntryId = ContestEntries.Entries.entries[position].ContestEntryId;
                    Log.d("currentEntry",Long.toString(ContestEntries.CurrentEntryId));
                    invalidateOptionsMenu();
                }
            });

        }
//the rest is omitted as it's not pertinent to my issue.

当一个新的dataload从服务器下来时,我只是将我的静态属性(Entries)设置为新版本。我还尝试访问PagerAdapter(条目)中的公共属性来直接修改它,但由于某种原因无法访问(即使类和属性都有公共访问器)。我认为这就是出错的地方,因为我需要修改那个实例,但老实说,我的大脑在这一点上已经被炒了,我只是不确定。这是我用于更新的代码:

UPDATECODE:

@Override
protected void onPostExecute(ContestEntriesModel entries) {
    Fragment currentFragment = MainActivity.mItems.get(MainActivity.CurrentEntryId);
    Log.d("fromUpdate",Long.toString(MainActivity.CurrentEntryId));
    MainActivity.Entries = entries;
    MainActivity.NUM_PAGES = MainActivity.Entries.entries.length;
    //MainActivity.mItems.clear();
    MainActivity.mPagerAdapter.notifyDataSetChanged();
    MainActivity.mPager.setCurrentItem(MainActivity.mPagerAdapter.getItemPosition(currentFragment));
}

所以使用它,它确实让我保持当前幻灯片(这是我需要的),但是如上所述,幻灯片没有正确重新定位,其他相邻幻灯片也没有“刷新”。再说一次,假设我在幻灯片#3上,如果我向左滑动一旦旧幻灯片#2仍在那里。如果我向左滑动两次,幻灯片#1将更新为“新”幻灯片(第一次加载时幻灯片#7)。如果我然后向右滑动一次,滑动#2仍然存在(应该是幻灯片#6)。如果我再次向右滑动,我需要维护的“原始”幻灯片现在用幻灯片#5替换(同样这个数字来自原始负载)。如果我一直向右滑动到“新”幻灯片#5,我再次看到原版。这就是它需要处于新数据负载的位置。

我真诚地为这段文字道歉,但我想如果我能提供尽可能多的代码,那就更有意义了。我已经阅读了几种不同的解决这个问题的方法,但是我无法专注于满足我需要的任何方法。

非常感谢任何建议/代码帮助。

2 个答案:

答案 0 :(得分:1)

你正在沿着正确的方向努力。你的getItemPosition永远不会工作,因为它总是实例化一个新的Fragment对象。如果对象视图仍处于正确位置,则需要维护自己的排序列表并接收返回POSITION_UNCHANGED。我将很快用代码示例更新我的答案。

答案 1 :(得分:0)

如果所有片段属于同一类,您可以使用重新排序它们的新数据来更新片段。

MyFragment:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    DataProvider.getInstance().addOnDataChangedListener(this);
    if (mRoot == null) {
        mRoot = inflater.inflate(R.layout.entitlement, container, false);
    }

    updateDetails();
    return mRoot;
}

@Override
public void onDestroyView() {
    DataProvider.getInstance().removeOnDataChangeListener(this);
    super.onDestroyView();
}

public void dataChanged() {
    updateDetails();
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    mContext = activity;
}

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}

DataProvider只是在已注册的侦听器上调用dataChanged()。重要提示:如果您在getActivity()中使用getResource() updateDetails()或类似内容,则可能会崩溃,因为尽管已调用OnAttach(),但活动可能仍为空。为了避免这种情况,我使用自己的Context-Object来执行这些操作,这些操作在OnAttach()中设置并在OnDetach()中删除。

我的适配器只负责页面标题,因为片段会自行刷新:

protected LinkedList<CharSequence> mPageTitles = new LinkedList<CharSequence>();

@Override
public CharSequence getPageTitle(int position) {
    try {
        return mPageTitles.get(position);
    } catch (Exception ex) {
        return null;
    }
}

protected final void setData(List data) {
    mPageTitles.clear();
    if (list != null) {
        for (int idx = 0; idx < list.size(); ++idx) {
            mPageTitles.add(DataProvider.getInstance().getPageTitle(list.get(idx)));
        }
    }
}

@Override
public void notifyDataSetChanged() {
    setData(DataProvider.getInstance().getData());
    super.notifyDataSetChanged();
}

更新DataProvider的时候,您还必须致电myAdapter.notifyDataSetChanged()

这可能不是最好的方法,但你可以尝试一下。 这个解决方案实际上会产生打嗝,因为它的目标是立即显示新数据。

如果你的currentSlide不应该更新,你应该在updateDetails()中标记并处理它:

private boolean sticky = false;

public void setSticky() {
    sticky = true;
}

public void forceUpdate() {
    sticky = false;
    updateDetails();
}

protected void updateDetails() {
    if (sticky) {
        return;
    }
    // Do your update
}