在另一个片段中的SQLite数据库更新时,更新片段中的ListView。

huangapple go评论81阅读模式
英文:

Update ListView in Fragment when SQLite database is updated in another Fragment

问题

I've got two Fragments

`AddActivityFragment` - 此片段负责将数据添加到我的 SQLite 数据库中


`ActivityListFragment` - 此片段负责在 `ListView` 中显示数据

问题是每当将数据添加到 SQLite 表中时表格在应用程序重新启动之前不会更新因此如果我尝试将一些数据添加到表中直到重新启动应用程序之前数据才会在 `ListView` 中更新

我在多个线程中遇到过关于类似问题的 `adapter.notifyDataSetChanged()`,但我无论如何都无法弄清楚如何将其实现到我的代码中

`AddActivityFragment.java`:

```java
public class AddActivityFragment extends Fragment implements View.OnClickListener {
    // ...(此处省略其他代码)
    @Override
    public void onClick(View v) {
        switch(v.getId()) {
            case R.id.addActivityButton:
                // ...(此处省略其他代码)

                // 将活动数据添加到 SQL 数据库
                mDatabaseHelper.addData(activityName, activityDate, activityLocationLat, activityLocationLong);
                mActivityListFragment.updateListView();  // 添加此行以通知 ActivityListFragment 更新 ListView

                break;
            default:
                break;
        }
    }
}

ActivityListFragment.java

public class ActivityListFragment extends Fragment {
    // ...(此处省略其他代码)
    public void updateListView() {
        listData.clear();
        for (int i = 0; i < mDatabaseHelper.getRowCount(); i++) {
            listData.add(mDatabaseHelper.getDataFromIndex(i));
        }
        adapter.notifyDataSetChanged();  // 更新适配器数据
    }
}

ViewPagerAdapter.javaDatabaseHelper.java 中的代码保持不变。


<details>
<summary>英文:</summary>

I&#39;ve got two Fragments

`AddActivityFragment` - This Fragment is responsible for adding data into my SQLite database.

and

`ActivityListFragment` - This Fragment is responsible for displaying the data in a `ListView`

The problem is that whenever data is added to the SQLite table, the table is not updated until the application is restarted. Because of this, if I try to add some data into the table, it is not updated in the `ListView` until I restart the application.

I&#39;ve come across `adapter.notifyDataSetChanged()` on multiple threads regarding similar problems but I can not for the life of me figure out how I&#39;m supposed to implement this into my own code.

`AddActivityFragment.java`:

public class AddActivityFragment extends Fragment implements View.OnClickListener {

private static final String TAG = &quot;AddActivityFragment&quot;;

private TextInputLayout activityNameTextField, activityDateTextField;
private TextInputEditText activityNameInput, activityDateInput;
public DatabaseHelper mDatabaseHelper;
private LocationRequest mLocationRequest;
private FusedLocationProviderClient mFusedLocationProviderClient;
private Location mLastLocation;
private ActivityListFragment mActivityListFragment;
private DatePicker mDatePicker;
Context mContext;

private double activityLocationLat;
private double activityLocationLong;

public AddActivityFragment() {
    // Required empty public constructor
}
@Override
public android.view.View onCreateView(LayoutInflater inflater, ViewGroup container,
                                      Bundle savedInstanceState) {
    View activityViewFragment = inflater.inflate(R.layout.fragment_add_activity, container, false);
    // Get context from fragment.
    this.mContext = activityViewFragment.getContext();

    mDatabaseHelper = new DatabaseHelper(mContext);

    // Find the layout components.
    activityNameTextField = activityViewFragment.findViewById(R.id.activityNameTextField);
    activityNameInput = activityViewFragment.findViewById(R.id.activityNameInput);
    mDatePicker = activityViewFragment.findViewById(R.id.datePicker);


    Button addActivityButton = activityViewFragment.findViewById(R.id.addActivityButton);

    // Set listener to addActivityButton in this view.
    addActivityButton.setOnClickListener(this);
    // Set hints for text fields.
    activityNameTextField.setHint(&quot;Activity Name&quot;);


    return activityViewFragment;
}

@Override
public void onClick(View v) {
    switch(v.getId()) {
        case R.id.addActivityButton:
            StringBuilder sb = new StringBuilder();
            int day = mDatePicker.getDayOfMonth();
            int month = mDatePicker.getMonth() + 1;
            int year = mDatePicker.getYear();
            sb.append(day);
            sb.append(&quot;-&quot;);
            sb.append(month);
            sb.append(&quot;-&quot;);
            sb.append(year);

            String activityName = activityNameInput.getText().toString();
            String activityDate = sb.toString();


            activityNameInput.setText(&quot;&quot;);

            //Add the activity data to the SQL database

            mDatabaseHelper.addData(activityName,activityDate,activityLocationLat,activityLocationLong);


            break;
        default:
            break;
    }

@Override
public void onResume(){
    super.onResume();
    Log.d(TAG,&quot;Log when activity is swiped to.&quot;);
}

@Override
public void onPause() {
    super.onPause();
    Log.d(TAG, &quot;List paused.&quot;);
}

}


`ActivityListFragment.java`:

public class ActivityListFragment extends Fragment {

//Class tag
private static final String TAG = &quot;ActivityListFragment&quot;;
private static final String ACTIVITY_LIST_FRAGMENT_TAG = &quot;ACTIVITY_LIST_FRAGMENT&quot;;

DatabaseHelper mDatabaseHelper;
Context mContext;
private ListView mListView;
public ArrayAdapter adapter;
private ArrayList&lt;ArrayList&gt; listData;

public ActivityListFragment() {
    // Required empty public constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View listViewFragment = inflater.inflate(R.layout.fragment_activity_list, container, false);
    this.mContext = listViewFragment.getContext();
    mListView = listViewFragment.findViewById(R.id.activity_list_view);

    mDatabaseHelper = new DatabaseHelper(mContext);
    listData = new ArrayList&lt;&gt;();

    for(int i = 0; i &lt; mDatabaseHelper.getRowCount(); i++) {
        listData.add(mDatabaseHelper.getDataFromIndex(i));
    }

    adapter = new ArrayAdapter&lt;&gt;(mContext, android.R.layout.simple_list_item_1, listData);
    mListView.setAdapter(adapter);
    handleOnItemClick();
    return listViewFragment;
}
@Override
public void onResume(){
    super.onResume();
    Log.d(TAG,&quot;Log when activity is swiped to.&quot;);
}

@Override
public void onPause() {
    super.onPause();
    Log.d(TAG, &quot;List paused.&quot;);
}


`ViewPagerAdapter.java`:

public class ViewPagerAdapter extends FragmentStateAdapter {

public ViewPagerAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
    super(fragmentManager, lifecycle);
}

@NonNull
@Override
public Fragment createFragment(int position) {

    switch (position) {
        case 0:
            return new MapViewFragment();
        case 1:
            return new AddActivityFragment();
        case 2:
            return new ActivityListFragment();
    }
    return null;
}

@Override
public int getItemCount() {
    return 3;
}

}


`DataBaseHelper.java`:

public class DatabaseHelper extends SQLiteOpenHelper {

private static final String TAG = &quot;DatabaseHelper&quot;;

//Table name, column names.
private static final String TABLE_NAME = &quot;activity_table&quot;;
private static final String COL_ID = &quot;ID&quot;;
private static final String COL_NAME = &quot;name&quot;;
private static final String COL_DATE = &quot;date&quot;;
private static final String COL_LOCATION_LAT = &quot;location_lat&quot;;
private static final String COL_LOCATION = &quot;location_long&quot;;

private Cursor mCursor;
ArrayList latLongList;
public DatabaseHelper(Context context) {
    super(context, TABLE_NAME, null, 1);
}

/**
 * Method: onCreate
 * @param db - The database that we are creating the table in.
 */
@Override
public void onCreate(SQLiteDatabase db) {
    String CREATE_ACTIVITES_TABLE = &quot;CREATE TABLE &quot; + TABLE_NAME + &quot;(&quot;
            + COL_ID + &quot; INTEGER PRIMARY KEY,&quot; + COL_NAME + &quot; VARCHAR,&quot;
            + COL_DATE + &quot; VARCHAR,&quot; + COL_LOCATION_LAT + &quot; DOUBLE,&quot; + COL_LOCATION + &quot; DOUBLE&quot; + &quot;);&quot;;
    db.execSQL(CREATE_ACTIVITES_TABLE);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL(&quot;DROP TABLE IF EXISTS &quot; + TABLE_NAME);
    onCreate(db);
}

/**
 * Method: addData
 * @param name - The &quot;name&quot;-value which is added to the table column COL_NAME.
 * @param date - The &quot;date&quot;-value which is added to the table column COL_DATE.
 * @param locationLat - The &quot;locationLat&quot;-value which is added to the table column COL_LOCATION_LAT.
 * @param locationLong - The &quot;locationLong&quot;-value which is added to the table column COL_LOCATION_LONG.
 * @return true or false, depending if the addition to the table was successful or not.
 */
public boolean addData(String name, String date, Double locationLat, Double locationLong) {
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues contentValues = new ContentValues();
    contentValues.put(COL_NAME, name);
    contentValues.put(COL_DATE, date);
    contentValues.put(COL_LOCATION_LAT, locationLat);
    contentValues.put(COL_LOCATION, locationLong);

    Log.d(TAG, &quot;addData: Adding &quot; + name + &quot; to &quot; + TABLE_NAME);
    Log.d(TAG, &quot;addData: Adding &quot; + date + &quot; to &quot; + TABLE_NAME);
    Log.d(TAG, &quot;addData: Adding &quot; + locationLat + &quot; to &quot; + TABLE_NAME);
    Log.d(TAG, &quot;addData: Adding &quot; + locationLong + &quot; to &quot; + TABLE_NAME);

    long result = db.insert(TABLE_NAME, null, contentValues);

    if(result == -1) {
        Log.d(TAG, &quot;Something went wrong when adding data to database.&quot;);
        return false;
    } else {
        Log.d(TAG, &quot;Data correctly added to database&quot;);
        return true;
    }
}

/**
 * Method: getData
 * @return mCursor - Returns entire table from database.
 */
public Cursor getData() {
    SQLiteDatabase db = this.getWritableDatabase();
    String query = &quot;SELECT * FROM &quot; + TABLE_NAME;
    mCursor = db.rawQuery(query, null);
    return mCursor;
}

/**
 * Method: getDataFromIndex
 * @param index - Row index in activity_table table.
 * @return list - ArrayList of data at row index.
 */
public ArrayList getDataFromIndex (int index) {
    ArrayList list = new ArrayList();

    if(mCursor == null) {
        getData();
    }
    mCursor.moveToPosition(index);
    list.add(mCursor.getString(1));
    list.add(mCursor.getString(2));
    list.add(mCursor.getDouble(3));
    list.add(mCursor.getDouble(4));
    return list;
}

/**
 * Method: getRowCount
 * @return mCursor.getCount - Total amount of rows in table.
 */
public int getRowCount() {
    if(mCursor == null) {
        getData();
    }
    return mCursor.getCount();
}

/** TODO: This might be useless as we will need to fetch the names associated to the activity anyway, to be decided.
 * Method: latLongArrayListFromIndex
 * @param index - Row index in activity_table table.
 * @return latLongList - ArrayList of data at row index.
 */
public ArrayList latLongArrayListFromIndex(int index) {
    latLongList = new ArrayList();
    if(mCursor == null) {
        getData();
    }
    mCursor.moveToPosition(index);
    latLongList.add(mCursor.getDouble(mCursor.getColumnIndex(&quot;location_lat&quot;)));
    latLongList.add(mCursor.getDouble(mCursor.getColumnIndex(&quot;location_long&quot;)));

    return latLongList;
}

/**
 * Method getItemId
 */
public Cursor getItemId(String name) {
    SQLiteDatabase db = this.getWritableDatabase();
    String query = &quot;SELECT &quot; + COL_ID + &quot; FROM &quot; + TABLE_NAME + &quot; WHERE &quot; + COL_NAME + &quot; = &#39;&quot; + name + &quot;&#39;&quot;;
    Cursor data = db.rawQuery(query, null);
    return data;
}

/**
 * Method: updateName
 * @param newName
 * @param id
 * @param oldName
 */
public void updateName(String newName, int id, String oldName) {
    SQLiteDatabase db = this.getWritableDatabase();
    String query = &quot;UPDATE &quot; + TABLE_NAME + &quot; SET &quot; + COL_NAME + &quot; = &#39;&quot; + newName + &quot;&#39; WHERE &quot; + COL_ID + &quot; = &#39;&quot; + id + &quot;&#39;&quot; + &quot; AND &quot; + COL_NAME + &quot; = &#39;&quot; + oldName + &quot;&#39;&quot;;
    Log.d(TAG, &quot;updateName: query: &quot; + query);
    Log.d(TAG, &quot;updateName: Setting new name of activity to: &quot; + newName);
    db.execSQL(query);
}

public void deleteActivity(int id, String name) {
    SQLiteDatabase db = this.getWritableDatabase();
    String query = &quot;DELETE FROM &quot; + TABLE_NAME + &quot; WHERE &quot; + COL_ID + &quot; = &#39;&quot; + id + &quot;&#39;&quot; + &quot; AND &quot; + COL_NAME + &quot; = &#39;&quot; + name + &quot;&#39;&quot;;
    Log.d(TAG, &quot;deleteActivity: query: &quot; + query);
    Log.d(TAG, &quot;deleteActivity: Deleting &quot; + name + &quot; from table&quot;);
    db.execSQL(query);
}

}

    


</details>


# 答案1
**得分**: 0

你可以通过使用内容观察器(content observer)来实现所需的行为。我在[这里有一个Github项目][1],可能会帮助你理解这是如何工作的。

第一步是定义一个唯一的内容URI(Uniform Resource Identifier)。URI可能看起来如下所示。

```java
public static final Uri DB_TABLE_USER_URI = Uri
    .parse("sqlite://" + com.package.your.app + "/" + "user_table");

