2012-05-24 28 views
5

編輯:我沒有發佈我的這個對話框的XML。Android 4.0.3 CursorAdapter不會在changeCursor上填充ListView

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/tag_layout" 
    android:orientation="vertical" 
    android:layout_height="wrap_content" 
    android:layout_width="@dimen/min_dialog_width" 
    android:padding="5dp" 
    android:animateLayoutChanges="true" 
    > 

<!-- Here is the view to show if the list is emtpy --> 
<TextView 
     android:id="@android:id/empty" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:padding="50dp" 
     android:textAppearance="?android:attr/textAppearanceMedium" 
     android:layout_centerInParent="true" 
     android:gravity="center" 
     android:text="@string/no_items" 
     android:visibility="invisible" 
     /> 

<ListView 
     android:id="@android:id/list" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:visibility="invisible" 
     /> 

<ProgressBar 
     android:id="@+id/tag_spin_progress_bar" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_centerInParent="true" 
     android:indeterminate="true" 
     /> 

</RelativeLayout> 

我使用的android.support.v4.CursorLoader和CursorAdapter的,我試圖讓它更新其光標。在Android 2.3.3中,它工作得很好。但是,當我在4.0.3設備上嘗試它時,ListView不會刷新,並且我的適配器中的newView方法從不會被調用。我知道光標中有數據,因爲我可以在我的2.3.3設備上看到它。

如果我旋轉我的設備,ListView顯示我想要的。我試圖使ListView失效,但是這並沒有解決問題。

如果我不重置ListView的適配器列表不會空白,但它仍然不刷新列表。

我正在做一個嵌入在DialogFragment中的擴展AlertDialog的所有內容。

這裏是整個類

import android.app.AlertDialog; 
import android.content.Context; 
import android.content.DialogInterface; 
import android.content.res.Resources; 
import android.database.Cursor; 
import android.os.Bundle; 

import android.support.v4.app.LoaderManager; 
import android.support.v4.content.CursorLoader; 
import android.support.v4.content.Loader; 

import android.text.Editable; 
import android.text.TextWatcher; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.inputmethod.InputMethodManager; 
import android.widget.*; 
import org.lds.ldssa.service.MLDatabase; 
import org.lds.ldssa.service.aws.Annotation; 

import java.io.Serializable; 
import java.util.Collection; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.Map; 

