基于qwertySearch和T9Search实现的通讯录Demo

  • 先上效果图
通讯录.gif
  • 实现思路

    1. 异步加载联系人
     ```java
    private List<BeanContact> getBaseContactList(Context context) {
      List<BeanContact> kanjiStartBeanContact = new ArrayList<BeanContact>();
      HashMap<String, BeanContact> kanjiStartBeanContactHashMap = new HashMap<String, BeanContact>();
      List<BeanContact> nonKanjiStartBeanContact = new ArrayList<BeanContact>();
      HashMap<String, BeanContact> nonKanjiStartBeanContactHashMap = new HashMap<String, BeanContact>();
      List<BeanContact> beanContactList = new ArrayList<BeanContact>();
      BeanContact cs = null;
      Cursor cursor = null;
      String sortkey = null;
      long startLoadTime = System.currentTimeMillis();
      String[] projection = new String[]{ContactsContract.CommonDataKinds.Phone.CONTACT_ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER};
      try {
          cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, null, null, "sort_key");
          int idColumnIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID);
          int dispalyNameColumnIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
          int numberColumnIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
          while (cursor.moveToNext()) {
              String id = cursor.getString(idColumnIndex);
              String displayName = cursor.getString(dispalyNameColumnIndex);
              String phoneNumber = cursor.getString(numberColumnIndex);
              boolean kanjiStartBeanContactExist = kanjiStartBeanContactHashMap.containsKey(id);
              boolean nonKanjiStartBeanContactExist = nonKanjiStartBeanContactHashMap.containsKey(id);
              if (true == kanjiStartBeanContactExist) {
                  cs = kanjiStartBeanContactHashMap.get(id);
                  cs.addMultipleContact(cs, phoneNumber);
              } else if (true == nonKanjiStartBeanContactExist) {
                  cs = nonKanjiStartBeanContactHashMap.get(id);
                  cs.addMultipleContact(cs, phoneNumber);
              } else {
                  cs = new BeanContact(id, displayName, phoneNumber);
                  PinyinUtil.parse(cs.getmNamePinyinSearchUnit());
                  sortkey = PinyinUtil.getSortKey(cs.getmNamePinyinSearchUnit()).toUpperCase();
                  cs.setmSortKey(praseSortKey(sortkey));
                  boolean isKanji = PinyinUtil.isKanji(cs.getmName().charAt(0));
                  if (true == isKanji) {
                      kanjiStartBeanContactHashMap.put(id, cs);
                  } else {
                      nonKanjiStartBeanContactHashMap.put(id, cs);
                  }
              }
          }
      } catch (Exception e) {
          e.printStackTrace();
      } finally {
          if (null != cursor) {
              cursor.close();
              cursor = null;
          }
      }
      kanjiStartBeanContact.addAll(kanjiStartBeanContactHashMap.values());
      Collections.sort(kanjiStartBeanContact, BeanContact.mAscComparator);
      nonKanjiStartBeanContact.addAll(nonKanjiStartBeanContactHashMap.values());
      Collections.sort(nonKanjiStartBeanContact, BeanContact.mAscComparator);
      beanContactList.addAll(kanjiStartBeanContact);
      //merge nonKanjiStartBeanContact and kanjiStartBeanContact
      int lastIndex = 0;
      boolean shouldBeAdd = false;
      for (int i = 0; i < nonKanjiStartBeanContact.size(); i++) {
          String nonKanfirstLetter = PinyinUtil.getFirstLetter(nonKanjiStartBeanContact.get(i).getmNamePinyinSearchUnit());
          int j = 0;
          for (j = 0 + lastIndex; j < beanContactList.size(); j++) {
              String firstLetter = PinyinUtil.getFirstLetter(beanContactList.get(j).getmNamePinyinSearchUnit());
              lastIndex++;
              if (firstLetter.charAt(0) > nonKanfirstLetter.charAt(0)) {
                  shouldBeAdd = true;
                  break;
              } else {
                  shouldBeAdd = false;
              }
          }
          if (lastIndex >= beanContactList.size()) {
              lastIndex++;
              shouldBeAdd = true;
          }
          if (true == shouldBeAdd) {
              beanContactList.add(j, nonKanjiStartBeanContact.get(i));
              shouldBeAdd = false;
          }
      }
      long endLoadTime = System.currentTimeMillis();
      Log.i(TAG, "endLoadTime-startLoadTime=[" + (endLoadTime - startLoadTime) + "] BeanContact.size()=" + beanContactList.size());
      return beanContactList;
    

    }
    ```
    2 . 解析基础联系人list生成Map用于映射26个英文字母和对应的联系人list,这样就可以实现这种效果用于更精准定位联系人

