我尝试过使用扩展的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;
}
如上所述,当第一个数据有效负载从服务器下来时,我用来自该模型的数据(包括来自互联网资源(ImageUrl)的图像)填充了ContestEntryModel[]数组中的每个项目的一个片段,并且工作完美。我可以刷到手指流血,世界上一切都是美好的。
我现在正试图从服务器发送另一个有效载荷,其中数组中的项目以不同的顺序(我最终也会添加新项目,但一次一步)。我在StackOverflow上搜索了高低来解决这个问题,并在昨天发布了一个问题,这让我接近了我需要的地方(感谢Reinier),但我仍然在努力让我所有的碎片重新排序,并让适配器完全刷新新订单。适配器似乎缓存了一些幻灯片(与我当前正在查看的幻灯片相邻的幻灯片),并且它们不会"刷新",直到我滑过一些图像然后返回。昨天的问题是,当新数据从我正在工作的服务器上下来时,当前查看的幻灯片不会"刷新"……排序的。假设我的数组中有7个项目,我在第一次加载时滑动到幻灯片#3(索引2)。然后,我从服务器推送新数据,这只是对数组进行反向排序。当前查看的幻灯片(幻灯片#3)应该移动到索引位置4,我应该只能向右滑动两次(索引位置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.
当一个新的数据负载从服务器下来时,我只是设置我的静态属性(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,我又能看到原来的幻灯片了。这就是它在新数据加载中需要的位置。
我真诚地为这一墙的文本道歉,但我想如果我能提供尽可能多的代码,这将更有意义。我读过几种不同的方法来解决这个问题,但我想不出哪一种适合我的需求。任何建议/代码帮助将非常感激。
你的思路是对的。你的getItemPosition永远不会工作,因为它总是实例化一个新的Fragment对象。您需要维护自己的排序列表,并在对象视图仍处于正确位置时接收返回的POSITION_UNCHANGED。我将很快用一个代码示例更新我的答案。
如果所有的Fragments都属于同一个类,你可以直接用新数据更新Fragments,而不是重新排序它们。
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()
已被调用。为了避免这种情况,我使用我自己的上下文对象来做这些事情,它在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
}