然后在你的ActivityListFragment中使用LoaderManager.LoaderCallbacks<Cursor>来加载该片段中的用户,使用CursorLoader的回调函数(例如onCreateLoaderonLoadFinished等)。还要在onCreateLoader中为内容观察器进行注册。

this.registerContentObserver(cursor, DBConstants.DB_TABLE_USER_URI);

在你的DataBaseHelper类中添加/更新用户之后,你可能还希望通知观察器数据库中的数据已更改,以便它自动在RecyclerView中刷新数据。

context.getContentResolver().notifyChange(DBConstants.DB_TABLE_USER_URI, null);

我建议你分叉(fork)这个Github项目并运行代码,以查看它是如何工作的。

英文:

You can achieve the desired behaviour just by observing the SQLite data using content observer. I have a Github project here, that might help you to understand how this works.

The first step would be defining a content URI that is unique. The URI might look something as follows.

public static final Uri DB_TABLE_USER_URI = Uri
    .parse(&quot;sqlite://&quot; + com.package.your.app + &quot;/&quot; + &quot;user_table&quot;);

Then use the LoaderManager.LoaderCallbacks&lt;Cursor&gt; in your ActivityListFragment to load the users in that fragment using CursorLoader callback functions (i.e. onCreateLoader, onLoadFinished, etc.). Also register for content observer in your onCreateLoader.

this.registerContentObserver(cursor, DBConstants.DB_TABLE_USER_URI);

And after you add/update a user in your DataBaseHelper class, you might also want to notify the observer that the data is changed in your database, so that it refreshes the data automatically in the RecyclerView.

context.getContentResolver().notifyChange(DBConstants.DB_TABLE_USER_URI, null);

I would recommend forking the Github project and run the code to see how it works.

huangapple
  • 本文由 发表于 2020年8月17日 22:39:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/63453126.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定