У меня есть Fragment, содержащий ListView. Я добавляю некоторые значения в базу данных с помощью диалогового окна и хочу обновить это ListView после закрытия диалогового окна. Кроме того, когда я изменяю вкладку, ListView не обновляется, но когда приложение включается и выключается, ListView обновляется.

Классы Fragment и Dialog следующие:

Фрагмент:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View rootView = inflater.inflate(R.layout.fragment_teams, container, false);

    listViewTeams = rootView.findViewById(R.id.listView_teams);

    TeamDatabase teamDatabase = new TeamDatabase(getContext());
    teamDatabase.open();
    arrayListTeam = teamDatabase.getAllTeams();
    teamDatabase.close();

    int resID = R.layout.team_list_item;
    teamListArrayAdapter = new TeamListArrayAdapter(getContext(), resID, arrayListTeam);
    listViewTeams.setAdapter(teamListArrayAdapter);

    return rootView;
}

Диалог onClick Метод:

@Override
public void onClick(View view) {
   int id = view.getId();

   switch (id){
       case R.id.button_alertDialogAddTeam_cancel:
           this.dismiss();
           break;
       case R.id.button_alertDialogAddTeam_ok:
           Team team = new Team();
           team.setName(editTextTeamName.getText().toString());
           team.setCode(editTextTeamCode.getText().toString());

           TeamDatabase teamDatabase = new TeamDatabase(getContext());
           teamDatabase.open();
           if(teamDatabase.addNewTeam(team)) {
               Toast.makeText(getContext(), team.getCode() + " - " +
                       team.getName() + " was added successfully", Toast.LENGTH_SHORT).show();
           }

           this.dismiss();
           break;
   }
}

Класс TeamDatabase:

    public static final String TABLE_NAME = "team";

private static final String KEY_ID = "id";
private static final String KEY_NAME = "name";
private static final String KEY_CODE = "code";
private static final String KEY_EMBLEM = "emblem";

private Context context;

public static final String CREATE_TABLE = "CREATE TABLE "+
        TABLE_NAME + " ("+
        KEY_ID  + " INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, "+
        KEY_NAME + " TEXT NOT NULL, "+
        KEY_CODE + " TEXT NOT NULL, " +
        KEY_EMBLEM + " TEXT);";


public TeamDatabase(Context context) {
    super(context);
    this.context = context;
}

public boolean addNewTeam(Team team){
    ContentValues contentValues = new ContentValues();

    contentValues.put(KEY_NAME, team.getName());
    contentValues.put(KEY_CODE, team.getCode());

    return db.insert(TABLE_NAME, null, contentValues) > 0;
}

public ArrayList<Team> getAllTeams()
{
    ArrayList<Team> teams = new ArrayList<Team>();

    Cursor cursor = db.query(TABLE_NAME, new String[]{KEY_ID,
            KEY_NAME,
            KEY_CODE}, null, null, null, null, null);

    while(cursor.moveToNext()) {

        Team team = new Team();

        team.setId(cursor.getInt(cursor.getColumnIndex(KEY_ID)));
        team.setName(cursor.getString(cursor.getColumnIndex(KEY_NAME)));
        team.setCode(cursor.getString(cursor.getColumnIndex(KEY_CODE)));

        teams.add(team);
    }
    return teams;
}

Класс DatabaseHelper:

private static final String DATABASE_NAME = "fixtureCreator.db";
private static final int DATABASE_VERSION = 1;

public SQLiteDatabase db;

public DatabaseHelper(Context context){
    super(context, DATABASE_NAME, null, DATABASE_VERSION);

}

@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
    sqLiteDatabase.execSQL(TeamDatabase.CREATE_TABLE);
}



@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersinon) {
    Log.w("TaskDBAdapter", "Upgrading from version " +
            oldVersion + " to " + newVersinon + ", which will destroy all old data");

    sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TeamDatabase.TABLE_NAME);
    onCreate(sqLiteDatabase);
}

public DatabaseHelper open() throws SQLException {
    try{
        db = this.getWritableDatabase();
    }catch (SQLException e){
        db = this.getReadableDatabase();
    }
    return this;
}

public void close(){
    db.close();
}
2
Wooz12345 24 Фев 2018 в 20:37

4 ответа

Лучший ответ

Проблема может быть решена путем извлечения данных после их сохранения каждый раз, когда вы нажимаете Dialog и затем вызываете notifyDataSetChanged() на вашем адаптере. Тем не менее, есть более элегантный способ достижения такого поведения, который решит обе ваши проблемы с помощью обозревателя контента.

В случае наличия обозревателя контента в вашей таблице базы данных вам нужно сначала объявить URI, который ссылается на вашу таблицу.

public static final Uri DB_TABLE_TEAM_URI = Uri
            .parse("sqlite://" + Constants.ApplicationPackage + "/" + DB_TABLE_TEAM);

