编辑:我没有为此对话框发布我的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;
}
}
}
答案 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线程上更新。