亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android系統聯系人全特效實現(上)分組導航和擠壓動畫(附源碼)

2020-04-11 12:14:13
字體:
來源:轉載
供稿:網友
記得在我剛接觸Android的時候對系統聯系人中的特效很感興趣,它會根據手機中聯系人姓氏的首字母進行分組,并在界面的最頂端始終顯示一個當前的分組。如下圖所示:
 
最讓我感興趣的是,當后一個分組和前一個分組相碰時,會產生一個上頂的擠壓動畫。那個時候我思考了各種方法想去實現這種特效,可是限于功夫不到家,都未能成功。如今兩年多過去了,自己也成長了很多,再回頭去想想這個功能,突然發現已經有了思路,于是立刻記錄下來與大家分享。

首先講一下需要提前了解的知識點,這里我們最需要用到的就是SectionIndexer,它能夠有效地幫助我們對分組進行控制。由于SectionIndexer是一個接口,你可以自定義一個子類來實現SectionIndexer,不過自己再寫一個SectionIndexer的實現太麻煩了,這里我們直接使用Android提供好的實現AlphabetIndexer,用它來實現聯系人分組功能已經足夠了。

AlphabetIndexer的構造函數需要傳入三個參數,第一個參數是cursor,第二個參數是sortedColumnIndex整型,第三個參數是alphabet字符串。其中cursor就是把我們從數據庫中查出的游標傳進去,sortedColumnIndex就是指明我們是使用哪一列進行排序的,而alphabet則是指定字母表排序規則,比如:"ABCDEFGHIJKLMNOPQRSTUVWXYZ"。有了AlphabetIndexer,我們就可以通過它的getPositionForSection和getSectionForPosition方法,找出當前位置所在的分組,和當前分組所在的位置,從而實現類似于系統聯系人的分組導航和擠壓動畫效果,關于AlphabetIndexer更詳細的詳解,請參考官方文檔。
那么我們應該怎樣對聯系人進行排序呢?前面也提到過,有一個sortedColumnIndex參數,這個sortedColumn到底在哪里呢?我們來看一下系統聯系人的raw_contacts這張表(/data/data/com.android.providers.contacts/databases/contacts2.db),這個表結構比較復雜,里面有二十多個列,其中有一列名叫sort_key,這就是我們要找的了!如下圖所示:
 
可以看到,這一列非常人性化地幫我們記錄了漢字所對應的拼音,這樣我們就可以通過這一列的值輕松為聯系人進行排序了。
下面我們就來開始實現,新建一個Android項目,命名為ContactsDemo。首先我們還是先來完成布局文件,打開或新建activity_main.xml作為程序的主布局文件,在里面加入如下代碼:
復制代碼 代碼如下:

<RelativeLayout 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" >
<ListView
android:id="@+id/contacts_list_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:fadingEdge="none" >
</ListView>
<LinearLayout
android:id="@+id/title_layout"
android:layout_width="fill_parent"
android:layout_height="18dip"
android:layout_alignParentTop="true"
android:background="#303030" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="10dip"
android:textColor="#ffffff"
android:textSize="13sp" />
</LinearLayout>
</RelativeLayout>

布局文件很簡單,里面放入了一個ListView,用于展示聯系人信息。另外還在頭部放了一個LinearLayout,里面包含了一個TextView,它的作用是在界面頭部始終顯示一個當前分組。
然后新建一個contact_item.xml的布局,這個布局用于在ListView中的每一行進行填充,代碼如下:
復制代碼 代碼如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/sort_key_layout"
android:layout_width="fill_parent"
android:layout_height="18dip"
android:background="#303030" >
<TextView
android:id="@+id/sort_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="10dip"
android:textColor="#ffffff"
android:textSize="13sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/name_layout"
android:layout_width="fill_parent"
android:layout_height="50dip" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:src="@drawable/icon" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="#ffffff"
android:textSize="22sp" />
</LinearLayout>
</LinearLayout>

在這個布局文件中,首先是放入了一個和前面完成一樣的分組布局,因為不僅界面頭部需要展示分組,在每個分組內的第一個無素之前都需要展示分組布局。然后是加入一個簡單的LinearLayout,里面包含了一個ImageView用于顯示聯系人頭像,還包含一個TextView用于顯示聯系人姓名。
這樣我們的布局文件就全部寫完了,下面開始來真正地實現功能。
先從簡單的開始,新建一個Contact實體類:
復制代碼 代碼如下:

public class Contact {
/**
* 聯系人姓名
*/
private String name;
/**
* 排序字母
*/
private String sortKey;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSortKey() {
return sortKey;
}
public void setSortKey(String sortKey) {
this.sortKey = sortKey;
}
}

這個實體類很簡單,只包含了聯系人姓名和排序鍵。
接下來完成聯系人列表適配器的編寫,新建一個ContactAdapter類繼承自ArrayAdapter,加入如下代碼:
復制代碼 代碼如下:

public class ContactAdapter extends ArrayAdapter<Contact> {
/**
* 需要渲染的item布局文件
*/
private int resource;
/**
* 字母表分組工具
*/
private SectionIndexer mIndexer;
public ContactAdapter(Context context, int textViewResourceId, List<Contact> objects) {
super(context, textViewResourceId, objects);
resource = textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Contact contact = getItem(position);
LinearLayout layout = null;
if (convertView == null) {
layout = (LinearLayout) LayoutInflater.from(getContext()).inflate(resource, null);
} else {
layout = (LinearLayout) convertView;
}
TextView name = (TextView) layout.findViewById(R.id.name);
LinearLayout sortKeyLayout = (LinearLayout) layout.findViewById(R.id.sort_key_layout);
TextView sortKey = (TextView) layout.findViewById(R.id.sort_key);
name.setText(contact.getName());
int section = mIndexer.getSectionForPosition(position);
if (position == mIndexer.getPositionForSection(section)) {
sortKey.setText(contact.getSortKey());
sortKeyLayout.setVisibility(View.VISIBLE);
} else {
sortKeyLayout.setVisibility(View.GONE);
}
return layout;
}
/**
* 給當前適配器傳入一個分組工具。
*
* @param indexer
*/
public void setIndexer(SectionIndexer indexer) {
mIndexer = indexer;
}
}

上面的代碼中,最重要的就是getView方法,在這個方法中,我們使用SectionIndexer的getSectionForPosition方法,通過當前的position值拿到了對應的section值,然后再反向通過剛剛拿到的section值,調用getPositionForSection方法,取回新的position值。如果當前的position值和新的position值是相等的,那么我們就可以認為當前position的項是某個分組下的第一個元素,我們應該將分組布局顯示出來,而其它的情況就應該將分組布局隱藏。
最后我們來編寫程序的主界面,打開或新建MainActivity作為程序的主界面,代碼如下所示:
復制代碼 代碼如下:

public class MainActivity extends Activity {
/**
* 分組的布局
*/
private LinearLayout titleLayout;
/**
* 分組上顯示的字母
*/
private TextView title;
/**
* 聯系人ListView
*/
private ListView contactsListView;
/**
* 聯系人列表適配器
*/
private ContactAdapter adapter;
/**
* 用于進行字母表分組
*/
private AlphabetIndexer indexer;
/**
* 存儲所有手機中的聯系人
*/
private List<Contact> contacts = new ArrayList<Contact>();
/**
* 定義字母表的排序規則
*/
private String alphabet = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* 上次第一個可見元素,用于滾動時記錄標識。
*/
private int lastFirstVisibleItem = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adapter = new ContactAdapter(this, R.layout.contact_item, contacts);
titleLayout = (LinearLayout) findViewById(R.id.title_layout);
title = (TextView) findViewById(R.id.title);
contactsListView = (ListView) findViewById(R.id.contacts_list_view);
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
Cursor cursor = getContentResolver().query(uri,
new String[] { "display_name", "sort_key" }, null, null, "sort_key");
if (cursor.moveToFirst()) {
do {
String name = cursor.getString(0);
String sortKey = getSortKey(cursor.getString(1));
Contact contact = new Contact();
contact.setName(name);
contact.setSortKey(sortKey);
contacts.add(contact);
} while (cursor.moveToNext());
}
startManagingCursor(cursor);
indexer = new AlphabetIndexer(cursor, 1, alphabet);
adapter.setIndexer(indexer);
if (contacts.size() > 0) {
setupContactsListView();
}
}
/**
* 為聯系人ListView設置監聽事件,根據當前的滑動狀態來改變分組的顯示位置,從而實現擠壓動畫的效果。
*/
private void setupContactsListView() {
contactsListView.setAdapter(adapter);
contactsListView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
int section = indexer.getSectionForPosition(firstVisibleItem);
int nextSecPosition = indexer.getPositionForSection(section + 1);
if (firstVisibleItem != lastFirstVisibleItem) {
MarginLayoutParams params = (MarginLayoutParams) titleLayout.getLayoutParams();
params.topMargin = 0;
titleLayout.setLayoutParams(params);
title.setText(String.valueOf(alphabet.charAt(section)));
}
if (nextSecPosition == firstVisibleItem + 1) {
View childView = view.getChildAt(0);
if (childView != null) {
int titleHeight = titleLayout.getHeight();
int bottom = childView.getBottom();
MarginLayoutParams params = (MarginLayoutParams) titleLayout
.getLayoutParams();
if (bottom < titleHeight) {
float pushedDistance = bottom - titleHeight;
params.topMargin = (int) pushedDistance;
titleLayout.setLayoutParams(params);
} else {
if (params.topMargin != 0) {
params.topMargin = 0;
titleLayout.setLayoutParams(params);
}
}
}
}
lastFirstVisibleItem = firstVisibleItem;
}
});
}
/**
* 獲取sort key的首個字符,如果是英文字母就直接返回,否則返回#。
*
* @param sortKeyString
* 數據庫中讀取出的sort key
* @return 英文字母或者#
*/
private String getSortKey(String sortKeyString) {
String key = sortKeyString.substring(0, 1).toUpperCase();
if (key.matches("[A-Z]")) {
return key;
}
return "#";
}
}