// DB_TABLE_TEAM refers to the database table that you have for storing teams.

Теперь в вашей функции addNewTeam вам нужно сделать следующее.

public boolean addNewTeam(Team team) {
    // .. Save the team in database
    // Notify the observer about the change in the content
    context.getContentResolver().notifyChange(DBConstants.DB_TABLE_TEAM_URI, null);
}

Вам нужно вызывать функцию notifyChange() всякий раз, когда вы добавляете или обновляете запись в вашей командной таблице.

Теперь в вашем Activity или Fragment вам нужно зарегистрировать наблюдателя в своем cursor, чтобы данные команды были получены из таблицы вашей команды.

cursor = teamDatabase.getAllTeamsInCursor();
this.registerContentObserver(cursor, DBConstants.DB_TABLE_TEAM_URI);

Теперь заполните ваш ListView с помощью cursor, передав его адаптеру ListView. Список будет обновляться автоматически после добавления новых данных в таблицу вашей команды.

< Сильный > Обновление

Измените функцию addNewTeam в вашем классе TeamDatabase следующим образом.

public static final Uri DB_TABLE_TEAM_URI = Uri
        .parse("sqlite://" + Constants.ApplicationPackage + "/" + DB_TABLE_TEAM);

public boolean addNewTeam(Team team){
    ContentValues contentValues = new ContentValues();

    contentValues.put(KEY_NAME, team.getName());
    contentValues.put(KEY_CODE, team.getCode());

    boolean success = db.insert(TABLE_NAME, null, contentValues) > 0;
    context.getContentResolver().notifyChange(DBConstants.DB_TABLE_TEAM_URI, null);
    return success;
}

Для реализации функциональности с LoaderCallbacks вам сначала нужно implement интерфейс LoaderCallbacks в вашем Fragment. Таким образом, объявление вашего Fragment будет выглядеть так.

public class TeamsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
    // .... Code
}

Теперь вам нужно переопределить функции, которые идут вместе с интерфейсом LoaderCallbacks.

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    return new SQLiteCursorLoader(getActivity()) {
        @Override
        public Cursor loadInBackground() {
            // Initialize your database
            TeamDatabase teamDatabase = new TeamDatabase(getActivity());

            Cursor cursor = teamDatabase.getAllTeams();

            if (cursor != null) {
                // Register the content observer here
                this.registerContentObserver(cursor, DBConstants.DB_TABLE_TEAM_URI);
            }

            return cursor;
        }
    };
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // Set the cursor in your adapter. Handle null values in your setCursor function in your adapter. The cursor might return null when the table is empty.
    teamAdapter.setCursor(cursor);
    teamAdapter.notifyDataSetChanged();
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
}

Теперь в вашей функции onCreateView в Fragment вам нужно запустить загрузчик для извлечения данных из таблицы.

getLoaderManager().initLoader(0, null, this).forceLoad();

И уничтожить загрузчик и отменить регистрацию получателя в функции onDestroyView.

@Override
public void onDestroyView() {
    getLoaderManager().destroyLoader(0);
    super.onDestroyView();
}

Мне не хватало SQLiteCursorLoader класса, который нужно добавить сюда.

package com.wooz.observer.databases;

import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;

public abstract class SQLiteCursorLoader extends AsyncTaskLoader<Cursor> {
    private final ForceLoadContentObserver mObserver;

    private Uri mUri;
    private String[] mProjection;
    private String mSelection;
    private String[] mSelectionArgs;
    private String mSortOrder;

    private Cursor mCursor;

    /* Runs on a worker thread */
    @Override
    public abstract Cursor loadInBackground();

    /**
     * Registers an observer to get notifications from the content provider
     * when the cursor needs to be refreshed.
     */
    public void registerContentObserver(Cursor cursor, Uri observerUri) {
        cursor.registerContentObserver(mObserver);
        cursor.setNotificationUri(getContext().getContentResolver(), observerUri);
    }

