Android:使用 ContentProvider 和 Loader 刷新 ListFragement

标签 android android-fragments android-contentprovider android-listfragment

我正在使用 SQLite 数据库和 ContentProvider 来填充 ListFragment。问题是在我添加项目后 ListFragment 没有刷新。 ListFragment 是空的。我必须关闭并重新打开应用程序才能在列表中显示添加的项目。

我尝试这样更新它:

     public class RoomListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {

            //adapter using SQLite and ContentProvider to fill ListFragment
            private SimpleCursorAdapter dataAdapter;

            //needed for create room dialog
            private EditText enter_room;
            private static View textEntryView;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            //display ActionBar items
            setHasOptionsMenu(true);

            // TODO: replace with a real list adapter.
            displayListView();
        }
    @Override
        public void onResume() {
          super.onResume();
          //Starts a new or restarts an existing Loader in this manager
          getLoaderManager().restartLoader(0, null, this);
         }

        @Override
        public void onDestroy() {
          super.onDestroy();
         }

         private void displayListView() {


          // The desired columns to be bound
          String[] columns = new String[] {
            Database.KEY_GROUPADDRESS,
            Database.KEY_NAME,
            Database.KEY_DPT
          };

          // the XML defined views which the data will be bound to
          int[] to = new int[] {
            R.id.groupaddress,
            R.id.name,
            R.id.dpt,
          };

          // create an adapter from the SimpleCursorAdapter
          dataAdapter = new SimpleCursorAdapter(
            getActivity(),
            R.layout.device_info,
            null,
            columns,
            to,
            0);

          //set SimpleCursorAdapter to ListFragmentAdapter
          setListAdapter(dataAdapter);

          //Ensures a loader is initialized and active.
          getLoaderManager().initLoader(0, null, this);   
         }

         // This is called when a new Loader needs to be created.
         @Override
         public Loader<Cursor> onCreateLoader(int id, Bundle args) {
             String[] projection = {
                  Database.KEY_ROWID,
                  Database.KEY_GROUPADDRESS,
                  Database.KEY_NAME,
                  Database.KEY_DPT};
             CursorLoader cursorLoader = new CursorLoader(getActivity(),
                     MyContentProvider.CONTENT_URI, projection, null,       null,      null);
             return cursorLoader;
         }

         @Override
         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.)
             dataAdapter.swapCursor(data);
             dataAdapter.notifyDataSetChanged();

                if (isResumed()) {
                    setListShown(true);
                } else {
                    setListShownNoAnimation(true);
             }
         }

         @Override
         public void onLoaderReset(Loader<Cursor> loader) {
             // This is called when the last Cursor provided to onLoadFinished()
             // above is about to be closed.  We need to make sure we are no
             // longer using it.
             dataAdapter.swapCursor(null);
         }

我用这段代码添加了一个项目:

//Handle OnClick events on ActionBar items
            @Override
            public boolean onOptionsItemSelected(MenuItem item) {
               // handle item selection
               switch (item.getItemId()) {
                  case R.id.menu_add:
                     //Toast.makeText(getActivity(), "Click", Toast.LENGTH_SHORT).show();

                     LayoutInflater factory = LayoutInflater.from(getActivity());

                     //textEntryView is an Layout XML file containing text field to display in alert dialog
                     textEntryView = factory.inflate(R.layout.dialog_add_room, null);       

                     //get the control from the layout      
                     enter_room = (EditText) textEntryView.findViewById(R.id.enter_room);

                     //create Dialog
                     final AlertDialog.Builder alert1 = new AlertDialog.Builder(getActivity());
                     //configure dialog
                     alert1.setTitle("Raum hinzufügen:").setView(textEntryView)
                     .setPositiveButton("Hinzufügen",
                            new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog,
                                int whichButton) {
                            String roomname = enter_room.getText().toString();

                            Log.d("Insert: ", "Inserting ..");

                            ContentValues values = new ContentValues();
                            //TODO Richtige Spalte für Raumname verwenden
                            values.put(Database.KEY_NAME, roomname);

                            getActivity().getContentResolver().insert(MyContentProvider.CONTENT_URI, values);

                            dataAdapter.notifyDataSetChanged();

                        }
                     }).setNegativeButton("Abbrechen",
                            new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog,
                                int whichButton) {
                            //cancel dialog
                        }
                     });
                     alert1.show();          
                     return true;
                  default:
                     return super.onOptionsItemSelected(item);
               }
            }

