Android 4.0.3 CursorAdapter不会在changeCursor上填充ListView

时间:2012-05-24 16:41:46

标签: android android-cursoradapter

编辑:我没有为此对话框发布我的XML。

<?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;
    }
  }
}

2 个答案:

答案 0 :(得分:2)

注意我以前没有添加我的XML。 这就是问题的所在。

andriod:animateLayoutChanges 

不符合我的目的。

一旦我从我的XML中删除它,它就像一个魅力。

答案 1 :(得分: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

更新: 该文档讨论了使用Loaders for Activities和Fragments,但没有提到使用Dialogs。我猜测如果getLoaderManager()存在你很好,但是如果你没有使用LoaderManager并且你自己手动运行Loader,那么我认为你需要确保在调用swapCursor()时你在UI线程上这样做的setAdapter()。有时确保这一点的最简单方法是致电

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

我自己遇到了一些情况,我在后台更新了ListView,并且在我旋转设备之前它没有反映为更新,因为UI没有在UI线程上更新。