    /* Runs on the UI thread */
    @Override
    public void deliverResult(Cursor cursor) {
        try {
            if (isReset()) {
                // An async query came in while the loader is stopped
                if (cursor != null) {
                    cursor.close();
                }
                return;
            }
            Cursor oldCursor = mCursor;
            mCursor = cursor;

            if (isStarted()) {
                super.deliverResult(cursor);
            }

            if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
                oldCursor.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Creates an empty unspecified CursorLoader.  You must follow this with
     * calls to {@link #setUri(Uri)}, {@link #setSelection(String)}, etc
     * to specify the query to perform.
     */
    public SQLiteCursorLoader(Context context) {
        super(context);
        mObserver = new ForceLoadContentObserver();
    }

    /**
     * Creates a fully-specified CursorLoader.  See
     * {@link ContentResolver#query(Uri, String[], String, String[], String)
     * ContentResolver.query()} for documentation on the meaning of the
     * parameters.  These will be passed as-is to that call.
     */
    public SQLiteCursorLoader(Context context, Uri uri, String[] projection, String selection,
                              String[] selectionArgs, String sortOrder) {
        super(context);
        mObserver = new ForceLoadContentObserver();
        mUri = uri;
        mProjection = projection;
        mSelection = selection;
        mSelectionArgs = selectionArgs;
        mSortOrder = sortOrder;
    }

    /**
     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
     * will be called on the UI thread. If a previous load has been completed and is still valid
     * the result may be passed to the callbacks immediately.
     * <p>
     * Must be called from the UI thread
     */
    @Override
    protected void onStartLoading() {
        if (mCursor != null) {
            deliverResult(mCursor);
        }
        if (takeContentChanged() || mCursor == null) {
            forceLoad();
        }
    }

    /**
     * Must be called from the UI thread
     */
    @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;
    }

    public Uri getUri() {
        return mUri;
    }

    public void setUri(Uri uri) {
        mUri = uri;
    }

    public String[] getProjection() {
        return mProjection;
    }

    public void setProjection(String[] projection) {
        mProjection = projection;
    }

    public String getSelection() {
        return mSelection;
    }

    public void setSelection(String selection) {
        mSelection = selection;
    }

    public String[] getSelectionArgs() {
        return mSelectionArgs;
    }

    public void setSelectionArgs(String[] selectionArgs) {
        mSelectionArgs = selectionArgs;
    }

    public String getSortOrder() {
        return mSortOrder;
    }

    public void setSortOrder(String sortOrder) {
        mSortOrder = sortOrder;
    }

    @Override
    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
        super.dump(prefix, fd, writer, args);
        writer.print(prefix);
        writer.print("mUri=");
        writer.println(mUri);
        writer.print(prefix);
        writer.print("mProjection=");
        writer.println(Arrays.toString(mProjection));
        writer.print(prefix);
        writer.print("mSelection=");
        writer.println(mSelection);
        writer.print(prefix);
        writer.print("mSelectionArgs=");
        writer.println(Arrays.toString(mSelectionArgs));
        writer.print(prefix);
        writer.print("mSortOrder=");
        writer.println(mSortOrder);
        writer.print(prefix);
        writer.print("mCursor=");
        writer.println(mCursor);
        //writer.print(prefix); writer.print("mContentChanged="); writer.println(mContentChanged);
    }

    private class CursorLoaderContentObserver extends ContentObserver {
        public CursorLoaderContentObserver() {
            super(new Handler());
        }

        @Override
        public boolean deliverSelfNotifications() {
            return true;
        }

        @Override
        public void onChange(boolean selfChange) {
            onContentChanged();
        }
    }
} 
1
Reaz Murshed 13 Мар 2019 в 05:12

Нажав на элемент в вашем первом занятии, начните второе занятие с startActivityForResult()

И затем во втором действии, после закрытия диалога, в onClick этого вызова кнопки,

intent.putExtra("new data", "item text");
setResult(RESULT_OK, intent);
finish();

Теперь вы возвращаетесь к своему первому действию, и здесь вы должны реализовать onActivityResult() обратный вызов.

Вы можете извлечь данные из дополнений этого намерения и установить соответствующий элемент в вашем массиве и вызвать notifyDataSetChanged ().

Это в идеале, как вы должны это делать.

2
Ouail Bellal 24 Фев 2018 в 17:54

Вы должны вызвать notifyDataSetChanged непосредственно перед закрытием диалогового окна.

< Сильный > teamListArrayAdapter.notifyDataSetChanged ( ) ;

Вы должны изменить свой код примерно так ниже

   @Override
public void onClick(View view) {
   int id = view.getId();

   switch (id){
       case R.id.button_alertDialogAddTeam_cancel:
           this.dismiss();
           break;
       case R.id.button_alertDialogAddTeam_ok:
           Team team = new Team();
           team.setName(editTextTeamName.getText().toString());
           team.setCode(editTextTeamCode.getText().toString());

           TeamDatabase teamDatabase = new TeamDatabase(getContext());
           teamDatabase.open();
           if(teamDatabase.addNewTeam(team)) {
               Toast.makeText(getContext(), team.getCode() + " - " +
                       team.getName() + " was added successfully", Toast.LENGTH_SHORT).show();
           }
           arrayListTeam = teamDatabase.getAllTeams();
           teamListArrayAdapter.notifyDataSetChanged();
           this.dismiss();
           break;
   }
}
1
lib4 24 Фев 2018 в 17:48

Добавьте этот код в onCreateView:

teamListArrayAdapter.notifyDataSetChanged();
0
armen 24 Фев 2018 в 18:56