使用 FragmentStatePagerAdapter 时无法删除页面 - 使用整数主键时行 ID 的通常行为是什么?

Unable to delete pages when using FragmentStatePagerAdapter - What is the usual behavior of row ID's when integer primary key is used?

请随时跳到问题,因为您可能不需要这种背景知识。

我是 android 和 sqlite 的新手,我正在设计一个应用程序,它有一个内容提供程序来访问具有多个 tables 的 sqlite 数据库。有几个活动使用不同的适配器来显示从数据库到 UI 的信息(即游标适配器、片段状态页面适配器和数组适配器)。我在所有不使用游标适配器的活动中都遇到了删除功能的问题。当我尝试更新或删除 table 中的一行时,它会删除错误的行或根本不会删除任何内容。我认为这是适配器的问题,我试图找出将正确信息发送给内容提供商的那一行。

相同的 java 代码与游标适配器完美配合,行删除正常,其他 CRUD 操作正常。插入和查询功能对所有 tables.The 提供程序代码都正常工作,每个 table 都使用一个 switch 语句,但它对于每个 Uri 情况基本相同。所有 tables 都将 _id 作为整数主键,未设置为自动递增。由于我不完全理解行 ID 的工作原理,我的 java 代码没有反映它,我一直遇到这些问题。虽然我已经阅读了很多关于内容提供者、适配器、sqlite 数据库等的文档,但某些关键细节对我来说并不清楚。

我的问题是当行 ID 设置为 _id 列作为主键时,行 ID 如何在数据库中分配编号?当数据库更改时,这些编号会发生什么变化?

例如,假设我有一个空数据库。最初插入第一行后,Uri 将 return 0 行的路径段,适配器位置将为 0...数据库的行 ID 是什么(0 或 1)?

然后对于我插入的每一行,我知道行号会增加一个整数。假设我插入 4 行 - 0、1、2、3。现在,当我准备好删除时——Uri 上的最后一个路径段是否应该比行号小一个整数(即我是否发送最后一个路径段为 2 的 Uri 以删除第 3 行)?最后,删除后,行 ID 会自动重新分配,以便第 4 行现在变成第 3 行吗?是否需要编写一些代码才能在数据库中实现这一点?主键未设置为自动递增。

我有不同的适配器和活动,一旦数据显示在 UI 中,我就无法访问实际的数据库行 ID,所以我使用适配器位置作为代理项。这就是我在更新和删除时遇到问题的原因。 如果您阅读了整个问题并花时间回答,非常感谢您,这将对我有很大帮助。

我有一个 activity 选项卡式并使用由数据库填充的 FragmentStatePagerAdapter。这是我为跟踪行而调整的适配器:

  **EDITED:**

   public class TankSectionsPagerAdapter extends FragmentStatePagerAdapter {

private ArrayList<Fragment> tankFragments = new ArrayList<>();
private ArrayList<String> tankTitles = new ArrayList<>();

//I added this ArrayList below to store the tankIDs to match the Fragments//

**public ArrayList<Integer> tankIDs = new ArrayList<>();**



public TankSectionsPagerAdapter(FragmentManager fm) {
    super(fm);
}


@Override
public Fragment getItem(int position) {

    return tankFragments.get(position);
}


@Override
public int getCount() {
    return tankFragments.size();
}


@Override
public String getPageTitle(int position) {

    return tankTitles.get(position);
}


public void addPage(Fragment fragment, String tankName) {

    tankFragments.add(fragment);
    tankTitles.add(tankName);

   // I added this below so the ID position would match each fragment position //

    **tankIDs.add(tankId);**

    notifyDataSetChanged();
}
// Finally I added this method below to the adapter//

   **   public ArrayList<Integer> getPageId(){
  return tankIDs;
     }**

这里是 activity 调用方法的地方,它从游标中提取数据以传递给适配器。有一个循环,其中每一行在 ViewPager 中创建一个页面(选项卡):

 public class MyClass extends Tank implements TabLayout.OnTabSelectedListener, LoaderManager.LoaderCallbacks<Cursor> {

public TankSectionsPagerAdapter tankSectionsPagerAdapter;

TabLayout tabLayout;

private ViewPager mViewPager;
...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_my_class);


    mViewPager = (ViewPager) findViewById(R.id.container);

    addPages(mViewPager);

    tabLayout = (TabLayout) findViewById(R.id.tabLayout);
    tabLayout.setupWithViewPager(mViewPager);
    tabLayout.addOnTabSelectedListener(this);

}


    public void addPages(ViewPager mViewPager) {

                              tankSectionsPagerAdapter = new TankSectionsPagerAdapter(getSupportFragmentManager());

    mViewPager.setAdapter(tankSectionsPagerAdapter)

        try {
    ...
                                Cursor cursor = getContentResolver().query(MyProvider.CONTENT_URI_TABLE_TANK_SETUP, MyDatabaseHelper.ALL_TABLE_TANK_SETUP_COLUMNS, tankDataFilter, null, null);

                            if (cursor.moveToFirst()) {
                                do {

    tName =      cursor.getString(cursor.getColumnIndex(MyDatabaseHelper.TANK_NAME));                ...

    // all the variables are stored in the bundle passed to the   fragment/
                                                                                                                                            ...                  

                                                               **tankSectionsPagerAdapter.addPage(MainTankFragment.newInstance(tankBundle),tName, int tankID);**

  tankDataFilter = tankDataFilter + (-1);
                                }
                                while (cursor.moveToNext());
                                cursor.close();
                            } else {
                                Toast...
                            }
        } catch (Exception e) {

        Toast..
    }
       }

   ...   

  // Get Row ID from cursor(tankID), parameter in addPage() above//



