设为首页 收藏本站
查看: 1638|回复: 0

[经验分享] Android的SQlite先天不足:删除 插入后主键不能自动排序 解决(附:SQlite开发的完整demo)

[复制链接]

尚未签到

发表于 2016-12-1 09:20:55 | 显示全部楼层 |阅读模式
   SQlite有个问题,就是主键不能够自动排序。比如说主键id为1 2 3 4,共4条记录。现在删除2 3,还剩下1 4记录,当再次插入时,id会变成5,而不是2.假设在初始4条记录的基础上,把这4条记录全都删掉,再次插入时,得到的id是5. 这种机制实在是太不好了!解决方法是在主键id外新增加一个realid,对realid进行处理。具体请看代码:
   这里一并附上

Android开发:setContentView切换界面,自定义带CheckBox的ListView显示SQlite条目-----实现
  的代码。整个代码的功能是数据库里有个表,结构是id和姓名。可以增加、删除、更新,在主界面里有一个选定按钮,点击后会显示带checkbox的listview,显示数据库里的条目,按上面的确定按钮返回主界面。
  第一部分:-------main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white"
android:orientation="vertical" >
<EditText
android:id="@+id/nameEdit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:hint="请输入志愿者姓名"
android:textColor="@android:color/holo_red_dark"
android:textSize="25dip" />
<Button
android:id="@+id/choseBtn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="选定"
/>
<ListView
android:id="@+id/palmList"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>

  

  my_checkbox.xml-----------用来控制listview的每一行怎么显示,这是跳转到选定界面呈现的

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"   
android:orientation="horizontal" >
<TextView
android:id="@+id/item_text"
android:textSize="25dip"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox
android:id="@+id/item_check"
android:textSize="25dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"/>

</LinearLayout>

  

  list_check.xml-----------------------这个布局是主界面要跳转的界面的布局,上面是个按键,下面是个带checkbox的 listview

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<Button
android:id="@+id/confirmBtn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="确定" />
<ListView
android:id="@+id/checkList"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>

  

  
  第二部分:
  PalmDB.java--------------自定义的一个数据库类,继承SQLiteOpenHelper,功能有增加、删除、更新。

package yan.guoqi.testsqlite;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class PalmDB extends SQLiteOpenHelper{
private static final String DATABASE_NAME = "Palm.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "Palm_table";
//表里面的三个内容
private static final String ID = "_id";
private static final String NAME = "_name";
private static final String RealID = "_realid";
private static final String Tag = "PalmDB";

public PalmDB(Context context) //, String name, CursorFactory factory,int version
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
String sql = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER primary key autoincrement, "
+ NAME +" text, " + RealID + " INTEGER);"; //
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
String sql = "DROP TABLE IF EXISTS " + TABLE_NAME;
db.execSQL(sql);
onCreate(db);
}

public Cursor select(){
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, null, null, null, null, null, RealID +" ASC");
return cursor;
}

/*增加操作*/
public long insert(String name){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
int realid = getRealId();
cv.put(NAME, name);
cv.put(RealID, realid);
long row = db.insert(TABLE_NAME, null, cv);
return realid;
//return row;  //row是行的意思
}
/*得到一个真实的id*/
public int getRealId(){
Cursor cursor = select();
int realid = 1;
//如果cursor为空,返回id = 1
if(!cursor.moveToFirst()){
return realid;
}
else{
while(true){
if(realid != cursor.getInt(2))
{
return realid;
}
else
{
realid++;
if(!cursor.moveToNext())
return realid;
}
}
}
}
/*删除操作*/
public void delete(int id){
SQLiteDatabase db = this.getWritableDatabase();
String where = ID + "=?";
String[] whereValue = { Integer.toString(id) };
db.delete(TABLE_NAME, where, whereValue);
}
/*修改操作*/
//id是你要修改的id号,name是新的名字
public void update(int id, String name){
SQLiteDatabase db = this.getWritableDatabase();
String where = ID + "=?";
String[] whereValue = { Integer.toString(id) };
ContentValues cv = new ContentValues();
cv.put(NAME, name);
db.update(TABLE_NAME, cv, where, whereValue);
}

}

  
需要注意的几点:
  1,这个表里有3个元素,分别是ID NAME RealID ,这个ID是主键,类型是INTEGER primary key autoincrement, 第三个RealID是为了解决主键不能自动排序而新增加的。
  2,public void onCreate(SQLiteDatabase db)这个函数是数据库创建的时候,注意这句话的写法:
  String sql = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER primary key autoincrement, "
   + NAME +" text, " + RealID + " INTEGER);"; //
  里面的标点可是一点不敢错,每个元素后面带“,”。
  3,public Cursor select()这个函数很关键,是获得查询的游标。Cursor cursor = db.query(TABLE_NAME, null, null, null, null, null, RealID +" ASC"); 后面的最后一个参数是让查询的时候按照RealID升序排列。这个很关键,这和下面的自动产生排序的ID亲密相连。
  4,public int getRealId()这个函数得到自动排序的ID。里面就是算法:

/*得到一个排好序,即将插入的id*/
public int getRealId(){
Cursor cursor = select();
int realid = 1;
//如果cursor为空,返回id = 1
if(!cursor.moveToFirst()){
return realid;
}
else{
while(true){
if(realid != cursor.getInt(2))
{
return realid;
}
else
{
realid++;
if(!cursor.moveToNext())
return realid;
}
}
}
}

  
这个函数是让id自动排序的关键。首先判断cursor是否为空,空的话就是没有记录,则返回的id = 1; 不空的话则进入循环。如果realid不等于当前cursor的第一行元素,则直接返回realid。否则话realid加1,游标往下移一行。这里跟插入法排序有点像,前提是这个游标必须是有序的。这就和上面的3挂上钩了。可以用Java,借助数组模拟验证这个算法是否对:

package yan.guoqi;
public class MainClass {
public static void main(String[] args){
int a = MainClass.getRealId();
System.out.println(" "+ a);

}
/*得到真正的real—id*/
public static  int getRealId(){
int[] cursor = {1, 3, 6};
int id=1;
int i=0;
while(true){
if(cursor.length == 0)
return id;
else{
if(id!=cursor)
{
return id;
}
else
{
id++;
i++;
if(i == cursor.length)
return id;
}
}
}
}

}




5,至于下面的deleteupdate函数,是删除和更新,依据的索引是ID,而不是RealID当然如果你喜欢,改成依据RealID也无妨。详细可参考:

Android 高手进阶教程(十三)之----Android 数据库SQLiteDatabase的使用!!



  6,另外就是insert函数,返回的是realid,也就是你插入一条记录后,这个记录的真实行号。这个值和longrow=db.insert(TABLE_NAME,null,cv);里的row是不同的,这个rowID号。是不规则的那个行号,依据主键而来

第三部分:主界面的源码

package yan.guoqi.testsqlite;
import java.util.HashMap;
import java.util.Map;
import yan.guoqi.testsqlite.TestSQliteActivity.CheckListAdapter.ViewHolder;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class TestSQliteActivity extends Activity implements AdapterView.OnItemClickListener {
private PalmDB mPalmDB;
private Cursor cursor;
private EditText mNameEdit;
private ListView mPalmList;
private int id = 0;
protected static final int MENU_ADD = Menu.FIRST;
protected static final int MENU_DELETE = Menu.FIRST + 1;
protected static final int MENU_UPDATE= Menu.FIRST + 2;
private static final String Tag="SQlite:";
//为了实现新的布局
Button mChoseBtn = null;
Button mConfirmBtn = null;
boolean firstFlag = true;
ListView list2 = null;
View checkListView = null;
View mainView = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

LayoutInflater inflater = this.getLayoutInflater();
checkListView = inflater.inflate(R.layout.list_check, null);
mainView = inflater.inflate(R.layout.main, null);

setContentView(mainView);
//切换布局监听
mChoseBtn = (Button)mainView.findViewById(R.id.choseBtn);
mChoseBtn.setOnClickListener(new ButtonListener());
setUpViews();

}
class ButtonListener implements OnClickListener{
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()){
case R.id.choseBtn:
Jump2CheckList();
break;
case R.id.confirmBtn:
String s = getCheckInfo();
showToast("您选中的姓名有:"+ s);
Jump2Main();
break;
default:
break;
}
}
}
public String getCheckInfo()
{
String info = "";
for(int i=0; i<list2.getCount(); i++){
if(isSelected.get(i)){
//ViewHolder holder = (ViewHolder)list2.getChildAt(i).getTag();
cursor.moveToPosition(i);
info+=cursor.getInt(0)+".";
}
}
return info;
}
/*切换到选中布局*/
public void Jump2CheckList(){
setContentView(checkListView);
if(firstFlag){
mConfirmBtn = (Button)checkListView.findViewById(R.id.confirmBtn);
mConfirmBtn.setOnClickListener(new ButtonListener());
firstFlag = false;
}
initCheckList();

}

public void initCheckList(){
list2 = (ListView)(checkListView).findViewById(R.id.checkList);
list2.setItemsCanFocus(false);
list2.setAdapter(new CheckListAdapter(this, cursor));
list2.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
list2.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View view, int positon,
long id) {
// TODO Auto-generated method stub
ViewHolder vHolder = (ViewHolder) view.getTag();
vHolder.check.toggle();
isSelected.put(positon, vHolder.check.isChecked());

}
});
}

