要实现这种功能,只需要在   android系统联系人分组特效实现(1)---分组导航和挤压动画  的基础上再加上一个自定义控件即可完成.
1.新建项目,继续新建一个java类,BladeView,用于作为导航栏
public class BladeView extends View {
    private Handler handler = new Handler();
    public BladeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray a=getContext().obtainStyledAttributes(attrs,R.styleable.NewMyElement);
        textSize=a.getDimension(R.styleable.NewMyElement_textSize, 15);
        a.recycle();
    }
    private PopupWindow mPopupWindow;
    private TextView mPopupText;
    float textSize;
    
    public interface OnMyImteClickListener{
        void onItemClick(int s);
    }
    private OnMyImteClickListener mListener;
    
    public void setOnMyItemClickListener(OnMyImteClickListener listener){
        this.mListener=listener;
    }
    
    String b[]={ "#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
            "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
            "Y", "Z" };
    int choose=-1;
    Paint paint=new Paint();
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int height=getHeight();
        int width=getWidth();
        int singleHeight=height/b.length;
        for (int i = 0; i < b.length; i++) {
            paint.setTextSize(textSize);
            paint.setColor(Color.BLACK);
            if(i==choose){
                paint.setColor(Color.BLUE);
            }
            float xPos=width/2-paint.measureText(b[i])/2;
            float yPos=singleHeight*i+singleHeight;
            canvas.drawText(b[i], xPos, yPos, paint);
            paint.reset();
        }
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        float y=event.getY();
        int oldChoose=choose;
        int c=(int)(y/getHeight()*b.length);
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            if(oldChoose!=c){
                if(c>=0&&c<b.length){
                    choose=c;
                    mListener.onItemClick(c);
                    showPopup(c);
                    invalidate();
                }
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if(oldChoose!=c){
                if(c>=0&&c<b.length){
                    choose=c;
                    mListener.onItemClick(c);
                    showPopup(c);
                    invalidate();
                }
            }
            break;
        case MotionEvent.ACTION_UP:
            choose=-1;
            mListener.onItemClick(-1);
            dismissPopup();
            invalidate();
            break;
        default:
            break;
        }
        return true;
    }
    private void showPopup(int item) {
        if (mPopupWindow == null) {
            handler.removeCallbacks(dismissRunnable);
            mPopupText = new TextView(getContext());
            mPopupText.setBackgroundColor(Color.GRAY);
            mPopupText.setTextColor(Color.CYAN);
            mPopupText.setTextSize(50);
            mPopupText.setGravity(Gravity.CENTER_HORIZONTAL
                    | Gravity.CENTER_VERTICAL);
            mPopupWindow = new PopupWindow(mPopupText, 100, 100);
        }
        String text = "";
        if (item == 0) {
            text = "#";
        } else {
            text = Character.toString((char) ('A' + item - 1));
        }
        mPopupText.setText(text);
        if (mPopupWindow.isShowing()) {
            mPopupWindow.update();
        } else {
            mPopupWindow.showAtLocation(getRootView(),
                    Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL, 0, 0);
        }
    }
    private void dismissPopup() {
        handler.postDelayed(dismissRunnable, 800);
    }
    Runnable dismissRunnable = new Runnable() {
        @Override
        public void run() {
            if (mPopupWindow != null) {
                mPopupWindow.dismiss();
            }
        }
    };
}

2.打开activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:my="http://schemas.android.com/apk/res/com.example.mycontact" 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.mycontact.MainActivity" >
    <ListView 
        android:id="@+id/lv_contact"
        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="18dp"
        android:layout_alignParentTop="true"
        android:orientation="vertical"
        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="10dp"
            android:textColor="#ffffff"
            android:textSize="13sp"
            />
    </LinearLayout>
    <com.example.mycontact.BladeView 
        android:id="@+id/BladeView"
        android:layout_width="30dp"
        android:layout_height="fill_parent"
        android:layout_alignParentRight="true"
        my:textSize="23sp"
        />
    <RelativeLayout 
        android:id="@+id/section_toast_layout"
        android:layout_width="70dp"
        android:layout_height="70dp"
        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="#ffffff"
            android:textSize="30sp"
            />
    </RelativeLayout>

</RelativeLayout>


