Android 數據儲存的方式有不少種 —— SharedPreferences 儲存 、 文件儲存 、 數據庫儲存 、 網絡儲存(儲存到後臺網絡) ,雖然儲存方式那麼多,可是這些儲存方式也有必定的規律可尋:java
SharedPreferences : 存儲 key value 鍵值對類型的簡單少許的簡單數據 (如用戶名 ,密碼 ,性別,年齡 。。。)android
文件 : 沒有固定規則的,大量的數據 (如:大量的圖片 ,文字 。。。)
數據庫
數據庫 : 有固定規則的,大量數據(如:學生的信息:ID 姓名 年齡 班級 。。。每一個學生都有的)
網絡
網絡 : 。。。多線程
今天這篇文章介紹的是數據儲存之數據庫儲存:參考文獻:http://blog.csdn.net/wu_wxc/article/details/49476779ide
先看下效果圖:函數
使用數據庫儲存數據有四個方法必需要知道的:增 刪 改 查
工具
咱們介紹下功能:既然是數據庫的儲存,功能無非就是增刪改查。佈局
1.添加黑名單是: 增。優化
2.刪除黑名單是: 刪。
3.長按條目顯示彈出框,修改黑名單的攔截方式:改。
4.整個列表的展現:查。
接下來看下代碼:
BlackNumDbHelper.java 繼承SQLiteOpenHelper
/** * 新建一個數據庫的類 db (database) 類要繼承squliteOpenhelper * 再建一個操做數據庫的工具類(增刪改查) * Created by Administrator on 2017/6/29. */ public class BlackNumDbHelper extends SQLiteOpenHelper { public BlackNumDbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } @Override /** * 第一次運行時,建立數據庫,調用此方法 */ public void onCreate(SQLiteDatabase db) { //建表:自增加的主鍵 , 號碼 , 攔截模式 db.execSQL("create table black_num(_id integer primary key autoincrement, number varchar(20) , mode integer);"); } @Override /** * 當數據庫版本不同,升級數據庫時,調用此方法 */ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
用一個操做數據庫的工具類 , 通常寫成單例模式:BlackDao .java
/** * BlackDao 操做咱們數據庫的工具類 咱們通常寫成單例模式 * 單例模式 : 在整個應用程序中 無論什麼地方(類) 得到的都是同一個對象實例 * @author Administrator */ public class BlackDao { //把數據庫建立出來 private BlackNumDbHelper dbHelper; //black_num表名 private String table_black_num ="black_num"; //單例模式 //不能讓每個類都能new一個 那樣就不是同一個對象了 因此首先構造函數要私有化 以上下文做爲參數 private BlackDao(Context ctx){ //因爲數據庫只須要調用一次,因此在單例中建出來 dbHelper= new BlackNumDbHelper(ctx, "black_num.db", null, 1); } //public static 爲靜態類型 要調用就要有一個靜態的變量 爲私有的 private static BlackDao instance; //既然BlackDao類是私有的 那麼別的類就不可以調用 那麼就要提供一個public static(公共的 共享的)的方法 //方法名爲getInstance 參數爲上下文 返回值類型爲BlackDao //要加上一個synchronized(同步的) //若是同時有好多線程 同時去調用getInstance()方法 就可能會出現一些建立(new)多個BlackDao的現象 因此要加上synchronized public static synchronized BlackDao getInstance(Context ctx){ //就能夠判斷 若是爲空 就建立一個, 若是不爲空就還用原來的 這樣整個應用程序中就只能獲的一個實例 if(instance == null){ instance = new BlackDao(ctx); } return instance; } //經常使用方法 增刪改查 /** * 添加黑名單 至數據庫 * @param number * @param mode */ public void addBlackNum(String number,int mode){ //得到一個可寫的數據庫的一個引用 SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values= new ContentValues(); values.put("number", number); // KEY 是列名,vlaue 是該列的值 values.put("mode", mode);// KEY 是列名,vlaue 是該列的值 // 參數一:表名,參數三,是插入的內容 // 參數二:只要能保存 values中是有內容的,第二個參數能夠忽略 db.insert(table_black_num, null, values); } /** * 刪除黑名單 * @param number */ public void deleteBlackNum(String number){ // ??dad?asd??sad?asdasdasd? SQLiteDatabase db = dbHelper.getWritableDatabase(); //表名 刪除的條件 db.delete(table_black_num, "number = ?", new String[] {number}); } /** * 更新黑名單攔截模式 * @param number * @param newMode */ public void updateBlackNumMode(String number,int newMode){ SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values= new ContentValues(); values.put("mode",newMode); db.update(table_black_num, values," number = ?", new String[]{number}); } /** * //查找 每個黑名單都有 號碼和模式 先把號碼和模式封裝一個bean * 得到全部的黑名單 * @return */ //分頁查詢 修改 public List<BlackNumBean> getBlackNumByPage(int pageIndex, int pageSize){ //public List<BlackNumBean> getAllBlackNum(){ //建立集合對象 List<BlackNumBean> allBlackNum = new ArrayList<BlackNumBean>(); SQLiteDatabase db = dbHelper.getReadableDatabase(); //Cursor cursor = db.query(table_black_num, null, null, null, null, null, null); //分頁查詢 修改 //Cursor cursor = db.rawQuery("select * from black_num limit "+pageSize+"offent"+(pageIndex * pageSize)+";", null); //order by _id desc 根據_id倒敘排列 使每次添加的黑名單在下次打開時顯示上面 同時每頁限制20個 Cursor cursor = db.rawQuery("select * from black_num order by _id desc limit "+pageSize+" offset "+(pageIndex*pageSize)+";", null); // 返回的 cursor 默認是在第一行的上一行 //遍歷 while(cursor.moveToNext()){// cursor.moveToNext() 向下移動一行,若是有內容,返回true String number = cursor.getString(cursor.getColumnIndex("number"));// 得到number 這列的值 //得到模式 一共三列 mode爲第二列 int mode = cursor.getInt(2); //將number mode 封裝到bean中 BlackNumBean bean = new BlackNumBean(number, mode); //封裝的對象添加到集合中 allBlackNum.add(bean); } //關閉cursor cursor.close(); SystemClock.sleep(1000);// 休眠2秒,模擬黑名單比較多,比較耗時的狀況 return allBlackNum; }; /** * 得到黑名單的數量 */ public int getBlackNumCount(){ SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = db.query(table_black_num, new String[] {"count(*)"}, null, null, null, null, null); cursor.moveToNext(); int count = cursor.getInt(0);// 僅查了一列,count(*) 這一刻列 cursor.close(); return count; } /* *//** * //查找 每個黑名單都有 號碼和模式 先把號碼和模式封裝一個bean * 得到全部的黑名單 * @return *//* public List<BlackNumBean> getAllBlackNum(){ //建立集合對象 List<BlackNumBean> allBlackNum = new ArrayList<BlackNumBean>(); SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = db.query(table_black_num, null, null, null, null, null, null); // 返回的 cursor 默認是在第一行的上一行 //遍歷 while(cursor.moveToNext()){// cursor.moveToNext() 向下移動一行,若是有內容,返回true String number = cursor.getString(cursor.getColumnIndex("number"));// 得到number 這列的值 //得到模式 一共三列 mode爲第二列 int mode = cursor.getInt(2); //將number mode 封裝到bean中 BlackNumBean bean = new BlackNumBean(number, mode); //封裝的對象添加到集合中 allBlackNum.add(bean); } //關閉cursor cursor.close(); SystemClock.sleep(1000);// 休眠2秒,模擬黑名單比較多,比較耗時的狀況 return allBlackNum; }; */ /** * 根據號碼,得到攔截模式 * @param number * @return * 若是不是黑名單,那麼,返回 -1 */ public int getMOdeByNumber(String number) { int mode = -1; //得到一個可讀的數據庫的一個引用 SQLiteDatabase db = dbHelper.getReadableDatabase(); //查詢 表 列 條件 Cursor cursor = db.query(table_black_num, null, "number = ?", new String []{number}, null, null, null); if( cursor.moveToNext()){// 若是查到了,移動成功 mode = cursor.getInt(cursor.getColumnIndex("mode")); } cursor.close(); return mode ; } }
/** * Created by Administrator on 2017/6/29. */ public class BlackNumBean { public String number; public int mode; public BlackNumBean() { super(); // TODO Auto-generated constructor stub } public BlackNumBean(String number, int mode) { super(); this.number = number; this.mode = mode; } @Override public String toString() { return "BlackNumBean [number=" + number + ", mode=" + mode + "]"; } }
/** * 具備固定規則的大量的數據 能夠儲存在數據庫中 因此建庫 而後增刪改查 * 能夠有三列 _id主鍵列 number號碼列 mode int 0所有攔截 1攔截電話 2攔截短信 * 新建一個數據庫的類 db (database) 類要繼承squliteOpenhelper */ public class MainActivity extends AppCompatActivity { private ListView listView; private LinearLayout llLoading; private ProgressBar progressBar; private TextView tvDesc; private MyAdapter adapter; /** * 黑名單操做的工具類 */ private BlackDao blackDao; //黑名單的集合 //private List<BlackNumBean> allBlackNum; private List<BlackNumBean> blackNums; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ctx = this; listView = (ListView) findViewById(R.id.li_listView); llLoading = (LinearLayout) findViewById(R.id.ll_loading); progressBar = (ProgressBar) findViewById(R.id.pb_progressBar); tvDesc = (TextView) findViewById(R.id.tv_desc); blackDao = BlackDao.getInstance(this); // 只有當onCraete 執行完了之後,頁面纔會顯示出來, // 在onCreate 方法中不能有耗時的操做,哪怕是 1 秒也不行,會嚴重影響用戶體驗, // 若是有耗時的操做(加載數據),必定要開子線程 fillData(); //添加監聽 regListener(); } //修改黑名單 當長按某一個條目時 彈出提個修改對話框 //添加一個長按的監聽 private void regListener() { // listView 添加條目長按的監聽 listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override /** * 長按某個條目時,調用此方法 , * 注意,返回 true */ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { showUpdataBlackNumDialog(position); return true; } }); } /** * 顯示更新黑名單對話框 * * @param position 長按的條目的下標 */ protected void showUpdataBlackNumDialog(int position) { //複製添加黑名單的code 修改 //在java代碼中建立對話框 AlertDialog.Builder adb = new AlertDialog.Builder(this); dialog = adb.create(); //從集合中獲取封裝的對象 final BlackNumBean blackNumBean = blackNums.get(position); //將佈局轉換爲view對 View view = getLayoutInflater().inflate(R.layout.dialog_updata_blacknum, null); final TextView tvBlackNum = (TextView) view.findViewById(R.id.tv_black_number); //獲取電話號碼 並賦值 tvBlackNum.setText("黑名單:" + blackNumBean.number); final CheckBox cbStopCall = (CheckBox) view.findViewById(R.id.cb_stop_call); final CheckBox cbStopSms = (CheckBox) view.findViewById(R.id.cb_stop_sms); //獲取模式 並初始化模式 switch (blackNumBean.mode) { case 0: cbStopCall.setChecked(true); cbStopSms.setChecked(true); break; case 1: cbStopCall.setChecked(true); cbStopSms.setChecked(false); break; case 2: cbStopCall.setChecked(false); cbStopSms.setChecked(true); break; } Button btnCancel = (Button) view.findViewById(R.id.btn_cancel); Button btnOk = (Button) view.findViewById(R.id.btn_ok); //爲兩個按鈕 添加點擊事件 //取消按鈕 btnCancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); //點擊肯定按鈕 btnOk.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //獲取輸入框號碼 String number = tvBlackNum.getText().toString().trim(); if (TextUtils.isEmpty(number)) { MyUtils.showToast(ctx, "號碼不能爲空"); return; } //獲取模式 int newMode = 0;// 初始化模式 if (cbStopCall.isChecked() && cbStopSms.isChecked()) { newMode = 0; } else if (cbStopCall.isChecked() && !cbStopSms.isChecked()) { newMode = 1; } else if (!cbStopCall.isChecked() && cbStopSms.isChecked()) { newMode = 2; } else { MyUtils.showToast(ctx, "請選擇攔截模式"); return; } //更新黑名單 blackDao.updateBlackNumMode(blackNumBean.number, newMode); //顯示至listview頁面 blackNumBean.mode = newMode; //修改數據 adapter.notifyDataSetChanged();//刷新 //關閉對話框 dialog.dismiss(); } }); dialog.setView(view); dialog.show(); } /** * 當手指在listview上滑動時 */ private void resListener() { //爲ListView設置一個滑動監聽 listView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override /** * 當滑動狀態發生改變時 */ public void onScrollStateChanged(AbsListView view, int scrollState) { // OnScrollListener.SCROLL_STATE_IDLE; //空閒狀態 idle空閒 // OnScrollListener.SCROLL_STATE_FLING;// 快速滑東, 沒有觸摸 但在滑動 // OnScrollListener.SCROLL_STATE_TOUCH_SCROLL; // 觸摸並滑動 //在空閒的時候 判斷屏幕最後一個條目,是不是listvist 的最後一個條目, 若是是 說命該加載項更多的數據了 if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) { //得到可見的最後一個條目的下表 int lastVisiblePosition = listView.getLastVisiblePosition(); if (lastVisiblePosition == adapter.getCount() - 1) {// 看到最後一個條目了 if (pageIndex < totalPage - 1) { //當前頁面的下標 加一 pageIndex++; fillData(); } else { MyUtils.showToast(ctx, "沒有數據了"); } } } } @Override /** * 滑動時不斷調用此方法 */ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }); } private void fillData() { //當點擊上一頁 下一頁 是將加載狂顯示出來 llLoading.setVisibility(View.VISIBLE); // 進入頁面後 加載數據(開子線程) 根據結果 顯示頁面 new Thread() { public void run() { //當第一次加載後 會把數據設置給集合 //當第二次加載後 會把數據追加在第一次後 if (blackNums == null) {//第一次 //全部的黑名單的集合 // allBlackNum = blackDao.getAllBlackNum(); blackNums = blackDao.getBlackNumByPage(pageIndex, pageSize); } else { ///加載更多 ,數據追加 blackNums.addAll(blackDao.getBlackNumByPage(pageIndex, pageSize)); } //獲取黑名單的數量 int totalcount = blackDao.getBlackNumCount(); if (totalcount % pageSize == 0) {// 判斷是否能整除 totalPage = totalcount / pageSize; } else { totalPage = totalcount / pageSize + 1; } //若是集的size 是0 則沒有黑名單 ,若是不是零 則有黑名單 則要展現出來、、 //而子線程不能改變頁面 那麼就要發送handler信息 //發送一個空的消息 數據獲取完了 能夠刷新頁面 handler.sendEmptyMessage(FLUSH_UI); } ; }.start(); } //黑名單數據加載的方式二 分批加載 //生成變量 /** * 當前頁面的下標 */ private int pageIndex = 0; /** * 每一頁的數量 */ private int pageSize = 20; /** * 總頁數 */ private int totalPage; protected Activity ctx; //刷新界面用的 當得到了數據後 就發送一個信息 private final int FLUSH_UI = 100; private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case FLUSH_UI://子線程得到了數據,開始刷新頁面 // 沒有黑名單的狀況 if (blackNums.size() == 0) {// 沒有黑名單的狀況 progressBar.setVisibility(View.GONE); tvDesc.setText("沒有黑名單,請添加幾個吧"); } else { // 有黑名單,關閉加載框,listView 展現黑名單 就是爲Listview 設置Adapter llLoading.setVisibility(View.GONE); if (adapter == null) { //第一次加載 adapter = new MyAdapter(); listView.setAdapter(adapter); } else { //追加數據 adapter.notifyDataSetChanged();// 刷新listView 不然仍會從頭開始 顯示 } } break; } } ; }; /** * listview 若是不優化可能出現的問題: * 一: getView 方法 大量調用,建立大量的對象,形成內存的浪費,甚至是 OOM 異常 * 二:若是getview 方法執行的時間過長,超過 150 毫秒,用戶就會明顯的感受到卡頓現象 * * @author Administrator * <p/> * 優化的目標:建立儘量少的對象,執行getView的時間儘量短 * <p/> * listview 優化一:複用convertView * 優化的結果:當convertView 不爲空時,再也不建立新的 view 對象,省略了 * getLayoutInflater().inflate(R.layout.list_item_black_num, null); * 而 inflate 是一個比較耗時的動做。 * <p/> * listView 優化二:使用ViewHolder * 優化的結果:當convertView 不爲空時 ,經過 convertView 身上的揹包,得到他的子view 而後,爲子view賦值, * 從而,省略了 findViewById 這個方法 */ private class MyAdapter extends BaseAdapter { @Override /** * 告訴 listview 有多少個條目 */ public int getCount() { // TODO Auto-generated method stub return blackNums.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return null; } @Override public long getItemId(int position) { // TODO Auto-generated method stub return 0; } @Override /** * 返回第一個條目對應的 view , * 當某個 條目 將要顯示在屏幕上時,就會調用getView 方法 ,將該條目建立出來 * @param position 條目的下標 * 新建一個list_item_black_num.xml的條目佈局 */ public View getView(final int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub View view; ViewHolder vh; if (convertView == null) { view = getLayoutInflater().inflate(R.layout.list_item_black_num, null); //建立ViewHolder vh = new ViewHolder(); // 找到 子 view TextView tvNum = (TextView) view.findViewById(R.id.tv_number_list_item); TextView tvMode = (TextView) view.findViewById(R.id.tv_mode_list_item); ImageView ivDelete = (ImageView) view.findViewById(R.id.iv_delete_list_item); // 將子view 打包 vh.tvMode = tvMode; vh.tvNum = tvNum; vh.ivDelete = ivDelete; // 將揹包背在view的身上 view.setTag(vh); } else { view = convertView; //取出揹包 vh = (ViewHolder) convertView.getTag(); } BlackNumBean blackNumBean = blackNums.get(position); //用取出的揹包賦值 vh.tvNum.setText(blackNumBean.number); switch (blackNumBean.mode) { case 0: vh.tvMode.setText("所有攔截"); break; case 1: vh.tvMode.setText("攔截電話"); break; case 2: vh.tvMode.setText("攔截短信"); break; } //刪除黑名單 // 爲ivdelete設置一個點擊事件 vh.ivDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //從數據庫中刪除數據 blackDao.deleteBlackNum(blackNums.get(position).number); //從集合中刪除數據 blackNums.remove(position); //刷新頁面 notifyDataSetChanged(); } }); return view; } } //優化二 先聲明一個臨時的輔助類 而後有幾個子view 聲明幾個成員變量 private static class ViewHolder { public ImageView ivDelete; TextView tvNum; TextView tvMode; } /** * 添加黑名單 * * @param v */ public void addBlackNum(View v) { // 展現天年黑名單的對話框 showAddBlackNumDialog(); } private AlertDialog dialog; private void showAddBlackNumDialog() { //在java代碼中建立對話框 AlertDialog.Builder adb = new AlertDialog.Builder(this); dialog = adb.create(); //將佈局轉換爲view對象 View view = getLayoutInflater().inflate(R.layout.dialog_add_blacknum, null); final EditText etInputNum = (EditText) view.findViewById(R.id.et_input_number); final CheckBox cbStopCall = (CheckBox) view.findViewById(R.id.cb_stop_call); final CheckBox cbStopSms = (CheckBox) view.findViewById(R.id.cb_stop_sms); Button btnCancel = (Button) view.findViewById(R.id.btn_cancel); Button btnOk = (Button) view.findViewById(R.id.btn_ok); //爲兩個按鈕 添加點擊事件 //取消按鈕 btnCancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); //點擊肯定按鈕 btnOk.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //獲取輸入框號碼 String number = etInputNum.getText().toString().trim(); if (TextUtils.isEmpty(number)) { MyUtils.showToast(ctx, "號碼不能爲空"); return; } //先初始化mode int mode = 0; if (cbStopCall.isChecked() && cbStopSms.isChecked()) { mode = 0; } else if (cbStopCall.isChecked() && !cbStopSms.isChecked()) { mode = 1; } else if (!cbStopCall.isChecked() && cbStopSms.isChecked()) { mode = 2; } else { MyUtils.showToast(ctx, "請選擇攔截模式"); return; } //將黑名單信息插入數據庫 blackDao.addBlackNum(number, mode); //顯示至listview頁面 //加入集合 blackNums.add(0, new BlackNumBean(number, mode));//要 將黑名單添加至集合的第一位 //祖傳錯誤 當數據庫中沒有數據 會直接進入黑名單添加數據 可是adapter在這個時候並無建立 因此刷新的時候會顯示空指針異常 //因此要先判斷adapter 是否爲空 if (adapter == null) { adapter = new MyAdapter(); // listView.setAdapter(adapter); } llLoading.setVisibility(View.GONE); adapter.notifyDataSetChanged();//刷新 //關閉對話框 dialog.dismiss(); } }); dialog.setView(view); dialog.show(); } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="mp.zyqj.zz.mobilephone.MainActivity"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView style="@style/tv_title" android:text="黑名單攔截" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="addBlackNum" android:layout_alignParentRight="true" android:layout_margin="5dp" android:text="添加黑" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" > <LinearLayout android:id="@+id/ll_loading" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="#6600ff00" android:gravity="center" android:orientation="vertical" android:padding="8dp" > <ProgressBar android:id="@+id/pb_progressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_desc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="玩命加載中,請稍候..." /> </LinearLayout> <ListView android:id="@+id/li_listView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" > </ListView> </RelativeLayout> </LinearLayout>