In my last article, I worked with you to implement a group navigation and squeeze animation similar to that of Contacts on Android, but since the article was called “Full Effects for Contacts on Android”, it’s obviously not “full” without fast scrolling. So in this article I’m going to show you how to improve on the code from the previous article and add fast scrolling.

If you haven’t read my last article, check out Android contacts full effects implementation (part 1), group navigation, and squeeze animations.

The ListView itself is actually a fast scrolling attribute, can set the android: in XML fastScrollEnabled = “true” to enable. Contacts, including older versions of Android, use this method for fast scrolling. The effect is shown below:

However, this fast scrolling mode was ugly, and many phone manufacturers later changed the default fast scrolling mode to A similar a-Z alphabet scrolling mode on the iPhone when customizing their ROMs. How can we be behind The Times here! Our fast scrolling is also going to use the A-Z alphabet! Let’s start implementing it.

one

First, open the ContactsDemo project and modify the activity_main.xml layout file. Since we are going to add an alphabet to the interface, we need A Button with A background of an a-Z sorted image, aligned to the right. You also need a TextView that displays the current group on the pop-up group layout, default is gone, and only makes it appear when you swipe your finger across the alphabet. The modified layout file 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:scrollbars="none"
        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>
    
    <Button 
        android:id="@+id/alphabetButton"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentRight="true"
        android:background="@drawable/a_z"
        />
    
    <RelativeLayout 
        android:id="@+id/section_toast_layout"
        android:layout_width="70dip"
        android:layout_height="70dip"
        android:layout_centerInParent="true"
        android:background="@drawable/section_toast"
        android:visibility="gone"
        >
        <TextView 
            android:id="@+id/section_toast_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textColor="#fff"
            android:textSize="30sp"
            />
    </RelativeLayout>
 
</RelativeLayout>
Copy the code

We need to listen for the alphabet button’s touch event, so we add the following code to MainActivity:

private void setAlpabetListener() { alphabetButton.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { float alphabetHeight = alphabetButton.getHeight(); float y = event.getY(); int sectionPosition = (int) ((y / alphabetHeight) / (1f / 27f)); if (sectionPosition < 0) { sectionPosition = 0; } else if (sectionPosition > 26) { sectionPosition = 26; } String sectionLetter = String.valueOf(alphabet.charAt(sectionPosition)); int position = indexer.getPositionForSection(sectionPosition); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: alphabetButton.setBackgroundResource(R.drawable.a_z_click); sectionToastLayout.setVisibility(View.VISIBLE); sectionToastText.setText(sectionLetter); contactsListView.setSelection(position); break; case MotionEvent.ACTION_MOVE: sectionToastText.setText(sectionLetter); contactsListView.setSelection(position); break; default: alphabetButton.setBackgroundResource(R.drawable.a_z); sectionToastLayout.setVisibility(View.GONE); } return true; }}); }Copy the code

two

As you can see, in this method we register the onTouch event for the alphabet button, and then do some logical judgment and processing in the onTouch method, which I’ll explain in detail. First, the total height of the alphabet is obtained through the getHeight method of the alphabet button, and then the ordinate of the finger on the alphabet is obtained by the event.getY method. By dividing the ordinate by the total height, the current finger position can be obtained by a decimal (0 is at the # end, 1 is at the Z end). Since there are 27 characters in our alphabet, we can take the decimal we just calculated and divide it by 1/27 to get a floating point number in the range 0 to 27, and then round it down to figure out which letter we are pressing. Then, judge the action of the event. If it is ACTION_DOWN or ACTION_MOVE, display the letter that the current finger presses on the pop-up group, and call the setSelection method of ListView to scroll the list to the corresponding group. For other actions, hide the pop-up group layout.

The complete code for MainActivity is as follows:

Public class MainActivity extends Activity {/** * private LinearLayout titleLayout; /** * Private RelativeLayout sectionToastLayout; /** * private Button alphabetButton; /** * private TextView title; Private TextView sectionToastText; private TextView sectionToastText; /** * contact ListView */ private ListView contactsListView; /** * contact list adapter */ private ContactAdapter adapter; /** * Private AlphabetIndexer Indexer; /** * Private List<Contact> contacts = new ArrayList<Contact>(); / * * * define the collation of the alphabet * / private String alphabet = "# ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /** * the last visible element, used to record the identifier while 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); sectionToastLayout = (RelativeLayout) findViewById(R.id.section_toast_layout); title = (TextView) findViewById(R.id.title); sectionToastText = (TextView) findViewById(R.id.section_toast_text); alphabetButton = (Button) findViewById(R.id.alphabetButton); 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(); setAlpabetListener(); }} /** * Set the listener event for the contact ListView, according to the current slide state to change the group display position, so as to achieve the effect of squeeze 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; }}); } /** * Set the alphabet touch event, based on the current touch position combined with the height of the alphabet, calculate which letter the current touch is on. * Display pop-up grouping when finger is pressed over alphabet. Hide the pop-up grouping when your fingers leave the alphabet. */ private void setAlpabetListener() { alphabetButton.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { float alphabetHeight = alphabetButton.getHeight(); float y = event.getY(); int sectionPosition = (int) ((y / alphabetHeight) / (1f / 27f)); if (sectionPosition < 0) { sectionPosition = 0; } else if (sectionPosition > 26) { sectionPosition = 26; } String sectionLetter = String.valueOf(alphabet.charAt(sectionPosition)); int position = indexer.getPositionForSection(sectionPosition); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: alphabetButton.setBackgroundResource(R.drawable.a_z_click); sectionToastLayout.setVisibility(View.VISIBLE); sectionToastText.setText(sectionLetter); contactsListView.setSelection(position); break; case MotionEvent.ACTION_MOVE: sectionToastText.setText(sectionLetter); contactsListView.setSelection(position); break; default: alphabetButton.setBackgroundResource(R.drawable.a_z); sectionToastLayout.setVisibility(View.GONE); } return true; }}); } /** * get the first character of the sort key, if it is an English letter, otherwise return #. Private String getSortKey(String sortKeyString) {private String getSortKey(String sortKeyString) { alphabetButton.getHeight(); String key = sortKeyString.substring(0, 1).toUpperCase(); if (key.matches("[A-Z]")) { return key; } return "#"; }}Copy the code

Ok, we have changed the above two files, but the other files remain unchanged. Let’s run it to see what happens:

Very good! When you run your finger over the alphabet on the right, the list of contacts changes accordingly, displaying a current group in the center of the screen.

Now let’s go back to the group navigation, squeeze animation, alphabet fast scroll, Android contacts are all implemented. Well, this is the end of today’s explanation, if you have questions, please leave a message below.

To download the source code, click here

Pay attention to my technical public account “Guo Lin”, high-quality technical articles push.