Remember that when I first contacted Android, I was interested in the special effects of the system contacts, which grouped according to the initials of the contact's last name on the phone and always displayed a current group at the top of the interface. As shown in the following illustration:
What interests me most is that when the latter group is colliding with the previous group, a top extrusion animation is produced. At that time I thought of various ways to achieve this special effect, but not to the home, can not succeed. Now more than two years later, I have grown a lot, and then go back to think about this function, suddenly found that there is a train of thought, so immediately recorded down to share with you.
First of all, we need to know the knowledge points in advance, here we need to use the most is sectionindexer, it can effectively help us to control the group. Since Sectionindexer is an interface, you can customize a subclass to implement Sectionindexer, but it's too troublesome to write a sectionindexer implementation yourself. Here we use Android directly to provide a good implementation alphabetindexer, it is enough to implement the contact grouping function.
The Alphabetindexer constructor needs to pass in three arguments, the first argument is cursor, the second is the Sortedcolumnindex integer, and the third argument is the alphabet string. Where cursor is to pass the cursor that we have detected from the database, Sortedcolumnindex is to indicate which column we are using to sort, and alphabet specifies the alphabet collation, for example: " ABCDEFGHIJKLMNOPQRSTUVWXYZ ". With Alphabetindexer, we can use its getpositionforsection and Getsectionforposition methods to find the group where the current position is, and where the current group is located, In order to achieve similar to the system Contact Group navigation and extrusion animation effect, about Alphabetindexer more detailed details, please refer to the official documentation.
So how are we going to sort the contacts? As mentioned before, there is a sortedcolumnindex parameter, where is this sortedcolumn? Let's take a look at the raw_contacts of the system contact (/DATA/DATA/COM.ANDROID.PROVIDERS.CONTACTS/DATABASES/CONTACTS2.DB), which is a very complex table structure, There are more than 20 columns, including one named Sort_key, which is what we are looking for! As shown in the following illustration:
As you can see, this column is very humane to help us record the pinyin corresponding to the Chinese character, so that we can easily sort the contact by the value of this column.
Let's start with the implementation of a new Android project named Contactsdemo. First, we'll finish the layout file, open or create a new activity_main.xml as the program's main layout file, adding the following code in it:
Copy Code code as follows:
<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>
The layout file is simple and contains a ListView for displaying contact information. In addition, a linearlayout is placed in the head, which contains a textview that is always displayed at the interface head with a current grouping.
Then create a new Contact_item.xml layout, which is used to populate each line in the ListView with the following code:
Copy Code code as follows:
<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>
In this layout file, the first is to put the same grouping layout as the previous one, because not only the interface head needs to show the grouping, in each group before the first no element need to show the group layout. Then add a simple linearlayout that contains a imageview to display the contact Avatar and a textview to display the contact's name.
So our layout files are all finished, and the following starts to really implement the functionality.
First, start with a simple, new contact entity class:
Copy Code code as follows:
public class Contacts {
/**
* Contact person's name
*/
private String name;
/**
* Sort Letters
*/
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;
}
}
This entity class is simple and contains only the contact name and the sort key.
Next finish writing the contact list adapter, create a new Contactadapter class to inherit from Arrayadapter, and add the following code:
Copy Code code as follows:
public class Contactadapter extends Arrayadapter<contact> {
/**
* The item layout file that needs to be rendered
*/
private int resource;
/**
* Alphabet Grouping Tool
*/
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;
}
/**
* Pass in a grouping tool to the current adapter.
*
* @param Indexer
*/
public void Setindexer (sectionindexer indexer) {
Mindexer = indexer;
}
}
The above code, the most important is the GetView method, in this method, we use the Sectionindexer Getsectionforposition method, through the current position value to get the corresponding section value, And then reverse through just get the section value, call the Getpositionforsection method, retrieve the new position value. If the current position value is equal to the new position value, then we can assume that the current position item is the first element under a group, we should display the group layout, and other situations should hide the group layout.
Finally, we write the main interface of the program, open or create a new mainactivity as the main interface of the program, the code looks like this:
Copy Code code as follows:
public class Mainactivity extends activity {
/**
* Layout of groups
*/
Private LinearLayout titlelayout;
/**
* The letters displayed on the group
*/
Private TextView title;
/**
* Contact Person ListView
*/
Private ListView Contactslistview;
/**
* Contact List Adapter
*/
Private Contactadapter adapter;
/**
* Used to group alphabetical lists
*/
Private Alphabetindexer Indexer;
/**
* Store all contacts on the phone
*/
Private list<contact> contacts = new arraylist<contact> ();
/**
* Define the sorting rules for the alphabet
*/
Private String alphabet = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* The first visible element was last used to record the identity when scrolling.
*/
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 contacts = 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 ();
}
}
/**
* Set up the Listener event for the contact ListView, and change the display position of the group according to the current sliding state, so as to achieve the squeezing animation effect.
*/
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;
}
});
}
/**
* Get the first character of the sort key, and return it if it is an English letter, otherwise return #.
*
* @param sortkeystring
* read out of the sort key in the database
* @return English letters or #
*/
private string GetSortKey (string sortkeystring) {
String key = sortkeystring.substring (0, 1). toUpperCase ();
if (Key.matches ("[A-z]")) {
Return key;
}
Return "#";
}
}
As you can see, in the OnCreate method, we query the contact's name and sort key from the system contact database, and then pass the query returned cursor directly to Alphabetindexer as the first argument. Since we've checked two columns altogether, the sort key is in the second column, so our second sortedcolumnindex argument passes in 1. The third alphabet parameter here passed the "#ABCDEFGHIJKLMNOPQRSTUVWXYZ" string, because the names of some of the contacts may not be in the alphabet, we unify the # to represent this part of the contact.
Then we listen for ListView scrolling in the Setupcontactslistview method, get the grouped value of the first visible element in the Onscroll method, and then add 1 to the group value. Again, by using the Getpositionforsection method or the first element in the next grouping, if the first element value of the next group equals the value of the first visible element plus 1, then the layout of the next grouping will collide with the top group layout of the interface. Then through the ListView Getchildat (0) method, get to the interface displayed in the first child view, Then use View.getbottom to get the location of the bottom distance from the parent window, compared to the height of the packet layout to the top of the group layout longitudinal offset, you can achieve the effect of extrusion animation.
Finally give the Androidmanifest.xml code, because to read mobile phone contact, so need to add Android.permission.READ_CONTACTS statement:
Copy Code code as follows:
<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>
Now let's run the program, as shown in the following image:
At present, the group navigation and extrusion animation effect has been completed, it seems to feel pretty good, the next article I will lead you to continue to improve the program, add the alphabet fast scrolling function.
Well, today's explanation to this end, a friend in doubt please leave a message below.
Download the source, please click here