我的内容提供者:

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;

public class MyContentProvider extends ContentProvider{
     private MyDatabaseHelper dbHelper;

     private static final int ALL_COUNTRIES = 1;
     private static final int SINGLE_COUNTRY = 2;

     // authority is the symbolic name of your provider
     // To avoid conflicts with other providers, you should use
     // Internet domain ownership (in reverse) as the basis of your provider authority.
     private static final String AUTHORITY = "de.mokkapps.fixknxdemo.contentprovider";

     // create content URIs from the authority by appending path to database table
     public static final Uri CONTENT_URI =
      Uri.parse("content://" + AUTHORITY + "/countries");

     // a content URI pattern matches content URIs using wildcard characters:
     // *: Matches a string of any valid characters of any length.
        // #: Matches a string of numeric characters of any length.
     private static final UriMatcher uriMatcher;
     static {
      uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
      uriMatcher.addURI(AUTHORITY, "countries", ALL_COUNTRIES);
      uriMatcher.addURI(AUTHORITY, "countries/#", SINGLE_COUNTRY);
     }

     // system calls onCreate() when it starts up the provider.
     @Override
     public boolean onCreate() {
      // get access to the database helper
      dbHelper = new MyDatabaseHelper(getContext());
      return false;
     }

     //Return the MIME type corresponding to a content URI
     @Override
     public String getType(Uri uri) {

      switch (uriMatcher.match(uri)) {
      case ALL_COUNTRIES:
       return "vnd.android.cursor.dir/vnd.com.as400samplecode.contentprovider.countries";
      case SINGLE_COUNTRY:
       return "vnd.android.cursor.item/vnd.com.as400samplecode.contentprovider.countries";
      default:
       throw new IllegalArgumentException("Unsupported URI: " + uri);
      }
     }

     // The insert() method adds a new row to the appropriate table, using the values
     // in the ContentValues argument. If a column name is not in the ContentValues argument,
     // you may want to provide a default value for it either in your provider code or in
     // your database schema.
     @Override
     public Uri insert(Uri uri, ContentValues values) {

      SQLiteDatabase db = dbHelper.getWritableDatabase();
      switch (uriMatcher.match(uri)) {
      case ALL_COUNTRIES:
       //do nothing
       break;
      default:
       throw new IllegalArgumentException("Unsupported URI: " + uri);
      }
      long id = db.insert(Database.SQLITE_TABLE, null, values);
      getContext().getContentResolver().notifyChange(uri, null);
      return Uri.parse(CONTENT_URI + "/" + id);
     }

     // The query() method must return a Cursor object, or if it fails,
     // throw an Exception. If you are using an SQLite database as your data storage,
     // you can simply return the Cursor returned by one of the query() methods of the
     // SQLiteDatabase class. If the query does not match any rows, you should return a
     // Cursor instance whose getCount() method returns 0. You should return null only
     // if an internal error occurred during the query process.
     @Override
     public Cursor query(Uri uri, String[] projection, String selection,
       String[] selectionArgs, String sortOrder) {

      SQLiteDatabase db = dbHelper.getWritableDatabase();
      SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
      queryBuilder.setTables(Database.SQLITE_TABLE);

      switch (uriMatcher.match(uri)) {
      case ALL_COUNTRIES:
       //do nothing
       break;
      case SINGLE_COUNTRY:
       String id = uri.getPathSegments().get(1);
       queryBuilder.appendWhere(Database.KEY_ROWID + "=" + id);
       break;
      default:
       throw new IllegalArgumentException("Unsupported URI: " + uri);
      }

      Cursor cursor = queryBuilder.query(db, projection, selection,
        selectionArgs, null, null, sortOrder);
      return cursor;

     }

     // The delete() method deletes rows based on the selection or if an id is
     // provided then it deleted a single row. The methods returns the numbers
     // of records delete from the database. If you choose not to delete the data
     // physically then just update a flag here.
     @Override
     public int delete(Uri uri, String selection, String[] selectionArgs) {

      SQLiteDatabase db = dbHelper.getWritableDatabase();
      switch (uriMatcher.match(uri)) {
      case ALL_COUNTRIES:
       //do nothing
       break;
      case SINGLE_COUNTRY:
       String id = uri.getPathSegments().get(1);
       selection = Database.KEY_ROWID + "=" + id
       + (!TextUtils.isEmpty(selection) ?
         " AND (" + selection + ')' : "");
       break;
      default:
       throw new IllegalArgumentException("Unsupported URI: " + uri);
      }
      int deleteCount = db.delete(Database.SQLITE_TABLE, selection, selectionArgs);
      getContext().getContentResolver().notifyChange(uri, null);
      return deleteCount;
     }

