欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android系統(tǒng)聯(lián)系人全特效實(shí)現(xiàn)(上)分組導(dǎo)航和擠壓動(dòng)畫(huà)(附源碼)

 更新時(shí)間:2013年06月08日 11:03:09   作者:  
本文將為大家講解下Android系統(tǒng)聯(lián)系人全特效實(shí)現(xiàn)之分組導(dǎo)航和擠壓動(dòng)畫(huà),具體實(shí)現(xiàn)及源代碼如下,感興趣的朋友可以參考下哈,希望對(duì)大家學(xué)習(xí)有所幫助
記得在我剛接觸Android的時(shí)候?qū)ο到y(tǒng)聯(lián)系人中的特效很感興趣,它會(huì)根據(jù)手機(jī)中聯(lián)系人姓氏的首字母進(jìn)行分組,并在界面的最頂端始終顯示一個(gè)當(dāng)前的分組。如下圖所示:
 
最讓我感興趣的是,當(dāng)后一個(gè)分組和前一個(gè)分組相碰時(shí),會(huì)產(chǎn)生一個(gè)上頂?shù)臄D壓動(dòng)畫(huà)。那個(gè)時(shí)候我思考了各種方法想去實(shí)現(xiàn)這種特效,可是限于功夫不到家,都未能成功。如今兩年多過(guò)去了,自己也成長(zhǎng)了很多,再回頭去想想這個(gè)功能,突然發(fā)現(xiàn)已經(jīng)有了思路,于是立刻記錄下來(lái)與大家分享。

首先講一下需要提前了解的知識(shí)點(diǎn),這里我們最需要用到的就是SectionIndexer,它能夠有效地幫助我們對(duì)分組進(jìn)行控制。由于SectionIndexer是一個(gè)接口,你可以自定義一個(gè)子類來(lái)實(shí)現(xiàn)SectionIndexer,不過(guò)自己再寫(xiě)一個(gè)SectionIndexer的實(shí)現(xiàn)太麻煩了,這里我們直接使用Android提供好的實(shí)現(xiàn)AlphabetIndexer,用它來(lái)實(shí)現(xiàn)聯(lián)系人分組功能已經(jīng)足夠了。

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

<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>

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

<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>

在這個(gè)布局文件中,首先是放入了一個(gè)和前面完成一樣的分組布局,因?yàn)椴粌H界面頭部需要展示分組,在每個(gè)分組內(nèi)的第一個(gè)無(wú)素之前都需要展示分組布局。然后是加入一個(gè)簡(jiǎn)單的LinearLayout,里面包含了一個(gè)ImageView用于顯示聯(lián)系人頭像,還包含一個(gè)TextView用于顯示聯(lián)系人姓名。
這樣我們的布局文件就全部寫(xiě)完了,下面開(kāi)始來(lái)真正地實(shí)現(xiàn)功能。
先從簡(jiǎn)單的開(kāi)始,新建一個(gè)Contact實(shí)體類:
復(fù)制代碼 代碼如下:

public class Contact {
/**
* 聯(lián)系人姓名
*/
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;
}
}

這個(gè)實(shí)體類很簡(jiǎn)單,只包含了聯(lián)系人姓名和排序鍵。
接下來(lái)完成聯(lián)系人列表適配器的編寫(xiě),新建一個(gè)ContactAdapter類繼承自ArrayAdapter,加入如下代碼:
復(fù)制代碼 代碼如下:

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;
}
/**
* 給當(dāng)前適配器傳入一個(gè)分組工具。
*
* @param indexer
*/
public void setIndexer(SectionIndexer indexer) {
mIndexer = indexer;
}
}

上面的代碼中,最重要的就是getView方法,在這個(gè)方法中,我們使用SectionIndexer的getSectionForPosition方法,通過(guò)當(dāng)前的position值拿到了對(duì)應(yīng)的section值,然后再反向通過(guò)剛剛拿到的section值,調(diào)用getPositionForSection方法,取回新的position值。如果當(dāng)前的position值和新的position值是相等的,那么我們就可以認(rèn)為當(dāng)前position的項(xiàng)是某個(gè)分組下的第一個(gè)元素,我們應(yīng)該將分組布局顯示出來(lái),而其它的情況就應(yīng)該將分組布局隱藏。
最后我們來(lái)編寫(xiě)程序的主界面,打開(kāi)或新建MainActivity作為程序的主界面,代碼如下所示:
復(fù)制代碼 代碼如下:

public class MainActivity extends Activity {
/**
* 分組的布局
*/
private LinearLayout titleLayout;
/**
* 分組上顯示的字母
*/
private TextView title;
/**
* 聯(lián)系人ListView
*/
private ListView contactsListView;
/**
* 聯(lián)系人列表適配器
*/
private ContactAdapter adapter;
/**
* 用于進(jìn)行字母表分組
*/
private AlphabetIndexer indexer;
/**
* 存儲(chǔ)所有手機(jī)中的聯(lián)系人
*/
private List<Contact> contacts = new ArrayList<Contact>();
/**
* 定義字母表的排序規(guī)則
*/
private String alphabet = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* 上次第一個(gè)可見(jiàn)元素,用于滾動(dòng)時(shí)記錄標(biāo)識(shí)。
*/
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();
}
}
/**
* 為聯(lián)系人ListView設(shè)置監(jiān)聽(tīng)事件,根據(jù)當(dāng)前的滑動(dòng)狀態(tài)來(lái)改變分組的顯示位置,從而實(shí)現(xiàn)擠壓動(dòng)畫(huà)的效果。
*/
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的首個(gè)字符,如果是英文字母就直接返回,否則返回#。
*
* @param sortKeyString
* 數(shù)據(jù)庫(kù)中讀取出的sort key
* @return 英文字母或者#
*/
private String getSortKey(String sortKeyString) {
String key = sortKeyString.substring(0, 1).toUpperCase();
if (key.matches("[A-Z]")) {
return key;
}
return "#";
}
}

可以看到,在onCreate方法中,我們從系統(tǒng)聯(lián)系人數(shù)據(jù)庫(kù)中去查詢聯(lián)系人的姓名和排序鍵,之后將查詢返回的cursor直接傳入AlphabetIndexer作為第一個(gè)參數(shù)。由于我們一共就查了兩列,排序鍵在第二列,所以我們第二個(gè)sortedColumnIndex參數(shù)傳入1。第三個(gè)alphabet參數(shù)這里傳入了"#ABCDEFGHIJKLMNOPQRSTUVWXYZ"字符串,因?yàn)榭赡苡行┞?lián)系人的姓名不在字母表范圍內(nèi),我們統(tǒng)一用#來(lái)表示這部分聯(lián)系人。

然后我們?cè)趕etupContactsListView方法中監(jiān)聽(tīng)了ListView的滾動(dòng),在onScroll方法中通過(guò)getSectionForPosition方法獲取第一個(gè)可見(jiàn)元素的分組值,然后給該分組值加1,再通過(guò)getPositionForSection方法或者到下一個(gè)分組中的第一個(gè)元素,如果下個(gè)分組的第一個(gè)元素值等于第一個(gè)可見(jiàn)元素的值加1,那就說(shuō)明下個(gè)分組的布局要和界面頂部分組布局相碰了。之后再通過(guò)ListView的getChildAt(0)方法,獲取到界面上顯示的第一個(gè)子View,再用view.getBottom獲取底部距離父窗口的位置,對(duì)比分組布局的高度來(lái)對(duì)頂部分組布局進(jìn)行縱向偏移,就可以實(shí)現(xiàn)擠壓動(dòng)畫(huà)的效果了。

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

<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>

現(xiàn)在我們來(lái)運(yùn)行一下程序,效果如下圖所示:
 
目前的話,分組導(dǎo)航和擠壓動(dòng)畫(huà)效果都已經(jīng)完成了,看起來(lái)感覺(jué)還是挺不錯(cuò)的,下一篇文章我會(huì)帶領(lǐng)大家繼續(xù)完善這個(gè)程序,加入字母表快速滾動(dòng)功能。

好了,今天的講解到此結(jié)束,有疑問(wèn)的朋友請(qǐng)?jiān)谙旅媪粞浴?
源碼下載,請(qǐng)點(diǎn)擊這里

相關(guān)文章

最新評(píng)論