//Get ID's from Adapter //

    ** ArrayList <Integer>  pageID= tankSectionsPagerAdapter.getPageId();** 

这是 Activity 带有 Spinner 选择 rows/fragments 编辑或删除。

public class EditTank extends Tank  implements LoaderManager.LoaderCallbacks<Cursor>
     ...


 // Get the ArrayList//

   ArrayList<Integer> IDtags =getIDIntent.getIntegerArrayListExtra("tank_edit_key");

    loadEditTankSpinnerData();


////***Here is the Spinner. Use row ID from the ArrayList****** 
           Note: Don't use the id of the spinner


       editTankSpinner.setOnItemSelectedListener(new    AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view int position, long id) {


     *** tankID = IDtags.get(position); ***           

        }




   private void loadEditTankSpinnerData() {

    List<String> tankNames = new ArrayList<String>();

        Cursor cursor = getContentResolver().query(MyProvider.CONTENT_URI_TABLE_TANK_SETUP, MyDatabaseHelper.TANK_NAMES, null, null,null);
    try{
        if (cursor.moveToFirst()) {
            do {
                tankNames.add(cursor.getString(1));
            } while (cursor.moveToNext());
            cursor.close();
        } else {
            deleteTankBtn.setEnabled(false);
            editTankBtn.setEnabled(false);
            Toast...
        }

    } catch (Exception e) {
        Toast...
    }


 ...
}

以上代码在 CursorAdapter 上运行良好,但在 fragmentStatePagerAdapter 上运行不佳(***在编辑之前它不起作用,现在它运行良好并正确删除)。

我为此花了几天(几周)时间,因为我不明白为什么这些行没有删除。我希望这对某人有所帮助。

忠告 - 尝试将您的问题和代码写得尽可能简单。你不应该在这里分享太多代码。人们只会忽略。

  1. 您正在使用 CursorLoader 但没有正确使用它。使用 LoadFinished 方法的游标数据。

  2. 然后你可以直接将光标传递给你的 FragmentPageAdapter 并在那里直接使用它。

希望对您有所帮助。

感谢@pskink、@CL 和@albeee - 这是我从你们和我自己的研究中学到的。

为了从填充 FragmentStatePagerAdapter 或 ArrayAdapter 的数据库中删除行,您必须能够 link 正确的行显示在适配器中。否则,这些行不会被删除,或者会不一致或不正确。 CursorAdapter 将自动处理更改的监视并为您 selecting ID。如果您可以使用 CursorAdapter 或直接 onItemClickListener 并使用 getSelectedId() 或只是长 ID 直接从 AdapterView 获取 ID,那么这是获取行 ID 的好方法。但是,如果您通过其他方式间接获取 id,则必须处理关联...

1.您不应将适配器位置、微调器位置甚至微调器 ID 用于 select 该行。这些仅对确定您想要的 item/fragment 有用。 只有项目本身的 OnClickListener ID 才能始终给出正确的行。

2.数据库行的行为如此 - 即使未选择 AUTOINCREMENT,整数主键也会自动递增,但行 ID 一旦分配就稳定。 这意味着每个新行的 ID 都比上一个高,但是如果您删除中间的行,它不会更改剩余行的 ID。所以当有编辑和删除时,行会跳过而不是连续的。因此,您必须有一种方法可以将项目 link 永久地添加到 ID。可能有更好的方法来执行此操作,但是,我将编辑上面的代码以显示我能够为 FragmentStatePagerAdapter 执行此操作的一种方法,以便用户可以动态添加和删除片段。