public class TagDialog extends AlertDialog implements LoaderManager.LoaderCallbacks<Cursor>,AdapterView.OnItemClickListener { 

private static final String TAG = "ldssa.tagdialog"; 
public static final int TAGLOADERID = 0; 

// View Items 
private EditText mEditText; 
private ListView mListView; 
private TextView mEmptyView; 
private ProgressBar mProgressBar; 
private ImageButton mNewTagButton; 
private ImageButton mSortTagButton; 
private TextView mTitle; 
private String mTagTitle; 
private String mNewTagTitle; 

private Annotation mAnnotation; 
private ContentFragment mContentFragment; 

private boolean isNewTagView; 
private static final String KEY_NEWTAGVIEW = "new_tag_view"; 

private static final String POSITION_KEY = "TAG_POSITION"; 
private static final String Y_KEY = "TAG_Y"; 
private static final String SORT_KEY = "TAG_SORT"; 
private static final String CHECKED_STATE_KEY = "TAG_CHECKED_STATE"; 
private static final int NOT_SET = -1; 
private int mPosition; 
private int mY; 

private TagSuggestionAdapter mSuggestionAdapter; 
private TagListAdapter mTagAdapter; 

private MLDatabase mlDatabase; 
private boolean mSortAlpha; 
private HashMap<Long, CheckedState> mCheckedState; 

protected TagDialog(Context context) { 
    super(context); 
} 

public void onCreate(Bundle savedInstanceState){ 
    Context context = getContext(); 
    Resources r = context.getResources(); 

    final LayoutInflater inflater = LayoutInflater.from(context); 
    View view = inflater.inflate(R.layout.dialog_tag, null); 

    // Main parts of the view   
    mEditText = (EditText) view.findViewById(R.id.tag_new_tag); 
    mListView = (ListView) view.findViewById(android.R.id.list); 
    mProgressBar = (ProgressBar) view.findViewById(R.id.tag_spin_progress_bar); 
    mEmptyView = (TextView) view.findViewById(android.R.id.empty); 
    mEmptyView.setVisibility(View.INVISIBLE); 

    // Titlebar 
    View titleBar = inflater.inflate(R.layout.dialog_tag_title, null); 
    mNewTagButton = (ImageButton) titleBar.findViewById(R.id.tag_new_icon); 
    mSortTagButton = (ImageButton) titleBar.findViewById(R.id.tag_sort_icon); 
    mTitle = (TextView) titleBar.findViewById(R.id.tag_title); 
    mTagTitle = r.getString(R.string.tag_dialog_title); 
    mNewTagTitle = r.getString(R.string.tag_new_dialog_title); 
    this.setCustomTitle(titleBar); 

    // Buttons 
    final String OK = r.getString(R.string.ok); 
    final String CANCEL = r.getString(R.string.cancel); 
    this.setButton(BUTTON_POSITIVE, OK, new OnClickListener() { 
     @Override 
     public void onClick(DialogInterface dialog, int which) { /*Never Used*/}}); 
    this.setButton(BUTTON_NEGATIVE, CANCEL, new OnClickListener() { 
     @Override 
     public void onClick(DialogInterface dialog, int which) { /*Never Used*/}}); 

    // Setup Button Listeners 
    setOnShowListener(new OnShowListener() { 
     @Override 
     public void onShow(DialogInterface dialog) { 
      Button ok = getButton(BUTTON_POSITIVE); 
      ok.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
        if(isNewTagView){ 
         hideIMM(); 
         addNewTag(); 
         mEditText.setText(""); 
         setupTagDialog(); 
        } else { 
         Collection<CheckedState> changes = mCheckedState.values(); 
         boolean success = true; 
         MLDatabase db = getDatabase(); 
         db.beginAnnotationTransaction(); 
         for(CheckedState change : changes){ 
          if(!change.checked()){ 
           //Detag 
           db.detagAnnotation(mAnnotation.getDbKey(), change.tagID()); 
          } else { 
           mAnnotation.saveHighlightsToDatabase(db); 
           if(mAnnotation.getDbKey().intValue() != MLDatabase.NOT_SET_INT & 
             change.tagID() != MLDatabase.NOT_SET_INT){ 
            success = db.tagAnnotation(mAnnotation.getDbKey(), change.tagID(), change.changed()); 
           } 
          } 
         } 
         if(success){ 
          db.setAnnotationTransactionSuccessful(); 
         } 
         db.endAnnotationTransaction(); 
         mCheckedState.clear(); 
         dismiss(); 
        } 
       } 
      }); 

      Button cancel = getButton(BUTTON_NEGATIVE); 
      cancel.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
        if(isNewTagView){ 
         hideIMM(); 
         setupTagDialog(); 
         mEditText.setText(""); 
        } else { 
         mCheckedState.clear(); 
         dismiss(); 
        } 
       } 
      }); 
     } 
    }); 

    mNewTagButton.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      setupNewTagDialog(); 
     } 
    }); 
    mSortTagButton.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      mSortAlpha = !mSortAlpha; 
      restartLoader(); 
     } 
    }); 

    mListView.setOnItemClickListener(TagDialog.this); 

    mEditText.addTextChangedListener(new TextWatcher() { 
     @Override 
     public void beforeTextChanged(CharSequence s, int start, int count, int after) {} 

     @Override 
     public void onTextChanged(CharSequence s, int start, int before, int count) {} 

     @Override 
     public void afterTextChanged(Editable s) { 
      LoaderManager lm = getLoaderManager(); 
      if(lm != null){ 
       Loader l = lm.getLoader(TAGLOADERID); 
       if(l != null){ 
        l.forceLoad(); 
       } else { 
        restartLoader(); 
       } 
      } else { 
       restartLoader(); 
      } 
     } 
    }); 

    //Handle Rotations 
    if(savedInstanceState == null){ 
     //New 
     mPosition = NOT_SET; 
     mY = NOT_SET; 
     mSortAlpha = false; 
     mCheckedState = getCheckedState(mAnnotation.getDbKey()); 
     isNewTagView = false; 
    } else { 
     //rotated 
     isNewTagView = savedInstanceState.getBoolean(KEY_NEWTAGVIEW, false); 
     mPosition = savedInstanceState.getInt(POSITION_KEY, NOT_SET); 
     mY = savedInstanceState.getInt(Y_KEY, NOT_SET); 
     mSortAlpha = savedInstanceState.getBoolean(SORT_KEY, false); 
     restoreCheckedState(savedInstanceState); 
    } 

    mTagAdapter = new TagListAdapter(context, null, mCheckedState); 
    mSuggestionAdapter = new TagSuggestionAdapter(context, null, 0); 

    LoaderManager lm = getLoaderManager(); 
    if(lm != null){ 
     lm.initLoader(TAGLOADERID, null, this); 
    } 

    this.setView(view); 
    super.onCreate(savedInstanceState); 
} 