     // The update method() is same as delete() which updates multiple rows
     // based on the selection or a single row if the row id is provided. The
     // update method returns the number of updated rows.
     @Override
     public int update(Uri uri, ContentValues values, String selection,
       String[] selectionArgs) {
      SQLiteDatabase db = dbHelper.getWritableDatabase();
      switch (uriMatcher.match(uri)) {
      case ALL_COUNTRIES:
       //do nothing
       break;
      case SINGLE_COUNTRY:
       String id = uri.getPathSegments().get(1);
       selection = Database.KEY_ROWID + "=" + id
       + (!TextUtils.isEmpty(selection) ?
         " AND (" + selection + ')' : "");
       break;
      default:
       throw new IllegalArgumentException("Unsupported URI: " + uri);
      }
      int updateCount = db.update(Database.SQLITE_TABLE, values, selection, selectionArgs);
      getContext().getContentResolver().notifyChange(uri, null);
      return updateCount;
     }
}

最佳答案

您应该从内容提供商的插入方法中调用 notifyDataSetChanged(); 并将 URI 作为参数提供。

在您当前调用 notifyDataSetChanged() 的时间点;方法插入可能实际上并没有发生,因为内容提供者对插入的调用将被异步处理。

一个例子看起来像这样

@Override
public Uri insert(Uri uri, ContentValues values) {
    SQLiteDatabase sqlDB = mDB.getWritableDatabase();
    int uriType = sURIMatcher.match(uri);
    long id;
    switch (uriType) {
        case TEAMS:
            id = sqlDB.replace(TeamModel.TEAM_TABLE_NAME, null, values);
            break;
        case CARS:
            id = sqlDB.replace(CarModel.CAR_TABLE_NAME, null, values);
            break;
        case TEAM_ERRORS:
            id = sqlDB.replace(TeamErrorModel.TEAMS_ERRORS_TABLE_NAME, null, values);
            String teamId = values.get(TeamErrorModel.COL_TEAM_ID).toString();
            String selection = TeamModel.COL_ID + " = ?";
            String[] selectionArgs = {teamId};
            setErrorFlagTeamModel(sqlDB, true, selection, selectionArgs);
            break;
        default:
            throw new IllegalArgumentException("Unknown URI: " + uri);
    }
    getContext().getContentResolver().notifyChange(uri, null, false);
    return Uri.parse(uri + "/" + id);
}

第一个参数是传递给插入方法的 URI,它将告诉所有监听该特定 uri 的适配器更新它们的数据。

最后一个参数 (false) 告诉同步适配器忽略此更改。我假设您没有使用同步适配器

ContentProvider 中的所有方法都应该以类似的方式调用 notifyChange 方法。

您可能会发现插入实际上失败了。因此请检查记录是否确实被插入。

更新 根据@zapi 的以下评论

And you need to add cursor.setNotificationUri(contentresolver, uri) inside the query method or the Cursor does not know for which uri notification it has to listen

在回答您的问题后,您已经发布了您的内容提供商,我现在可以看到,根据上面的引述,这实际上是您丢失的链接

关于Android:使用 ContentProvider 和 Loader 刷新 ListFragement,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15642975/

相关文章:

android - 应用过滤器复合体因与音频相关的内容而失败

Android:我可以在 TextView 中使用混合格式文本吗

Android : If markers are overlapping each other on the map, 然后触发最后一个隐藏的点击事件

android - 选项卡的 viewpager 的 findFragmentById 返回相同的 fragment ,而不管显示的 fragment 如何

java - 如何将大量可绘制图像加载到 fragment ListView中?

java - Android 调用带有 onClick 错误的 fragment 方法

Android 文件复制导致 java.io.IOException : File descriptor closed

android - Facebook Unity SDK 测试用户登录

android - ContentProvider 放置在android clean architecture中

android - 无法使用 ContentProvoider 更新联系人