需求背景
今天在讨论群里,发现有多位童鞋们问到了这个问题,于是乎,产生了撸个轮子玩玩!这也是我第一次以这样方式玩简书,希望能帮助童鞋们解决实际中的问题,这才是重点!
知识要点梳理
- 使用Android自带的SQLiteOpenHelper
- 数据库的增删改查
- sql 语句的编写
下面开始开车啦,老司机们赶紧上车哦!!哈哈。。。
1.撸个页面
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.yhx.app.MainActivity">
<RelativeLayout
android:textColor="@color/gray"
android:textSize="14sp"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="45dp">
<Button
android:id="@+id/btn_serarch"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:hint="搜索"/>
<EditText
android:id="@+id/et_search"
android:hint="输入内容"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/btn_serarch"/>
</RelativeLayout>
<RelativeLayout
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="历史记录"/>
<TextView
android:id="@+id/tv_deleteAll"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="删除历史记录"/>
</RelativeLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/mRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
2.使用Android自带的SQLiteOpenHelper来创建表数据
/**
* Created by yi.huangxing on 17/12/13.类描述:
*/
public class RecordSQLiteOpenHelper extends SQLiteOpenHelper{
private static String name = "record.db";
private static Integer version = 1;
public RecordSQLiteOpenHelper(Context context) {
super(context, name, null, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
//打开数据库,建立了一个叫records的表,里面只有一列name来存储历史记录:
db.execSQL("create table records(id integer primary key autoincrement,name varchar(200))");
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
3.表数据建好了,当然就是数据库的增,删,改,查,啰,为了使代码可读性性强,来个Dao层吧,那就叫DbDao
注意了,代码中的注释都是关键点,容易出错的地方哦。。。
public class DbDao {
private Context context;
private RecordSQLiteOpenHelper helper;
private SQLiteDatabase db;
public DbDao(Context context) {
this.context = context;
init();
}
private void init() {
//实例化数据库SQLiteOpenHelper子类对象
helper = new RecordSQLiteOpenHelper(context);
// 第一次进入时查询所有的历史记录
queryData("");
}
public List<String> queryData(String tempName) {
List<String> data = new ArrayList<>();
//模糊搜索
Cursor cursor = helper.getReadableDatabase().rawQuery(
"select id as _id,name from records where name like '%" + tempName + "%' order by id desc ", null);
while (cursor.moveToNext()) {
//注意这里的name跟建表的name统一
String name = cursor.getString(cursor.getColumnIndex("name"));
data.add(name);
}
cursor.close();
return data;
}
/**
* 检查数据库中是否已经有该条记录
*
* @param tempName
* @return
*/
public boolean hasData(String tempName) {
//从Record这个表里找到name=tempName的id
Cursor cursor = helper.getReadableDatabase().rawQuery(
"select id as _id,name from records where name =?", new String[]{tempName});
//判断是否有下一个
return cursor.moveToNext();
}
/**
* 插入数据
*
* @param tempName
*/
public void insertData(String tempName) {
db = helper.getWritableDatabase();
db.execSQL("insert into records(name) values('" + tempName + "')");
db.close();
}
/**
* 插入数据
*
* @param name
* @return
*/
public int delete(String name) {
// 获取数据
SQLiteDatabase db = helper.getWritableDatabase();
// 执行SQL
int delete = db.delete("records", " name=?", new String[]{name});
// 关闭数据库连接
db.close();
return delete;
}
/**
* 清空数据
*/
public void deleteData() {
db = helper.getWritableDatabase();
db.execSQL("delete from records");
db.close();
}
}
4.如何使用?
public class MainActivity extends AppCompatActivity {
private Button mbtn_serarch;
private EditText met_search;
private RecyclerView mRecyclerView;
private TextView mtv_deleteAll;
private SeachRecordAdapter mAdapter;
private DbDao mDbDao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
}
private void initViews() {
mDbDao =new DbDao(this);
mbtn_serarch = (Button) findViewById(R.id.btn_serarch);
met_search = (EditText) findViewById(R.id.et_search);
mtv_deleteAll = (TextView) findViewById(R.id.tv_deleteAll);
mtv_deleteAll.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mDbDao.deleteData();
mAdapter.updata(mDbDao.queryData(""));
}
});
mRecyclerView = (RecyclerView) findViewById(R.id.mRecyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter =new SeachRecordAdapter(mDbDao.queryData(""),this);
mAdapter.setRvItemOnclickListener(new BaseRecycleAdapter.RvItemOnclickListener() {
@Override
public void RvItemOnclick(int position) {
mDbDao.delete(mDbDao.queryData("").get(position));
mAdapter.updata(mDbDao.queryData(""));
}
});
mRecyclerView.setAdapter(mAdapter);
//事件监听
mbtn_serarch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (met_search.getText().toString().trim().length() != 0){
boolean hasData = mDbDao.hasData(met_search.getText().toString().trim());
if (!hasData){
mDbDao.insertData(met_search.getText().toString().trim());
}else {
Toast.makeText(MainActivity.this, "该内容已在历史记录中", Toast.LENGTH_SHORT).show();
}
//
mAdapter.updata(mDbDao.queryData(""));
}else {
Toast.makeText(MainActivity.this, "请输入内容", Toast.LENGTH_SHORT).show();
}
}
});
}
}
5.demo中我使用了baseAdapter, 一并撸上吧
/**
* Created by yi.huangxing on 17/12/13.类描述:
*/
public abstract class BaseRecycleAdapter <T> extends RecyclerView.Adapter<BaseRecycleAdapter.BaseViewHolder>{
protected List<T> datas;
protected Context mContext;
public BaseRecycleAdapter(List<T> datas,Context mContext) {
this.datas = datas;
this.mContext =mContext;
}
// 头部控件
private View mHeaderView;
// 底部控件
private View mFooterView;
// item 的三种类型
public static final int ITEM_TYPE_NORMAL = 0X1111; // 正常的item类型
public static final int ITEM_TYPE_HEADER = 0X1112; // header
public static final int ITEM_TYPE_FOOTER = 0X1113; // footer
private boolean isHasHeader = false;
private boolean isHasFooter = false;
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType==ITEM_TYPE_FOOTER){
// 如果是底部类型,返回底部视图
return new BaseViewHolder(mFooterView);
}
if(viewType==ITEM_TYPE_HEADER){
return new BaseViewHolder(mHeaderView);
}
View view = LayoutInflater.from(parent.getContext()).inflate(getLayoutId(),parent,false);
return new BaseViewHolder(view);
}
@Override
public void onBindViewHolder(BaseRecycleAdapter.BaseViewHolder holder, final int position) {
if(isHasHeader&&isHasFooter){
// 有头布局和底部时,向前便宜一个,且最后一个不能绑定数据
if(position==0 ||position==datas.size()+1){
return;
}
bindData(holder,position-1);
}
if(position!=0&&isHasHeader&&!isHasFooter){
// 有顶部,但没有底部
bindData(holder,position-1);
}
if(!isHasHeader&&isHasFooter){
// 没有顶部,但有底部
if(position==datas.size()){
return;
}
bindData(holder,position);
}
if(!isHasHeader&&!isHasFooter){
// 没有顶部,没有底部
bindData(holder,position);
}
}
/**
* 添加头部视图
* @param header
*/
public void setHeaderView(View header){
this.mHeaderView = header;
isHasHeader = true;
notifyDataSetChanged();
}
/**
* 添加底部视图
* @param footer
*/
public void setFooterView(View footer){
this.mFooterView = footer;
isHasFooter = true;
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
// 根据索引获取当前View的类型,以达到复用的目的
// 根据位置的索引,获取当前position的类型
if(isHasHeader&&position==0){
return ITEM_TYPE_HEADER;
}
if(isHasHeader&&isHasFooter&&position==datas.size()+1){
// 有头部和底部时,最后底部的应该等于size+!
return ITEM_TYPE_FOOTER;
}else if(!isHasHeader&&isHasFooter&&position==datas.size()){
// 没有头部,有底部,底部索引为size
return ITEM_TYPE_FOOTER;
}
return ITEM_TYPE_NORMAL;
}
/**
* 刷新数据
* @param datas
*/
public void refresh(List<T> datas){
this.datas.clear();
this.datas.addAll(datas);
notifyDataSetChanged();
}
/**
* 刷新数据
* @param data
*/
public void updata(List<T> data){
this.datas=data;
notifyDataSetChanged();
}
/**
* 添加数据
* @param datas
*/
public void addData(List<T> datas){
this.datas.addAll(datas);
notifyDataSetChanged();
}
/**
* 移除数据
*
* @param position
*/
public void remove(int position) {
if (position >= 0 && position < datas.size()) {
datas.remove(position);
notifyDataSetChanged();
}
}
/**
* 绑定数据
* @param holder 具体的viewHolder
* @param position 对应的索引
*/
protected abstract void bindData(BaseViewHolder holder, int position);
@Override
public int getItemCount() {
int size = datas.size();
if(isHasFooter)
size ++;
if(isHasHeader)
size++;
return size;
}
/**
* 封装ViewHolder ,子类可以直接使用
*/
public class BaseViewHolder extends RecyclerView.ViewHolder{
private Map<Integer, View> mViewMap;
public BaseViewHolder(View itemView) {
super(itemView);
mViewMap = new HashMap<>();
}
/**
* 获取设置的view
* @param id
* @return
*/
public View getView(int id) {
View view = mViewMap.get(id);
if (view == null) {
view = itemView.findViewById(id);
mViewMap.put(id, view);
}
return view;
}
}
/**
* 获取子item
* @return
*/
public abstract int getLayoutId();
/**
* 设置文本属性
* @param view
* @param text
*/
public void setItemText(View view,String text){
if(view instanceof TextView){
((TextView) view).setText(text);
}
}
public RvItemOnclickListener getRvItemOnclickListener() {
return mRvItemOnclickListener;
}
public void setRvItemOnclickListener(RvItemOnclickListener rvItemOnclickListener) {
mRvItemOnclickListener = rvItemOnclickListener;
}
protected RvItemOnclickListener mRvItemOnclickListener;
public interface RvItemOnclickListener{
void RvItemOnclick(int position);
}
/**
* 这个方法很重要的
* @param holder
*/
//
// @Override
// public void onAttachedToRecyclerView(RecyclerView recyclerView) {
// super.onAttachedToRecyclerView(recyclerView);
//
// RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
// if(manager instanceof GridLayoutManager) {
// final GridLayoutManager gridManager = ((GridLayoutManager) manager);
// gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
// @Override
// public int getSpanSize(int position) {
// return getItemViewType(position) == TYPE_HEADER
// ? gridManager.getSpanCount() : 1;
// }
// });
// }
// }
}
6. 我知道效果图才是老铁们的最爱:造就完了
studio下载demo猛戳这里
问题反馈:
这几天陆续有几位老铁反馈,下载demo运行是没有问题,但用在自己的项目中报错(具体报错信息请看老铁的留言),原因很简单,baseAdapter 复制代码不全,导致的。
解决方案:
1.使用自己定义adapter即可。
2.直接copy demo里面的baseAdapter,不建议,自己手写一半,复制一半,导致复制代码不全。