2012-08-01 31 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更新其視圖。