private void addNewTag() { 
    String tag = mEditText.getText().toString().trim(); 
    if(!tag.equals("")){ 
     getDatabase(); 
     Integer langID = mAnnotation.getLanguageId(); 
     try{ 
      long tagID = mlDatabase.insertTag(langID, tag); 
      if(mAnnotation.getDbKey().intValue() != MLDatabase.NOT_SET_INT && 
        tagID != MLDatabase.NOT_SET_INT){ 
       mCheckedState.put(tagID, new CheckedState(tagID, true, true)); 
      } 
     } catch (Exception e) { 
      Log.d(TAG, "Problem saving new tag: " + tag + " : " + e.getMessage()); 
      e.printStackTrace(); 
     } 
    } 
} 

public void onStart(){ 
    if(isNewTagView){ 
     setupNewTagDialog(); 
    } else { 
     setupTagDialog(); 
    } 
    restartLoader(); 
} 

@Override 
public Bundle onSaveInstanceState(){ 
    Bundle bundle = super.onSaveInstanceState(); 

    //Save What dialog we are in. 
    bundle.putBoolean(KEY_NEWTAGVIEW, isNewTagView); 
    bundle.putBoolean(SORT_KEY, mSortAlpha); 

    //Save position 
    bundle.putInt(POSITION_KEY, mListView.getFirstVisiblePosition()); 
    final View v = mListView.getChildAt(0); 
    bundle.putInt(Y_KEY, (v == null) ? 0 : v.getTop()); 

    //Save Checked State 
    Iterator it = mCheckedState.entrySet().iterator(); 
    int i = 0; 
    while(it.hasNext()){ 
     Map.Entry pair = (Map.Entry)it.next(); 
     bundle.putSerializable(CHECKED_STATE_KEY + i, (CheckedState)pair.getValue()); 
     i++; 
    } 
    bundle.putInt(CHECKED_STATE_KEY, i); 

    return bundle; 
} 

private void restoreCheckedState(Bundle bundle){ 
    int count = bundle.getInt(CHECKED_STATE_KEY); 
    mCheckedState = new HashMap<Long, CheckedState>(); 
    boolean success = true; 
    for(int i = 0; i < count; i++){ 
     CheckedState cs = (CheckedState)bundle.getSerializable(CHECKED_STATE_KEY+i); 
     if(cs == null){ 
      success = false; 
      break; 
     } 
     mCheckedState.put(cs.tagID(), cs); 
    } 
    if(!success){ 
     mCheckedState = getCheckedState(mAnnotation.getDbKey()); 
    } 
} 

