Full Special Effects of contacts in Android system (I) Grouping navigation and extrusion animation (with source code)

Source: Internet
Author: User

I remember that I was very interested in the special effects in the system contact when I first came into contact with Android. It will be grouped based on the first letter of the contact's last name on the mobile phone, the current group is always displayed at the top of the page. As shown in:

What interests me most is that when the last group is in touch with the previous group, a top-up squeeze animation will be generated. At that time, I thought about various methods to implement this special effect, but I was not able to succeed because I had no efforts. Now more than two years have passed, and I have grown a lot. Now I think about this function, and I suddenly find that I already have the idea, so I will immediately record it and share it with you.

First, let's talk about the knowledge points we need to know in advance. Here we need SectionIndexer, which can effectively help us control groups. Because SectionIndexer is an interface, you can customize a subclass to implement SectionIndexer. However, it is too troublesome to write another SectionIndexer. Here, we directly use the Android-provided AlphabetIndexer implementation, it is sufficient to use it to implement the Contact Group function.

The AlphabetIndexer constructor requires three parameters. The first parameter is cursor, the second parameter is sortedColumnIndex, and the third parameter is the alphabet string. The cursor transmits the cursor we found from the database. The sortedColumnIndex indicates which column is used for sorting, while the alphabet specifies the alphabetic sorting rules, for example: "ABCDEFGHIJKLMNOPQRSTUVWXYZ ". With AlphabetIndexer, we can use its getPositionForSection and getSectionForPosition methods to find the group where the current position is located and the location where the current group is located, this allows you to achieve group navigation and extrusion animation effects similar to system contacts. For more details about AlphabetIndexer, see the official documentation.
So how should we sort contacts? As mentioned above, there is a sortedColumnIndex parameter. Where is this sortedColumn? Let's take a look at the raw_contacts table (/data/com. android. providers. contacts/databases/contacts2.db). This table has a complex structure and contains more than 20 columns, one of which is named sort_key. This is what we are looking! As shown in:

We can see that this column records the pinyin corresponding to Chinese characters in a very user-friendly manner, so that we can easily sort contacts by the values in this column.
Next we will start to implement it. Create an Android project named ContactsDemo. First, complete the layout file, open or create activity_main.xml as the main layout file of the program, and add the following code to it:Copy codeThe Code is 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 very simple. A ListView is placed in it to display the contact information. In addition, a LinearLayout header contains a TextView, which is used to display a current group at all times in the interface header.
Create a new contact_item.xml layout for filling each row in the ListView. The Code is as follows:Copy codeThe Code is 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 group layout is put in the same way as the previous layout, because not only the interface header needs to display the group, the Group layout must be displayed before the first element in each group. Then add a simple LinearLayout that contains an ImageView used to display the contact profile and a TextView used to display the contact name.
In this way, all the layout files are completed. The following describes how to implement the functions.
First, create a Contact object class from the simple start:Copy codeThe Code is as follows: public class Contact {
/**
* Contact 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 contains only the contact name and sort key.
Next, write the contact list adapter, create a ContactAdapter class that inherits from ArrayAdapter, and add the following code:Copy codeThe Code is as follows: public class ContactAdapter extends ArrayAdapter <Contact> {
/**
* Layout file of items 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;
}
/**
* Input a grouping tool to the current adapter.
*
* @ Param indexer
*/
Public void setIndexer (SectionIndexer indexer ){
MIndexer = indexer;
}
}

In the above Code, the most important thing is the getView method. In this method, we use the getSectionForPosition method of SectionIndexer to get the corresponding section value through the current position value, then, call the getPositionForSection method through the section value you just obtained to retrieve the new position value. If the current position value and the new position value are equal, we can think that the current position item is the first element in a group. We should display the group layout, in other cases, the Group layout should be hidden.
Finally, let's write the main interface of the program. Open or create a new MainActivity as the main interface of the program. The Code is as follows:Copy codeThe Code is as follows: public class MainActivity extends Activity {
/**
* Group Layout
*/
Private LinearLayout titleLayout;
/**
* Letters displayed on the group
*/
Private TextView title;
/**
* Contact ListView
*/
Private ListView contactsListView;
/**
* Contact list Adapter
*/
Private ContactAdapter adapter;
/**
* Used for alphabet grouping
*/
Private AlphabetIndexer indexer;
/**
* Store contacts on all mobile phones
*/
Private List <Contact> contacts = new ArrayList <Contact> ();
/**
* Sorting rules defining the alphabet
*/
Private String alphabet = "# ABCDEFGHIJKLMNOPQRSTUVWXYZ ";
/**
* The first visible element used for record identification during rolling.
*/
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 ();
}
}
/**
* Set a listener event for the contact ListView, and change the display position of the group based on the current sliding State to achieve the effect of extrusion animation.
*/
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. If it is an English letter, return directly. Otherwise, return #.
*
* @ Param sortKeyString
* The sort key read from the database
* @ Return: English letter or #
*/
Private String getSortKey (String sortKeyString ){
String key = sortKeyString. substring (0, 1). toUpperCase ();
If (key. matches ("[A-Z]") {
Return key;
}
Return "#";
}
}

We can see that in the onCreate method, we query the contact name and sort key from the system contact database, and then pass the returned cursor in AlphabetIndexer as the first parameter. Since we checked two columns in total and the sorting key was in the second column, we passed in 1 as the second sortedColumnIndex parameter. In the third alphabet parameter, the "# ABCDEFGHIJKLMNOPQRSTUVWXYZ" string is input here, because some contact names may not be within the alphabet, and we use # to represent these contacts.

Then, we listened to the ListView scrolling in the setupContactsListView method. In the onScroll method, we obtained the group value of the first visible element through the getSectionForPosition method, and then added 1 to the group value, then, use the getPositionForSection method or the first element in the next group. If the first element value of the next group is equal to the value of the first visible element, add 1, it indicates that the layout of the next group must be different from that of the group on the top of the interface. Then, use the getChildAt (0) method of ListView to obtain the first child View displayed on the interface, and then use the view. getBottom obtains the position at the bottom of the parent window and compares the height of the group layout to vertical offset the group layout on the top to achieve the extrusion animation effect.

Finally, the AndroidManifest. xml code is provided. to read the mobile phone contact, you must add the android. permission. READ_CONTACTS statement:Copy codeThe Code is as follows: <manifest xmlns: android = "http://schemas.android.com/apk/res/android"
Package = "com. example. contactsdemo"
Android: versionCode = "1"
Android: versionName = "1.0" type = "codeph" text = "/codeph">
<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 figure:

At present, the group navigation and extrusion animation effects have been completed, and it looks quite good. In the next article, I will guide you to continue to improve this program and add the alphabet quick scrolling function.

Now, today's explanation is over. If you have any questions, please leave a message below.
Click here to download the source code

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.