使用 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 上运行不佳(***在编辑之前它不起作用,现在它运行良好并正确删除)。
我为此花了几天(几周)时间,因为我不明白为什么这些行没有删除。我希望这对某人有所帮助。
忠告 - 尝试将您的问题和代码写得尽可能简单。你不应该在这里分享太多代码。人们只会忽略。
您正在使用 CursorLoader 但没有正确使用它。使用 LoadFinished 方法的游标数据。
然后你可以直接将光标传递给你的 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 执行此操作的一种方法,以便用户可以动态添加和删除片段。
请随时跳到问题,因为您可能不需要这种背景知识。
我是 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 上运行不佳(***在编辑之前它不起作用,现在它运行良好并正确删除)。
我为此花了几天(几周)时间,因为我不明白为什么这些行没有删除。我希望这对某人有所帮助。
忠告 - 尝试将您的问题和代码写得尽可能简单。你不应该在这里分享太多代码。人们只会忽略。
您正在使用 CursorLoader 但没有正确使用它。使用 LoadFinished 方法的游标数据。
然后你可以直接将光标传递给你的 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 执行此操作的一种方法,以便用户可以动态添加和删除片段。