/*切换到主布局*/
public void Jump2Main(){
setContentView(mainView);
setUpViews();
}
/*初始化数据库,更新View*/
public void setUpViews(){
//禁止输入法自己探出来
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
mPalmDB = new PalmDB(this);
Log.i(Tag, "aaaaaaaaa");
cursor = mPalmDB.select();
Log.i(Tag, "11111111");
mNameEdit = (EditText)findViewById(R.id.nameEdit);
mPalmList = (ListView)findViewById(R.id.palmList);
mPalmList.setAdapter(new PalmListAdapter(this, cursor));
mPalmList.setOnItemClickListener(this);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
super.onCreateOptionsMenu(menu);
//menu.
menu.add(Menu.NONE, MENU_ADD, 0, "增加");
menu.add(Menu.NONE, MENU_DELETE, 1, "删除");
menu.add(Menu.NONE, MENU_UPDATE, 2, "修改");
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
super.onOptionsItemSelected(item);
switch(item.getItemId()){
case MENU_ADD:
add();
break;
case MENU_DELETE:
delete();
break;
case MENU_UPDATE:
update();
break;
default:
break;
}
return true;
}
public void add(){
String name = mNameEdit.getText().toString();
if(name.equals("") ){
showToast("志愿者姓名不能为空");
return;
}
long row = mPalmDB.insert(name);
cursor.requery();
mPalmList.invalidateViews();
mNameEdit.setText("");
showToast("添加成功!编号:" + row);// + Integer.toString(cursor.getInt(0))
}
public void delete(){
if(id == 0){
showToast("还未有志愿者,无法删除!");
return ;
}
if(mNameEdit.getText().toString().equals("")){
showToast("请选中后删除!");
return;
}
mPalmDB.delete(id);
cursor.requery();
mPalmList.invalidateViews();
mNameEdit.setText("");
showToast("删除成功!");
}
public void update(){
String name = mNameEdit.getText().toString();
if(name.equals("")) {
showToast("选中为空!无法修改。");
return;
}
mPalmDB.update(id, name);
cursor.requery();
mPalmList.invalidateViews();
mNameEdit.setText("");
showToast("修改成功!");

}
public void onItemClick(AdapterView<?> arg0, View view, int position, long _id) {
// TODO Auto-generated method stub
cursor.moveToPosition(position);
id = cursor.getInt(0);
mNameEdit.setText(cursor.getString(1));
}
public void showToast(String str){
Toast.makeText(this,
str,
Toast.LENGTH_SHORT).show();
}

/*给CheckList设置适配器*/
public static  Map<Integer, Boolean> isSelected;
public  class CheckListAdapter extends BaseAdapter{
private Context mContext;
private Cursor mCursor;

//构造函数
public CheckListAdapter(Context context, Cursor cursor){
mContext = context;
mCursor = cursor;
isSelected = new HashMap<Integer, Boolean>();
for(int i=0; i<mCursor.getCount(); i++){
isSelected.put(i, false);
}
}

public int getCount() {
// TODO Auto-generated method stub
return cursor.getCount();
}
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return 0;
}
public View getView(int position, View convertView, ViewGroup arg2) {
// TODO Auto-generated method stub
ViewHolder holder = null;
if(convertView == null){
holder = new ViewHolder();
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.my_checkbox, null);
holder.text = (TextView) convertView.findViewById(R.id.item_text);
holder.check = (CheckBox)convertView.findViewById(R.id.item_check);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder)convertView.getTag();
}

mCursor.moveToPosition(position);
holder.text.setText(Integer.toString(mCursor.getInt(2)));
holder.text.append(mCursor.getString(1));
holder.check.setChecked(isSelected.get(position));
return convertView;
}
public final class ViewHolder{
public TextView text;
public CheckBox check;
}
}

/*自定义适配器*/
public class PalmListAdapter extends BaseAdapter{
private Context mContext;
private Cursor mCursor;
//构造函数
public PalmListAdapter(Context context, Cursor cursor){
mContext = context;
mCursor = cursor;
}
public int getCount() {
// TODO Auto-generated method stub
return mCursor.getCount();
}
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
TextView mTextView = new TextView(mContext);
mCursor.moveToPosition(position);
mTextView.setTextSize(25);
mTextView.setTextColor(Color.BLUE);
mTextView.setText(Integer.toString(mCursor.getInt(2)));
mTextView.append(mCursor.getString(1));
return mTextView;
}
}
}

  
  注意:
  1,程序基本上和上篇

Android开发:setContentView切换界面,自定义带CheckBox的ListView显示SQlite条目-----实现
  差不多。可以看到适配器其实很好写,CheckListAdapter是带checkbox的listview的适配器, PalmListAdapter是主界面的那个适配器。 适配器的重点在public View getView(int position, View convertView, ViewGroup parent)这个函数,可以对比两个适配器在获得view上写法上的不同。
  
  2,由于在源程序2里,删除依据的id是ID,也就是主键,所以在这里提供的也必须是主ID。 如果一个用ID 一个提供的是RealID,会出现删除不了,删除错乱的情况!
  最后看看效果:
  初始界面:
DSC0000.jpg

  增加4条记录后:
DSC0001.jpg

  删除2 和3:
DSC0002.jpg

  再新加一个记录:
DSC0003.jpg

  再次新加:
DSC0004.jpg

  点上面的选定后:
DSC0005.jpg

  选中3条记录
DSC0006.jpg

  点击上面的确定回到主界面:
DSC0007.jpg

  源码下载:http://www.pudn.com/downloads528/sourcecode/comm/android/detail2187701.html
  欢迎android爱好者加群248217350 备注:yanzi
  ---------------------本文系原创,转载请注明作者:yanzi1225627
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-308012-1-1.html 上篇帖子: android中滑动SQLite数据库分页加载 下篇帖子: 3个用于SQLite数据库操作的类
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表