3.实现主要功能之前,还需一个实体类
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;
    }
}

4.还需要一个ListView展示所显示的子布局
<?xml version="1.0" encoding="utf-8"?>
<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="18dp"
        android:orientation="vertical"
        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="10dp"
            android:textColor="#ffffff"
            android:textSize="13sp"
            />
    </LinearLayout>
    <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="50dp"
        android:orientation="horizontal"
        android:id="@+id/name_layout"
        >
        <ImageView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            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>

5.展示数据的适配器
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 sort_key=(TextView) layout.findViewById(R.id.sort_key);
        name.setText(contact.getName());
        int section=mIndexer.getSectionForPosition(position);
        if(position==mIndexer.getPositionForSection(section)){
            sortKeyLayout.setVisibility(View.VISIBLE);
            sort_key.setText(contact.getSortKey());
        }
        else{
            sortKeyLayout.setVisibility(View.GONE);
        }
        return layout;
    }
    
    //给当前适配器传入一个分组工具
    public void setIndexer(SectionIndexer indexer){
        mIndexer=indexer;
    }
}

6.最关键的MainActivity.java
public class MainActivity extends ActionBarActivity {
    //分组的布局
    private LinearLayout titleLayout;
    //弹出式分组的布局
    private RelativeLayout sectionToastLayout;
    //右侧可滑动字母表
    private BladeView bView;
    //分组上显示的字母
    private TextView title;
    //弹出式分组上的文字
    private TextView sectionToastText;
    //联系人ListView
    private ListView contactListView;
    //联系人列表适配器
    private ContactAdapter adapter;
    //用于对字母表进行分组
    private AlphabetIndexer indexer;
    //存储手机中所有联系人
    private List<Contact> contactsList=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.item,contactsList);
        titleLayout=(LinearLayout) findViewById(R.id.title_layout);
        title=(TextView) findViewById(R.id.title);
        sectionToastLayout=(RelativeLayout) findViewById(R.id.section_toast_layout);
        sectionToastText=(TextView) findViewById(R.id.section_toast_text);
        bView=(BladeView) findViewById(R.id.BladeView);
        contactListView=(ListView) findViewById(R.id.lv_contact);
        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);
                contactsList.add(contact);
            } while (cursor.moveToNext());
        }
        startManagingCursor(cursor);
        indexer=new AlphabetIndexer(cursor, 1, alphabet);
        adapter.setIndexer(indexer);
        if(contactsList.size()>0){
            setupContactListView();
            setAlphabetListener();
        }
    }
    //为联系人ListView设置滑动监听
    private void setupContactListView(){
        contactListView.setAdapter(adapter);
        contactListView.setOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView arg0, int arg1) {
            }
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                int section=indexer.getSectionForPosition(firstVisibleItem);
                int nextSectionPosition=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(nextSectionPosition==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;
            }
        });
    }
    
    //设置字母表上的触摸事件
    private void setAlphabetListener(){
        bView.setOnMyItemClickListener(new OnMyImteClickListener() {
            @Override
            public void onItemClick(int s) {
                if(s!=-1){
                    contactListView.setSelection(indexer.getPositionForSection(s));
                }
            }
        });
    }
    
    //获取sort_key的首字母
    private String getSortKey(String sortKeyString){
        String key=sortKeyString.substring(0, 1).toUpperCase();
        if(key.matches("[A-Z]")){
            return key;
        }
        return "#";
    }
}

 

