0

我尝试使用扩展的 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[] 数组中的每个项目中填充一个片段,其中包含来自该模型的数据(包括来自互联网资源 (ImageUrl) 的图像),并且效果很好. 我可以滑动直到我的手指流血,世界上一切都很好。

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

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

主要活动:

    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.

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

更新代码:

@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,我会再次看到原始幻灯片。那是它需要在新数据加载的位置,在那个位置。

我真诚地为这堵文字墙道歉,但我想如果我能提供尽可能多的代码,这会更有意义。我已经阅读了几种不同的方法来解决这个问题,但我无法解决任何特别适合我需求的方法。

任何建议/代码帮助将不胜感激。

4

2 回答 2

1

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

于 2013-06-21T22:37:52.867 回答
0

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

我的片段:

@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()可能会崩溃,因为 Activity 尽管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
}
于 2013-06-18T17:13:09.640 回答