@Override 
public void onBackPressed(){ 
    if(isNewTagView){ 
     hideIMM(); 
     setupTagDialog(); 
    } else { 
     this.dismiss(); 
    } 
} 

private void setupTagDialog() { 
    isNewTagView = false; 
    mTitle.setText(mTagTitle); 
    mNewTagButton.setVisibility(View.VISIBLE); 
    mSortTagButton.setVisibility(View.VISIBLE); 
    mEmptyView.setVisibility(View.INVISIBLE); 
    mEditText.setVisibility(View.GONE); 
    mListView.setVisibility(View.GONE); 
    mProgressBar.setVisibility(View.VISIBLE); 
    mListView.setAdapter(mTagAdapter); 
    restartLoader(); 
} 

private void setupNewTagDialog() { 
    isNewTagView = true; 
    mTitle.setText(mNewTagTitle); 
    mNewTagButton.setVisibility(View.INVISIBLE); 
    mSortTagButton.setVisibility(View.INVISIBLE); 
    mEmptyView.setVisibility(View.INVISIBLE); 
    mEditText.setVisibility(View.VISIBLE); 
    mListView.setVisibility(View.GONE); 
    mProgressBar.setVisibility(View.VISIBLE); 
    mListView.setAdapter(mSuggestionAdapter); 
    restartLoader(); 
} 

public void setAnnotation(Annotation a) { 
    mAnnotation = a; 
} 

public void setContentViewInterface(ContentFragment contentFragment) { 
    mContentFragment = contentFragment; 
} 

private MLDatabase getDatabase() { 
    if(mlDatabase == null){ 
     GospelLibraryApplication app = (GospelLibraryApplication) getContext().getApplicationContext(); 
     mlDatabase = app.getMlDatabase(); 
    } 
    return mlDatabase; 
} 

public String getFilter() { 
    return mEditText.getText().toString().trim(); 
} 

public Integer getAnnotationID(){ 
    if(mAnnotation != null){ 
     return mAnnotation.getDbKey(); 
    } 
    return MLDatabase.NOT_SET_INT; 
} 

private LoaderManager getLoaderManager(){ 
    if(mContentFragment == null){ 
     Log.d(TAG, "ContentFragment is NULL!"); 
     return null; 
    } 
    return mContentFragment.getContentActivity().getSupportLoaderManager(); 
} 

private void restartLoader(){ 
    LoaderManager lm = getLoaderManager(); 
    if(lm != null){ 
     lm.restartLoader(TAGLOADERID, null, this); 
    } 
} 

private void hideIMM(){ 
    InputMethodManager imm = (InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
    imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); 
} 

private HashMap<Long, CheckedState> getCheckedState(Integer annotationID) { 
    HashMap<Long, CheckedState> checkedState = new HashMap<Long, CheckedState>(); 
    MLDatabase db = getDatabase(); 
    Cursor cursor = db.queryAllTagsWithAnnotation(annotationID); 
    if(cursor != null){ 
     for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()){ 
      Long tagID = cursor.getLong(cursor.getColumnIndex(MLDatabase.CL_ID)); 
      boolean isChecked = !cursor.isNull(cursor.getColumnIndex(MLDatabase.CL_ANNOTATION)); 
      checkedState.put(tagID, new CheckedState(tagID, isChecked, false)); 
     } 
    } 
    return checkedState; 
} 

@Override 
public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
    TagCursorLoader loader = new TagCursorLoader(getContext(), this); 
    return loader; 
} 

