请随意跳到问题,因为您可能不需要了解背景知识
我是android和sqlite的新手,我正在设计一个应用程序,该应用程序有一个内容提供商来访问带有多个表的sqlite数据库。有几个活动使用不同的适配器将信息从数据库显示到UI(即游标适配器、片段状态页适配器和数组适配器)。我在所有不使用游标适配器的活动中都遇到了删除函数的问题。当我尝试更新或删除表中的一行时,它会删除错误的行,或者根本不删除任何内容。我认为这是适配器的问题,我正试图弄清楚哪一行将正确的信息发送给内容提供商。
相同的java代码与游标适配器完美配合,行删除正常,其他CRUD操作正常。插入和查询函数对所有表都正常工作。提供程序代码为每个表使用一个switch语句,但对于每个Uri情况,它基本上是相同的。所有表都将_id作为整数主键,该主键未设置为自动递增。由于我不完全理解行id是如何工作的,我的java代码没有反映它,我一直有这些问题。尽管我已经阅读了许多关于内容提供商、适配器、sqlite数据库等的文档,但某些关键细节对我来说并不清楚
我的问题是当将行id设置为_id列作为主键时,如何在数据库中为其分配数字?当数据库更改时,这些数字会发生什么
例如,假设我有一个空数据库。最初,在插入第一行之后,Uri将返回0行的路径段,适配器位置将为0…数据库的行id是什么(0或1)?
然后,对于我插入的每一行,我知道行数将增加一个整数。假设我插入4行-0.1,2,3。现在,当我准备删除时-Uri上的最后一个路径段是否应该比行号少一个整数(即,我是否发送最后一个路线段为2的Uri来删除第3行)?最后,删除后,行ID是否会自动重新分配,从而使第4行变成第3行?我是否需要编写一些代码才能在数据库中实现这一点?主键未设置为自动递增。
我有不同的适配器和活动,一旦数据显示在UI中,我就无法访问实际的数据库行ID,所以我使用适配器位置作为代理。这就是我在更新和删除时遇到问题的原因。非常感谢你,如果你阅读了整个问题并花时间回答它,这将对我有很大帮助。
我有一个选项卡式活动,它使用由数据库填充的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;
}**
以下是调用方法的活动,以及从游标中提取数据以传递给适配器的活动。有一个循环,每行在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();**
这是使用Spinner选择要编辑或删除的行/片段的"活动"。
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和@albee——这是我从你们和我自己的研究中学到的。
为了从填充FragmentStatePagerAdapter或ArrayAdapter的数据库中删除行,您必须能够将正确的行与适配器中显示的行链接起来。否则,这些行将不会删除,或者不一致或不正确。CursorAdapter将自动处理监视更改和为您选择ID。如果您可以使用CursorAdapter或直接的onItemClickListener,并使用getSelectedId()或长id直接从AdapterView获取id,那么这是获取行id的好方法。但是,如果您通过其他方式间接获取id,则必须处理关联。。。
1.您不应该使用适配器位置、微调器位置甚至微调器id来选择行。这些仅用于确定您想要的项目/片段只有项目本身的OnClickListener的ID才会始终给出正确的行。
2.数据库行的行为是这样的——即使没有选择AUTOINCREMENT,整数主键也会自动递增,但行ID一旦分配就稳定了这意味着每一个新行的ID都将高于上一行,但如果删除中间的行,则不会更改其余行的ID。因此,当进行编辑和删除时,这些行将跳过并且不连续。因此,您必须有一个方法将项目永久链接到ID。也许有更好的方法可以做到这一点,但是,我将编辑上面的代码,展示我为FragmentStatePagerAdapter实现这一点的一种方法,这样用户就可以动态添加和删除片段。