可以看到,在onCreate方法中,我們從系統聯系人數據庫中去查詢聯系人的姓名和排序鍵,之后將查詢返回的cursor直接傳入AlphabetIndexer作為第一個參數。由于我們一共就查了兩列,排序鍵在第二列,所以我們第二個sortedColumnIndex參數傳入1。第三個alphabet參數這里傳入了"#ABCDEFGHIJKLMNOPQRSTUVWXYZ"字符串,因為可能有些聯系人的姓名不在字母表范圍內,我們統一用#來表示這部分聯系人。

然后我們在setupContactsListView方法中監聽了ListView的滾動,在onScroll方法中通過getSectionForPosition方法獲取第一個可見元素的分組值,然后給該分組值加1,再通過getPositionForSection方法或者到下一個分組中的第一個元素,如果下個分組的第一個元素值等于第一個可見元素的值加1,那就說明下個分組的布局要和界面頂部分組布局相碰了。之后再通過ListView的getChildAt(0)方法,獲取到界面上顯示的第一個子View,再用view.getBottom獲取底部距離父窗口的位置,對比分組布局的高度來對頂部分組布局進行縱向偏移,就可以實現擠壓動畫的效果了。

最后給出AndroidManifest.xml的代碼,由于要讀取手機聯系人,因此需要加上android.permission.READ_CONTACTS的聲明:
復制代碼 代碼如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.contactsdemo"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="8" />
<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar"
>
<activity
android:name="com.example.contactsdemo.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

現在我們來運行一下程序,效果如下圖所示:
 
目前的話,分組導航和擠壓動畫效果都已經完成了,看起來感覺還是挺不錯的,下一篇文章我會帶領大家繼續完善這個程序,加入字母表快速滾動功能。