@Override 
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor data) { 
    if(isNewTagView) { 
     mSuggestionAdapter.changeCursor(data); 
     if(mListView.getAdapter() == null){ 
      mListView.setAdapter(mSuggestionAdapter); 
     } 
    } else { 
     mTagAdapter.changeCursor(data); 
     if(mListView.getAdapter() == null){ 
      mListView.setAdapter(mTagAdapter); 
     } 
    } 
    if(mPosition != NOT_SET && mY != NOT_SET){ 
     mListView.setSelectionFromTop(mPosition, mY); 
     mPosition = mY = NOT_SET; 
    } 

    if (mListView.getAdapter() != null) { 
     if (mListView.getAdapter().getCount() > 0) { 
      mEmptyView.setVisibility(View.INVISIBLE); 
     } 
     else { 
      mEmptyView.setVisibility(View.VISIBLE); 
     } 
    } 
    else { 
     mEmptyView.setVisibility(View.VISIBLE); 
    } 
    mProgressBar.setVisibility(View.GONE); 
    mListView.setVisibility(View.VISIBLE); 
    mListView.invalidate(); 
} 

@Override 
public void onLoaderReset(Loader<Cursor> cursorLoader) { 
    if(mSuggestionAdapter != null) { 
     mSuggestionAdapter.changeCursor(null); 
    } 
} 

@Override 
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 
    if(isNewTagView){ 
     TextView tv = (TextView)view; 
     mEditText.setText(tv.getText()); 
     Button ok = getButton(BUTTON_POSITIVE); 
     if(ok != null){ 
      ok.performClick(); 
     } 
    } else { 
     CheckedTextView ctv = (CheckedTextView)view; 
     boolean checked = !ctv.isChecked(); 
     ctv.setChecked(checked); 
     mCheckedState.put(id, new CheckedState(id, checked, true)); 
    } 

} 

public static class TagCursorLoader extends CursorLoader { 
    private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver(); 

    private TagDialog dialog; 
    private MLDatabase mlDatabase; 
    private Cursor mCursor; 
    private String mFilter; 
    private Integer mAnnotationID; 

    // Runs on worker thread 
    @Override 
    public Cursor loadInBackground(){ 
     Cursor cursor = null; 
     if(dialog.isNewTagView){ 
      mFilter = dialog.getFilter(); 
      cursor = mlDatabase.getTagSuggestions(mFilter); 
     } else { 
      cursor = mlDatabase.queryTags(dialog.mSortAlpha); 
     } 

     if(cursor != null){ 
      cursor.registerContentObserver(mObserver); 
     } 

     return cursor; 

    } 

    //Runs on UI thread 
    @Override 
    public void deliverResult(Cursor cursor){ 
     //Handle if canceled in the middle. 
     if(isReset()){ 
      if(cursor != null){ 
       cursor.close(); 
      } 
      return; 
     } 

     Cursor oldCursor = mCursor; 
     mCursor = cursor; 
     if(isStarted()) { 
      super.deliverResult(cursor); 
     } 

     if(oldCursor != null && !oldCursor.equals(cursor) && !oldCursor.isClosed()) { 
      oldCursor.close(); 
     } 
    } 

    public TagCursorLoader(Context context, TagDialog dialog) { 
     super(context); 
     this.dialog = dialog; 
     mlDatabase = dialog.getDatabase(); 
    } 

    @Override 
    public void onStartLoading(){ 
     if(mCursor == null) { 
      forceLoad(); 
     } else { 
      if(dialog.isNewTagView && mFilter.equals(dialog.getFilter())) { 
       deliverResult(mCursor); 
      } else { 
       forceLoad(); 
      } 
     } 
    } 

    @Override 
    protected void onStopLoading() { 
     // Attempt to cancel the current load task if possible. 
     cancelLoad(); 
    } 

    @Override 
    public void onCanceled(Cursor cursor) { 
     if (cursor != null && !cursor.isClosed()) { 
      cursor.close(); 
     } 
    } 

    @Override 
    protected void onReset() { 
     super.onReset(); 

     // Ensure the loader is stopped 
     onStopLoading(); 

     if (mCursor != null && !mCursor.isClosed()) { 
      mCursor.close(); 
     } 
     mCursor = null; 
    } 

} 