精准定位联系人.png
 public class ContactIndexHelper {

 private HashMap<String, List<BeanContact>> hashMap;//映射对应字母的联系人集合
 private static ContactIndexHelper helper;

 public HashMap<String, List<BeanContact>> getHashMap() {
     return hashMap;
 }

 public void setHashMap(HashMap<String, List<BeanContact>> hashMap) {
     this.hashMap = hashMap;
 }

 private ContactIndexHelper() {
     hashMap = new HashMap<>();
     for (int i = 0; i < QuickAlphabeticBar.getSelectCharacters().length; i++) {
         hashMap.put(String.valueOf(QuickAlphabeticBar.getSelectCharacters()[i]), new ArrayList<BeanContact>());
     }
 }

 public static ContactIndexHelper getInstance() {
     if (helper == null) {
         helper = new ContactIndexHelper();
     }
     return helper;
 }

 public void parseContact(List<BeanContact> list) {
     for (int i = 0; i < list.size(); i++) {
         hashMap.get(String.valueOf(list.get(i).getmSortKey().charAt(0))).add(list.get(i));
     }
 }
}
//当滑动右侧quickBar的时候根据手指滑动到的字母获取到对应的联系人集合,然后再次进行过滤获取不重复的联系人
public void updateUi(String letter) {
     textView.setText(letter);
     List<BeanContact> tempList = ContactIndexHelper.getInstance().getHashMap().get(letter);//获取对应字母的联系人集合
     List<BeanContact> resultList = new ArrayList<>();
     if (tempList.size() > 0 && tempList.get(0) != null) {
         resultList.add(tempList.get(0));
     }
     for (int i = 1; i < tempList.size(); i++) {//过滤这个联系人list,得到姓不重复的新联系人list
         if (!String.valueOf(tempList.get(i).getmName().charAt(0)).equalsIgnoreCase(String.valueOf(tempList.get(i - 1).getmName().charAt(0)))) {
             resultList.add(tempList.get(i));
         }
     }
     quickAdapter.clear();
     quickAdapter.addAll(resultList);
 }
   ```
3 . 自定义右侧qucikBar
```java
 public class QuickAlphabeticBar extends View {
 private static final String TAG = "QuickAlphabeticBar";
 public static final char DEFAULT_INDEX_CHARACTER = '#';
 private static char[] mSelectCharacters = {DEFAULT_INDEX_CHARACTER, 'A',
         'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
         'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};

 private SectionIndexer mSectionIndexer;
 private ListView mQuickAlphabeticLv;
 private TextView mSelectCharTv;

 private char mCurrentSelectChar;
 private OnQuickAlphabeticBar onQuickAlphabeticBar;

 interface OnQuickAlphabeticBar {
     abstract void onTouchDown();//手指按下

     abstract void onTouchUp();//手指抬起
 }

 public OnQuickAlphabeticBar getOnQuickAlphabeticBar() {
     return onQuickAlphabeticBar;
 }

 public void setOnQuickAlphabeticBar(OnQuickAlphabeticBar onQuickAlphabeticBar) {
     this.onQuickAlphabeticBar = onQuickAlphabeticBar;
 }

 public QuickAlphabeticBar(Context context, AttributeSet attrs) {
     super(context, attrs);
 }

 public static char[] getSelectCharacters() {
     return mSelectCharacters;
 }

 /*public static void setSelectCharacters(char[] mSelectCharacters) {
     QuickAlphabeticBar.mSelectCharacters = mSelectCharacters;
 }*/

 public SectionIndexer getSectionIndexer() {
     return mSectionIndexer;
 }

 public void setSectionIndexer(SectionIndexer sectionIndexer) {
     mSectionIndexer = sectionIndexer;
 }

 public ListView getQuickAlphabeticLv() {
     return mQuickAlphabeticLv;
 }

 public void setQuickAlphabeticLv(ListView quickAlphabeticLv) {
     mQuickAlphabeticLv = quickAlphabeticLv;
 }

 public TextView getSelectCharTv() {
     return mSelectCharTv;
 }

 public void setSelectCharTv(TextView selectCharTv) {
     mSelectCharTv = selectCharTv;
 }

 public char getCurrentSelectChar() {
     return mCurrentSelectChar;
 }

 public void setCurrentSelectChar(char currentSelectChar) {
     mCurrentSelectChar = currentSelectChar;
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
     super.onTouchEvent(event);
     int index = getCurrentIndex(event);
     //Log.i(TAG,"index="+index);
     if ((event.getAction() == MotionEvent.ACTION_DOWN) || (event.getAction() == MotionEvent.ACTION_MOVE)) {
         setBackgroundColor(ContextCompat.getColor(getContext(), R.color.gray));
         //reference: http://blog.csdn.net/jack_l1/article/details/14165291
         if (null != mSectionIndexer) {
             int position = mSectionIndexer.getPositionForSection(mSelectCharacters[index]);
             if (position < 0) {
                 return true;
             }
             if (null != mSelectCharTv) {    //show select char
                 mSelectCharTv.setVisibility(View.VISIBLE);
                 mSelectCharTv.setText(String.valueOf(mSelectCharacters[index]));
             }
             if (null != mQuickAlphabeticLv) {
                 mQuickAlphabeticLv.setSelection(position);
             }
             if (onQuickAlphabeticBar != null) {
                 onQuickAlphabeticBar.onTouchDown();
             }
         }
     } else if (event.getAction() == MotionEvent.ACTION_UP) {
         setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
         if (null != mSelectCharTv) {    //hide select char
             mSelectCharTv.setVisibility(View.GONE);
         }
         if (onQuickAlphabeticBar != null) {
             onQuickAlphabeticBar.onTouchUp();
         }
     }
     return true;
 }

 @Override
 protected void onDraw(Canvas canvas) {
     float xPos = getMeasuredWidth() / 2;
     float yPos = 0;
     if (mSelectCharacters.length > 0) {
         float sigleHeight = getMeasuredHeight() / mSelectCharacters.length;
         for (int i = 0; i < mSelectCharacters.length; i++) {
             yPos = (i + 1) * sigleHeight;
             if (mSelectCharacters[i] == mCurrentSelectChar) {
                 RectF rectF = new RectF(xPos - sigleHeight / 3, yPos - sigleHeight / 3, xPos + sigleHeight / 3, yPos + sigleHeight / 3);
                 canvas.drawRect(rectF, mBluePaint);
                 Paint.FontMetricsInt fontMetrics = mCurrentIndexPaint.getFontMetricsInt();
                 // 转载请注明出处:http://blog.csdn.net/hursing
                 int baseline = ((int) rectF.bottom + (int) rectF.top - fontMetrics.bottom - fontMetrics.top) / 2;//让文字竖直居中可以看下textview源码
                 canvas.drawText(String.valueOf(mSelectCharacters[i]), rectF.centerX(), baseline, mCurrentIndexPaint);
             } else {
                 RectF rectF = new RectF(xPos - sigleHeight / 3, yPos - sigleHeight / 3, xPos + sigleHeight / 3, yPos + sigleHeight / 3);
                 Paint.FontMetricsInt fontMetrics = mCurrentIndexPaint.getFontMetricsInt();
                 int baseline = ((int) rectF.bottom + (int) rectF.top - fontMetrics.bottom - fontMetrics.top) / 2;
                 canvas.drawText(String.valueOf(mSelectCharacters[i]), rectF.centerX(), baseline, mOtherIndexPaint);
             }
         }
     }
     super.onDraw(canvas);
 }

 private int getCurrentIndex(MotionEvent event) {
     if (null == event) {
         return 0;
     }

     int y = (int) event.getY();
     int index = y / (getMeasuredHeight() / mSelectCharacters.length);
     if (index < 0) {
         index = 0;
     } else if (index >= mSelectCharacters.length) {
         index = mSelectCharacters.length - 1;
     }

     return index;
 }

 private Paint mCurrentIndexPaint = new Paint();

 {
     mCurrentIndexPaint.setColor(Color.WHITE);
     mCurrentIndexPaint.setTextSize(24);
     mCurrentIndexPaint.setTypeface(Typeface.DEFAULT_BOLD);
     mCurrentIndexPaint.setTextAlign(Paint.Align.CENTER);
 }

 private Paint mBluePaint = new Paint();

 {
     mBluePaint.setColor(ContextCompat.getColor(getContext(), R.color.blue2));
 }

 private Paint mOtherIndexPaint = new Paint();

 {
     mOtherIndexPaint.setColor(Color.BLACK);
     mOtherIndexPaint.setTextSize(24);
     mOtherIndexPaint.setTypeface(Typeface.DEFAULT_BOLD);
     mOtherIndexPaint.setTextAlign(Paint.Align.CENTER);
   }
   }

4 .qwertySearch和T9Search 分别用于联系人页的搜索和拨号页的拨号盘搜索

         public List<BeanContact> qwertySearch(String keyword) {
           List<BeanContact> mSearchContacts = new ArrayList<BeanContact>();
           if (TextUtils.isEmpty(keyword)) {// add all base data to search
           for (int i = 0; i < mBaseContactList.size(); i++) {
             BeanContact currentContacts = null;
             for (currentContacts = mBaseContactList.get(i); null != currentContacts; currentContacts                   = currentContacts.getmNextContacts()) {
                 currentContacts.setSearchByType(BeanContact.SearchByType.SearchByNull);
                 currentContacts.setmMatchKeywords("");
                 if (true == currentContacts.ismFirstMultipleContacts()) {
                     mSearchContacts.add(currentContacts);
                 } else {
                     if (false == currentContacts.ismHideMultipleContacts()) {
                         mSearchContacts.add(currentContacts);
                     }
                 }
             }
         }
         return mSearchContacts;
         }
         int contactsCount = mBaseContactList.size();
         /**
          * search process: 1:Search by name (1)Search by original name (2)Search
          * by name pinyin characters(original name->name pinyin characters)
          * 2:Search by phone number
          */
         for (int i = 0; i < contactsCount; i++) {
             PinyinSearchUnit namePinyinSearchUnit = mBaseContactList.get(i).getmNamePinyinSearchUnit();
             if (true == QwertyUtil.match(namePinyinSearchUnit, keyword)) {// search by name;
             BeanContact currentContacts = null;
             currentContacts = mBaseContactList.get(i);
             currentContacts.setSearchByType(BeanContact.SearchByType.SearchByName);
             currentContacts.setmMatchKeywords(namePinyinSearchUnit.getMatchKeyword().toString());
             mSearchContacts.add(currentContacts);
             continue;
             } else {
             BeanContact currentContacts = null;
             for (currentContacts = mBaseContactList.get(i); null != currentContacts; currentContacts = currentContacts.getmNextContacts()) {
                 if (currentContacts.getmPhoneNumber().contains(keyword)) {// search by phone number
                     currentContacts.setSearchByType(BeanContact.SearchByType.SearchByPhoneNumber);
                     currentContacts.setmMatchKeywords(keyword);
                     mSearchContacts.add(currentContacts);
                 }
             }
             continue;
           }
         }
         if (mSearchContacts.size() <= 0) {
         } else {
         Collections.sort(mSearchContacts, BeanContact.mAscComparator);
         }
         return mSearchContacts;
         }

    public List<BeanContact> t9InputSearch(String keyword) {
       List<BeanContact> mSearchByNameContacts = new ArrayList<BeanContact>();
       List<BeanContact> mSearchByPhoneNumberContacts = new ArrayList<BeanContact>();
       List<BeanContact> mSearchContacts = new ArrayList<BeanContact>();
       if (null == keyword) {// add all base data to search
         return mSearchContacts;
       }
       int contactsCount = mBaseContactList.size();

     /**
      * search process: 1:Search by name (1)Search by name pinyin
      * characters(org name->name pinyin characters) ('0'~'9','*','#')
      * (2)Search by org name ('0'~'9','*','#') 2:Search by phone number
      * ('0'~'9','*','#')
      */
     for (int i = 0; i < contactsCount; i++) {
         PinyinSearchUnit namePinyinSearchUnit = mBaseContactList.get(i).getmNamePinyinSearchUnit();
         if (true == T9Util.match(namePinyinSearchUnit, keyword)) {// search by name;
             BeanContact currentContacts = null;
             currentContacts = mBaseContactList.get(i);
//                currentContacts.setSearchByType(BeanContact.SearchByType.SearchByName);
             currentContacts.setmMatchT9Keywords(namePinyinSearchUnit.getMatchKeyword().toString());
             mSearchByNameContacts.add(currentContacts);
//                BeanContact firstContacts = null;
//                for (currentContacts = mBaseContactList.get(i), firstContacts = currentContacts; null != currentContacts; currentContacts = currentContacts.getmNextContacts()) {
//                    currentContacts.setSearchByType(BeanContact.SearchByType.SearchByName);
//                    currentContacts.setmMatchKeywords(namePinyinSearchUnit.getMatchKeyword().toString());
////                    currentContacts.setMatchStartIndex(firstContacts.getName().indexOf(firstContacts.getMatchKeywords().toString()));
////                    currentContacts.setMatchLength(firstContacts.getMatchKeywords().length());
//                    mSearchByNameContacts.add(currentContacts);
//                }

             continue;
         } else {
             BeanContact currentContacts = null;
             for (currentContacts = mBaseContactList.get(i); null != currentContacts; currentContacts = currentContacts.getmNextContacts()) {
                 if (currentContacts.getmPhoneNumber().contains(keyword)) {// search by phone number
//                        currentContacts.setSearchByType(BeanContact.SearchByType.SearchByPhoneNumber);
                     currentContacts.setmMatchT9Keywords(keyword);
//                        currentContacts.setMatchStartIndex(currentContacts.getPhoneNumber().indexOf(keyword));
//                        currentContacts.setMatchLength(keyword.length());
                     mSearchByPhoneNumberContacts.add(currentContacts);
                 }
             }
             continue;
         }
     }
     if (mSearchByNameContacts.size() > 0) {
         Collections.sort(mSearchByNameContacts, BeanContact.mAscComparator);
     }
     if (mSearchByPhoneNumberContacts.size() > 0) {
         Collections.sort(mSearchByPhoneNumberContacts, BeanContact.mAscComparator);
     }
     mSearchContacts.clear();
     mSearchContacts.addAll(mSearchByNameContacts);
     mSearchContacts.addAll(mSearchByPhoneNumberContacts);
     return mSearchContacts;
 }
      ```
5 . 通话记录获取
```java
public class CallLogHelper extends ContextWrapper {

 private SimpleDateFormat format;

 public List<BeanCallLog> getCalllogList(BaseFragment context) {
     HashMap<String, BeanCallLog> map = new HashMap<>();
     Numbercity nc = new Numbercity();
     List<BeanCallLog> list_bCallLogs = new ArrayList<BeanCallLog>();
     String[] projection = {CallLog.Calls.DATE, // 通话时间
             CallLog.Calls.NUMBER,// 电话号码
             CallLog.Calls.TYPE, // 通话类型,主拨,被叫,未接
             CallLog.Calls.CACHED_NAME,// 显示名称
             CallLog.Calls.DURATION, // 通话时长
             CallLog.Calls._ID}; // 查询的列
     if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
         context.requestPermissions(new String[]{Manifest.permission.READ_CALL_LOG}, CallFragment.READ_CALL_LOG_REQUEST_CODE);
         return list_bCallLogs;
     }
     Cursor cursor = getContentResolver().query(CallLog.Calls.CONTENT_URI, projection, null,
             null, CallLog.Calls.DEFAULT_SORT_ORDER);
     int id;
     String name;// 名称
     String number;// 号码
     String date_str;// 日期
     int type;// 来电:1,拨出:2,未接:3
     int duration; // 通话时长
     while (cursor.moveToNext()) {
         Date date = new Date(cursor.getLong(cursor
                 .getColumnIndex(CallLog.Calls.DATE)));
         number = cursor.getString(cursor
                 .getColumnIndex(CallLog.Calls.NUMBER));
         type = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.TYPE));
         name = cursor.getString(cursor
                 .getColumnIndex(CallLog.Calls.CACHED_NAME));// 缓存的名称与电话号码,如果它的存在
         id = cursor.getInt(cursor.getColumnIndex(CallLog.Calls._ID));
         duration = cursor.getInt(cursor
                 .getColumnIndex(CallLog.Calls.DURATION));
         date_str = format.format(date);
         BeanCallLog clb = new BeanCallLog();
         clb.setId(id);
         if (name == null) {
             name = "";
         }
         clb.setName(name);
         clb.setNumber(number);
         clb.setDate(date);
         clb.setType(type);
         clb.setDuration(duration);
         clb.setCount(1);
         clb.setPlace(nc.getCityByNum(number));
         if (!number.equals("-1")) {// 未知的号码不加入通话记录里面
             if (map.containsKey(clb.getName() + clb.getType() + date_str)) {
                 BeanCallLog beanCallLog = map.get(clb.getName() + clb.getType() + date_str);
                 int count = beanCallLog.getCount() + 1;
                 beanCallLog.setCount(count);
             } else {
                 map.put(clb.getName() + clb.getType() + date_str, clb);
             }
         }
     }
     cursor.close();
     nc.close();
     list_bCallLogs.addAll(map.values());
     Collections.sort(list_bCallLogs, BeanCallLog.mDescComparator);
     return list_bCallLogs;
 }

 public CallLogHelper(Context base) {
     super(base);
     format = new SimpleDateFormat("MM-dd");
 }
}

        ```
参考资料:
https://github.com/handsomezhou/PinyinSearchLibrary
Demo源码:
https://github.com/luohaiah/ContactDemo
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,723评论 0 33
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,572评论 18 399
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,357评论 0 17
  • 启动通讯录页面获取用户点击的 item 的数据(姓名、手机号) //启动通讯录页面 startActivityFo...
    ff_hh阅读 2,210评论 0 0
  • 很久没有打开笔记本,以至于刚打开时居然有点生疏感。像是在街角不经意地遇见故人,想寒暄几句,竟是如此不尽人意,匆促而...
    雨中葳蕤阅读 584评论 2 5