2012-08-01 50 views
1

我卡住试图重新加载ListViewListFragment。刷新是通过我在FragmentDialog中创建的侦听器调用的。 reloadItems方法已正确调用,并且我已验证项目列表在变量中已正确更新。重新加载列表视图从内联类

这似乎是一个简单的事情要做,但我卡住了。 ListView从来没有实际更新,但我相信它与使用的Context有关。

onClick通过使用属性设置的ListView行的按钮调用。

解决方案 的问题是,我的选项卡适配器getItem(...)被unexpectantly再次呼吁创建时AlertDialog被而且由于我可怜的代码,它重新实例化的标签,并刷新从未发生过的可见片段。 (请参见最后的原始代码)

一旦发现此问题(请参阅接受的答案),我可以通过重新修改代码选项卡适配器来修复它以维护引用。这是我的工作选项卡适配器,适用于ViewPager以防万一对任何人有帮助。

public class TabsAdapter extends FragmentPagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener { 
    private final Context mContext; 
    private final ActionBar mActionBar; 
    private final ViewPager mViewPager; 
    private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); 
    private ArrayList<Fragment> mFragments = new ArrayList<Fragment>(); 

    static final class TabInfo { 
     private final Class<?> mClass; 
     private final Bundle mArgs; 

     TabInfo(Class<?> _class, Bundle _args) { 
      mClass = _class; 
      mArgs = _args; 
     } 

     public String getClassName() { 
      return mClass.getName(); 
     } 

     public Bundle getArgs() { 
      return mArgs; 
     } 
    } 

    public TabsAdapter(Activity activity, ViewPager pager) { 
     super(activity.getFragmentManager()); 
     mContext = activity; 
     mActionBar = activity.getActionBar(); 
     mViewPager = pager; 
     mViewPager.setAdapter(this); 
     mViewPager.setOnPageChangeListener(this); 
    } 

    public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) { 
     TabInfo info = new TabInfo(clss, args); 
     tab.setTag(info); 
     tab.setTabListener(this); 
     mTabs.add(info); 
     mActionBar.addTab(tab); 
     Fragment fragment = Fragment.instantiate(mContext, info.getClassName(), info.getArgs()); 
     mFragments.add(fragment); 

     notifyDataSetChanged(); 
    } 

    @Override 
    public int getCount() { 
     return mTabs.size(); 
    } 

    @Override 
    public Fragment getItem(int position) { 
     return mFragments.get(position); 
    } 

    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 
    } 

    public void onPageSelected(int position) { 
     mActionBar.setSelectedNavigationItem(position); 
    } 

    public void onPageScrollStateChanged(int state) { 
    } 

    public void onTabSelected(Tab tab, FragmentTransaction ft) { 
     Object tag = tab.getTag(); 
     for (int i = 0; i < mTabs.size(); i++) { 
      if (mTabs.get(i) == tag) { 
       mViewPager.setCurrentItem(i); 
      } 
     } 
    } 

    public void onTabUnselected(Tab tab, FragmentTransaction ft) { 
    } 

    public void onTabReselected(Tab tab, FragmentTransaction ft) { 
    } 
} 

的解决方案最终

活动

public class MyActivity extends Activity { 
    // unrelated code removed 

    public void onClick(View v) { 
     // onClick is called via a button on a list item 
     MyFragment sf = (MyFragment)mTabsAdapter.getItem(0); 
     sf.myClick(v, getFragmentManager()); 
    } 
} 

MyFragment

public class MyFragment extends ListFragment { 

    static final String TAG = "StatusFragment"; 
    private ArrayList<MyObject> mItems = null; 
    AlarmDataSource datasource; 
    StatusAdapter mAdapter; 

    public void reloadItems(Context context) { 
     Log.d(TAG, "reloadItems"); 

     datasource = new MyObjectDataSource(context); 
     datasource.open(); 

     mItems = new ArrayList<MyObject>(); 
     ArrayList<MyObject> active = (ArrayList<MyObject>)datasource.getAllEnabled(); 
     for (int i = 0; i < active.size(); i++) { 
      MyObject next = active.get(i); 
      mItems.add(next); 
     } 

     mAdapter = new StatusAdapter(
       context, 
       R.layout.status_list_item, 
       mItems); 

     // set a breakpoint and this is called when called from 
     // statusOptionDialogClose below. Here mAdapter DOES HAVE updated items 
     // inside. However getView within the adaper is NOT called 
     setListAdapter(mAdapter); 

     datasource.close(); 
    } 

    public void myClick(View v, FragmentManager manager) { 
     StatusOptionDialog sod = StatusOptionDialog.newInstance(v.getContext()); 
     sod.setStatusOptionDialogListener(new StatusOptionDialogListener() { 

      public void statusOptionDialogClose(Context context) { 
       // this is correctly called when dialog closed 
       // context is the same as sent in with newInstance above. 
       reloadItems(context); 
      } 
     }); 
     sod.show(manager, "StatusOptionDialog"); 
    } 
} 

任何人看到什么歪?我尝试了很多不同的方式,但没有任何工作。

注意我也试过mAdapter.notifyDataSetChanged()更新时,也不起作用。今天晚些时候我会尝试逐步浏览Android SDK代码,看看是否可以在那里精确定位数据,以防数据没有进入适配器或有些奇怪。

更新我追查了更多的问题。看起来在setListItem(mAdapter)mAdapter成功更新了项目后,不会调用适配器getView(...)

更新#2我已经发布reloadItems(...)函数,我已经蒸馏成最简单的代码有问题。 setListAdapter(...)使用已更新项目的更新适配器调用。但是getView(...)永远不会调用reloadItems(...)通过statusOptionDialogClose(...)