/** 
* Class is used to store the temporary checked state of the tags. 
*/ 
public class CheckedState implements Serializable { 
    private static final long serialVersionUID = 1263560458217339487L; 

    /** 
    * @serialField 
    */ 
    private long tagID; 
    /** 
    * @serialField 
    */ 
    private boolean checked; 
    /** 
    * @serialField 
    */ 
    private boolean changed; 

    /** 
    * Constructor for CheckedState. 
    * @param tagID The tag ID 
    * @param checked The Current Checked State 
    * @param changed Ture if changed in the dialog. False if pulling from database. 
    */ 
    public CheckedState(long tagID, boolean checked, boolean changed){ 
     this.tagID = tagID; 
     this.checked = checked; 
     this.changed = changed; 
    } 

    public long tagID(){ 
     return tagID; 
    } 

    public boolean checked() { 
     return checked; 
    } 

    public boolean changed() { 
     return changed; 
    } 
    } 
} 
+0

CommonsWare的[此評論](http://stackoverflow.com/a/9063814/1348379)幫助我解決了類似問題。我認爲你不應該像你在做的那樣換掉適配器。通過'''swapCursor(光標)交換遊標''' - 是的,列表適配器 - 沒有。使用另一個加載器爲這些信息創建一個單獨的片段,並在onActivityCreated期間初始化適配器(通過''''setAdapter(...)''')和loader(''''initLoader'''')。 – OceanLife

+0

@OceanLife我沒有使用任何活動,我在擴展的AlertDialog中這樣做。 – Ge3ng

+0

你試過initLoader(...)。forceLoad()?我也有一些與ICS有關的問題,並且設法解決它們。 –

回答

2

注意我以前沒有添加過我的XML。 這就是問題所在。

andriod:animateLayoutChanges 

不能與我正在嘗試做的工作。

一旦我從我的XML中刪除它就像一個魅力。

1

在我所看到的大多數例子中,你曾經創建適配器實例,並將其設置爲當您查看創建ListView,然後調用getLoaderManager().initLoader()

// Prepare the loader. Either re-connect with an existing one, 
// or start a new one. 
getLoaderManager().initLoader(0, null, this); 

然後在onLoadFinished()方法調用swapCursor()自動刷新ListView

// This is the Adapter being used to display the list's data. 
SimpleCursorAdapter mAdapter; 
... 

public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 
    // Swap the new cursor in. (The framework will take care of closing the 
    // old cursor once we return.) 
    mAdapter.swapCursor(data); 
} 

上面的代碼從Loaders文檔 http://developer.android.com/guide/topics/fundamentals/loaders.html

UPDATE被複制: 有關使用裝載機的活動和碎片文件的談判,但沒有提到使用對話框。我在猜測,如果getLoaderManager()存在,那麼你很好,但是如果你不使用LoaderManager並且你自己手動運行Loader,那麼我認爲你需要確保當你調用swapCursor()或setAdapter(),你正在UI線程上做這件事。有時爲了確保這一點最簡單的方法,就是調用

getListView().post(new Runnable() { 
    public void run() { 
     // so the setAdapter() or swapCursor() here 
    } 
}); 

我遇到了,我已經在後臺更新一個ListView的情況下自己,它不反映爲被更新,直到我旋轉設備,因爲用戶界面未在UI線程上更新。

+0

我正在使用一個LoaderManager,所以我有onLoadFinished,那是我改變光標的地方。它被調用,但UI不更新。這很奇怪,因爲當我切換回第一個適配器時,UI會更新並按照它的設想工作。 – Ge3ng

+0

雖然你提到你正在'AlertDialog'中使用它。 'AlertDialog'不會擴展'Fragment'或'Activity',那麼如何獲得對'LoaderManager'的引用? – stuckless

+0

我將AlertDialog嵌入到DialogFragment中,這就是我訪問LoaderManager的方式。 – Ge3ng