android系统联系人分组特效实现(2)---字母表快速滚动的更多相关文章

  1. android系统联系人分组特效实现(1)---分组导航和挤压动画

    1.打开activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/andr ...

  2. Android系统联系人全特效实现(下),字母表快速滚动

    在上一篇文章中,我和大家一起实现了类似于Android系统联系人的分组导航和挤压动画功能,不过既然文章名叫做<Android系统联系人全特效实现>,那么没有快速滚动功能显然是称不上&quo ...

  3. Android系统联系人全特效实现(上),分组导航和挤压动画

    记得在我刚接触Android的时候对系统联系人中的特效很感兴趣,它会根据手机中联系人姓氏的首字母进行分组,并在界面的最顶端始终显示一个当前的分组.如下图所示: 最让我感兴趣的是,当后一个分组和前一个分 ...

  4. Android系统源码学习步骤

    Android系统是基于Linux内核来开发的,在分析它在运行时库层的源代码时,我们会经常碰到诸如管道(pipe).套接字(socket)和虚拟文件系统(VFS)等知识. 此外,Android系统还在 ...

  5. Android系统源代码学习步骤

    目前,互联网行业正在朝着移动互联网方向强劲地发展,而移动互联网的发展离不开背后的移动平台的支撑.众所周知,如今在移动平台市场上,苹果的iOS.谷歌的Android和微软的Windows Phone系统 ...

  6. 如何学习Android系统源码(转)

    一. Android系统的源代码非常庞大和复杂,我们不能贸然进入,否则很容易在里面迷入方向,进而失去研究它的信心.我们应该在分析它的源代码之前学习好一些理论知识,下面就介绍一些与Android系统相关 ...

  7. android 获取系统联系人 完全解析

    一.代码 1.ContactsEngine.java import java.util.ArrayList; import java.util.HashMap; import java.util.Li ...

  8. android: 通过内容提供器读取系统联系人

    读取系统联系人 由于我们之前一直使用的都是模拟器,电话簿里面并没有联系人存在,所以现在需要自 己手动添加几个,以便稍后进行读取.打开电话簿程序,界面如图 7.1 所示. 图   7.1 可以看到,目前 ...

  9. Android(java)学习笔记250:ContentProvider使用之获得系统联系人信息02(掌握)

    1.重要: 系统删除一个联系人,默认情况下并不是把这个联系人直接删除掉了,只是做了一个标记,标记为被删除. 2.前面一讲说过了如何获取系统联系人信息(通过ContentProvider),获取联系人信 ...

随机推荐

  1. 实现接口Controller定义控制器

    实现接口Controller定义控制器 控制器提供访问应用程序的行为,通常通过服务接口定义或注解定义两种方法实现. 控制器解析用户的请求并将其转换为一个模型.在Spring MVC中一个控制器可以包含 ...

  2. iPad游戏 Calcculator: The Game 程序自动计算求解方法

    今天在iPad上下了个小游戏,主要是一个计算器的界面,有开始值,目标值,限定步数,以及一些加减乘除,还有作者脑洞想出来的功能键,主要有左移,直接把一个数加到末尾,将其中的某个数改为另一个数等等..玩到 ...

  3. 3、SpringBoot------邮件发送(1)

    开发工具:STS 代码下载链接:https://github.com/theIndoorTrain/Springboot/tree/8878e8e89ce01ceb967ef8c1193ac740a6 ...

  4. Session和cookic

    session是无状态的方式,服务器存储机制,当用户第一次请求服务器,服务器会给客户分配一个标识id,客户端再次访问服务器,根据session id 去访问服务器数据库,返回信息,同时session ...

  5. spring-bean(注解方式-管理及依赖注入)

    Bean管理(注解方式) 1.添加注解的依赖包:Spring-aop.jar 2.配置spring的XML文件的引入(查官方源码) 3.开启注解的扫描 <context:component-sc ...

  6. Docker自学纪实(四)搭建LNMP部署wordpress

    我们在工作中最常用的就是LNMP网站平台 这个架构呢,是整个公司网站的核心 如果对于访问量较小的网站,可以直接在服务器上面部署 而如果是访问量很大的网站,那负载就是个很大的问题. 要么需要再买很多服务 ...

  7. 微信小程序开发入门学习(2):小程序的布局

    概述 小程序的布局采用了和Css3中相同的 flex(弹性布局)方式,使用方法也类似(只是属性名不同而已). 水平排列 默认是从左向右水平依次放置组件,从上到下依次放置组件. 任何可视组件都需要使用样 ...

  8. Redis ----------String的操作

    set    key   value 设置key对应的值为String类型的value mset    key   value 一次设置多个 key对应的值 mget    key   value 一 ...

  9. python——闰年的判断

    写一个程序,判断给定年份是否为闰年. 这样定义闰年的:能被4整除但不能被100整除,或者能被400整除都是闰年. while(1): year = input("请输入一个年份,让我判断一下 ...

  10. Nginx安装,目录结构与配置文件详解

    1.Nginx简介 Nginx(发音同 engine x)是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行.由俄罗斯的程序设 ...