好了,今天的講解到此結束,有疑問的朋友請在下面留言。
源碼下載,請點擊這里
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
成人欧美一区二区三区在线湿哒哒| 国产精品亚洲第一区| 在线免费观看羞羞视频一区二区| 日韩av手机在线看| 久久久久久久久久久成人| 成人午夜在线影院| 社区色欧美激情 | 国产一区二区黑人欧美xxxx| 欧美日韩亚洲成人| 精品女厕一区二区三区| 日本最新高清不卡中文字幕| www亚洲欧美| 日韩精品在线免费观看视频| 亚洲视屏在线播放| 亚洲精品一区中文| 亚洲天堂av电影| 国产精品日韩欧美大师| 日韩精品视频在线| 欧美精品精品精品精品免费| 日韩高清有码在线| 91热精品视频| 国产精彩精品视频| 亚洲开心激情网| 欧美日韩精品在线观看| 久久久这里只有精品视频| 亚洲娇小xxxx欧美娇小| 国产精品久久久久999| 成人h视频在线观看播放| 久久久精品国产网站| 伊人一区二区三区久久精品| 7m第一福利500精品视频| 九九热99久久久国产盗摄| 亚洲欧美日韩精品久久奇米色影视| 精品国产91久久久| 国产亚洲精品成人av久久ww| 日韩国产中文字幕| 奇门遁甲1982国语版免费观看高清| 日韩电影免费观看中文字幕| 欧美日韩福利在线观看| 欧美午夜精品在线| 国产精品第2页| 成人xxxxx| 欧美国产日韩一区二区在线观看| 成人欧美一区二区三区在线| 国产日韩精品在线播放| 在线视频免费一区二区| 色播久久人人爽人人爽人人片视av| 日韩av片永久免费网站| 亚洲精品一区二区三区不| 亚洲国产女人aaa毛片在线| 国产亚洲欧美视频| 日本免费一区二区三区视频观看| 日本免费久久高清视频| 久久精品国产99国产精品澳门| 欧美性xxxxx极品娇小| 国产精品永久免费视频| 亚洲一区二区三区xxx视频| 久久九九国产精品怡红院| 国产精品高清在线观看| 色悠悠国产精品| 亚洲国产高清高潮精品美女| 亚洲第一国产精品| 91社区国产高清| 97精品免费视频| 一区二区三区视频在线| 国外成人免费在线播放| 久久精品国产免费观看| 日韩精品欧美国产精品忘忧草| 97香蕉久久超级碰碰高清版| 久久久av网站| 国产亚洲精品久久| 欧洲日本亚洲国产区| 亚洲色图15p| 97在线观看视频国产| 国产一区二区日韩精品欧美精品| 亚洲男人7777| 亚洲精品国产拍免费91在线| 在线亚洲国产精品网| 日韩欧美主播在线| 亚洲午夜精品久久久久久性色| 国产精品第1页| 国产精品1区2区在线观看| 国产精品久久久久av免费| 欧美精品久久久久久久| 精品福利在线观看| 亚洲人精品午夜在线观看| 欧美有码在线视频| 亚洲国产成人一区| 不卡av电影在线观看| 中文字幕亚洲欧美日韩2019| 亚洲999一在线观看www| 久久久亚洲精品视频| 久久亚洲欧美日韩精品专区| 亚洲护士老师的毛茸茸最新章节| 欧美极品少妇全裸体| 日韩欧美在线视频日韩欧美在线视频| 久久久爽爽爽美女图片| 久久久久久久久久久国产| 亚洲福利在线观看| 欧美多人乱p欧美4p久久| 国产精品成人va在线观看| 日韩欧美国产骚| 97视频免费看| 国产热re99久久6国产精品| 丝袜美腿亚洲一区二区| 亚洲精品视频在线观看视频| 亚洲男人的天堂网站| 色综合天天综合网国产成人网| 国产欧美精品久久久| 欧美激情视频在线| 777777777亚洲妇女| 久久精品国产成人精品| 日韩在线观看视频免费| 欧美激情精品久久久| 欧美性生活大片免费观看网址| 国产精品7m视频| 亚洲一区二区久久久久久| 国产欧美在线看| 国产亚洲一区二区精品| 日本国产一区二区三区| 狠狠色狠狠色综合日日小说| 国产精品久久久久久久天堂| 成人免费福利视频| 日韩精品中文字| 亚洲精品999| 亚洲第一视频网站| 日韩精品在线观看网站| 高清一区二区三区四区五区| 精品国产乱码久久久久久虫虫漫画| 欧美三级欧美成人高清www| 亚洲欧美国产一本综合首页| 伊人久久免费视频| 日韩av高清不卡| 国产亚洲一级高清| 欧美做爰性生交视频| 欧美网站在线观看| 亚洲a∨日韩av高清在线观看| 精品欧美国产一区二区三区| 欧美精品国产精品日韩精品| 91av在线免费观看视频| 国产mv免费观看入口亚洲| 91精品国产高清久久久久久91| 久久天天躁狠狠躁夜夜av| 日韩免费看的电影电视剧大全| 亚洲免费av网址| 亚洲午夜小视频| 国产精品日韩在线播放| 性视频1819p久久| xxx成人少妇69| 成人午夜黄色影院| 亚洲欧美在线一区二区| 亚洲天堂av在线免费观看| 久久精品在线播放| 91福利视频网| 91国语精品自产拍在线观看性色| 91精品视频免费观看| 国产精品成人在线| 亚洲一区二区三区乱码aⅴ蜜桃女| 欧美日韩国产在线播放| 亚洲a∨日韩av高清在线观看| 久热精品视频在线免费观看| 国内精品久久久久久久久| 国产精品视频26uuu| 狠狠做深爱婷婷久久综合一区|