调用如果我杀死应用程序并重新启动它,ListView显示更新的数据。只是在再次调用reloadItem时不刷新。

适配器

public class StatusAdapter extends ArrayAdapter<MyObject> { 

    private Context mContext; 
    private int mRes; 
    private ArrayList<MyObject> mItems; 

    public StatusAdapter(Context context, int textViewResourceId, ArrayList<MyObject> items) { 
     super(context, textViewResourceId, items); 
     this.mItems = items; 
     this.mRes = textViewResourceId; 
     this.mContext = context; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     View v = convertView; 
     if (v == null) { 
      LayoutInflater vi = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      v = vi.inflate(mRes, null); 
     } 
     Alarm a = mItems.get(position); 
     if (a != null) { 
      // fills out list item 
     } 
     return v; 
    } 

    @Override 
    public int getCount() { 
     return mItems.size(); 
    } 

    public void setNewList(ArrayList<Alarm> items) { 
     this.mItems = items; 
    } 
} 

选项卡适配器这是用来使ViewPager和选项卡。

public class TabsAdapter extends FragmentPagerAdapter implements 
     ActionBar.TabListener, ViewPager.OnPageChangeListener { 
    private final Context mContext; 
    private final ActionBar mActionBar; 
    private final ViewPager mViewPager; 
    private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); 

    static final class TabInfo { 
     private final Class<?> clss; 
     private final Bundle args; 

     TabInfo(Class<?> _class, Bundle _args) { 
      clss = _class; 
      args = _args; 
     } 
    } 

    public TabsAdapter(Activity activity, ViewPager pager) { 
     super(activity.getFragmentManager()); 
     mContext = activity; 
     mActionBar = activity.getActionBar(); 
     mViewPager = pager; 
     mViewPager.setAdapter(this); 
     mViewPager.setOnPageChangeListener(this); 
    } 

    public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) { 
     TabInfo info = new TabInfo(clss, args); 
     tab.setTag(info); 
     tab.setTabListener(this); 
     mTabs.add(info); 
     mActionBar.addTab(tab); 
     notifyDataSetChanged(); 
    } 

    @Override 
    public int getCount() { 
     return mTabs.size(); 
    } 

    @Override 
    public Fragment getItem(int position) { 
     TabInfo info = mTabs.get(position); 
     return Fragment.instantiate(mContext, info.clss.getName(), info.args); 
    } 

    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 
    } 

    public void onPageSelected(int position) { 
     mActionBar.setSelectedNavigationItem(position); 
    } 

    public void onPageScrollStateChanged(int state) { 
    } 

    public void onTabSelected(Tab tab, FragmentTransaction ft) { 
     Object tag = tab.getTag(); 
     for (int i = 0; i < mTabs.size(); i++) { 
      if (mTabs.get(i) == tag) { 
       mViewPager.setCurrentItem(i); 
      } 
     } 
    } 

    public void onTabUnselected(Tab tab, FragmentTransaction ft) { 
    } 

    public void onTabReselected(Tab tab, FragmentTransaction ft) { 
    } 
} 

回答

1

理论上(正如其他人指出的)调用notifyDataSetChanged()应该是足够的,但看看调用ListView.invalidateViews()你在setNewList()方法可以迫使你的列表中显示新的数据更新后的数据(你getView()方法被调用)。

如果getView()在您强制它时仍然被调用,但它仍旧有旧数据,那么您有可能以某种方式获得了ListFragment的多余副本(并且具有更新的适配器数据的副本不可见)。也许你需要看看你的mTabsAdapter然后。

+0

谢谢你的回复。我发布了我使用的选项卡适配器,以防使用它。我不确定getItem(...)是否正确实现。我在那里放置了一个断点,它被称为超过预期。 – Kirk 2012-08-13 15:44:41

+0

你说得对。该选项卡适配器正在重新创建片段。我重写了它,并修复了它。哇,花了很多时间来解决这个问题。总是有些愚蠢的东西阻止我。 – Kirk 2012-08-13 16:30:07

+0

很高兴帮助!对于它的价值,我可以告诉你,我有这种怀疑,因为我在不同时间犯了同样的错误:) – Joe 2012-08-13 16:52:51

3

尽量不要每次都设置一个适配器,但更新您的适配器。

mAdapter.setNewList(newList); //custom method, just replace the old list with the new one 
mAdapter.notifyDataSetChanged(); 

这意味着你不需要使用setListAdapter(mAdapter);每一次,但只是第一次。

希望这会帮助你。

+0

谢谢你的回应。我也尝试过,它不会更新显示。我正在使用'setListAdapter'作为一种蛮力的方式来更新它。 我喜欢用'setNewList'方法的想法,并在适配器本身而不是外部使用它,稍后我会自由地尝试。任何其他想法可能会导致它? – Kirk 2012-08-01 15:09:05

+1

@Kirk对不起,我没有看到任何问题。您现在需要调试并找出问题所在。当你调用setNewList时,请确保你的新列表不同,并且设置了新的列表。然后在适配器的getView()上放置断点并检查项目是否正常。 – AMerle 2012-08-01 15:15:08

+0

再次感谢您。我需要像你提到的那样遍历getView,或者可能是SDK来查看它发生故障的位置。今天晚些时候我会告诉你它是如何发展的。 – Kirk 2012-08-01 15:17:25

0

就像CFlex说的那样,只有当您的Fragment第一次创建时,您应该只拨打setListAdapter()一次。

看起来你只是想清除你的清单。通常,您使用的Adapter将有一个clear()方法来为您处理这个问题,这可能会比每次完全重新分配数据对象时稍微好一些。如果您使用的是自定义Adapter,那么只需自己重写该方法即可。在对数据进行所有更改后,务必要致电notifyDataSetChanged()。这将导致ListView更新其视图。