qq 好友聊天界面,右侧 IndexBar  A B C D ,点击跳转到相应的联系人名字

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class QuickIndexBar extends View {

    private String[] letterArr = {"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"};

    public QuickIndexBar(Context context) {
        this(context, null);
    }

    public QuickIndexBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    Paint paint;
    int ColorDefault = Color.WHITE;
    int ColorPressed = Color.BLACK;

    public QuickIndexBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        paint = new Paint(Paint.ANTI_ALIAS_FLAG);//设置抗锯齿
        paint.setColor(ColorDefault);

        int size = getResources().getDimensionPixelSize(R.dimen.paint_size);
        paint.setTextSize(size);
        //文字绘制的起点默认是左下角,设置起点为文字底边的中心,baseline基准线
        paint.setTextAlign(Paint.Align.CENTER);

    }

    float cellHeight;//一个格子的高
    float x;

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        cellHeight = getMeasuredHeight() * 1f / letterArr.length;
        x = getMeasuredWidth() / 2;
    }

    @Override
    protected void onDraw(Canvas canvas) {

        //遍历26个字母,对每个字母进行绘制
        for (int i = 0; i < letterArr.length; i++) {
            String text = letterArr[i];
            //算法:格子高的一半 + 文字高的一半 + i*格子的高
            float y = cellHeight / 2 + getTextHeight(text) / 2 + i * cellHeight;

            //更改颜色
            paint.setColor(i==index?ColorPressed:ColorDefault);

            canvas.drawText(text, x, y, paint);
        }

    }
    int index = -1;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                int tempIndex = (int) (event.getY() / cellHeight);
                if(tempIndex!=index){
                    index = tempIndex;

                    //对index进行合法性的判断
                    if(index>=0 && index<letterArr.length){
                        String letter = letterArr[index];

                        if(listener!=null){
                            listener.onLetterChange(letter);
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                //重置为-1
                index = -1;
                if(listener!=null){
                    listener.onRelease();
                }

                break;
        }

        //重绘
        invalidate();

        return true;
    }

    /**
     * 获取文字的高度
     *
     * @param text
     * @return
     */
    private int getTextHeight(String text) {
        Rect bounds = new Rect();
        //当下面的方法执行完,bounds就有值了
        paint.getTextBounds(text, 0, text.length(), bounds);
        return bounds.height();
    }

    private OnLetterChangeListener listener;
    public void setOnLetterChangeListener(OnLetterChangeListener listener){
        this.listener = listener;
    }

    public interface OnLetterChangeListener{
        void onLetterChange(String letter);

        /**
         * 抬起的时候执行
         */
        void onRelease();
    }
}

Activity :

import android.os.Handler;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.animation.OvershootInterpolator;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Collections;
import java.util.logging.Logger;

public class MainActivity extends AppCompatActivity implements QuickIndexBar.OnLetterChangeListener {

    private QuickIndexBar quickIndex;
    ListView listview;
    ArrayList<Friend> friends = new ArrayList<>();
    TextView tv_word;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        quickIndex = (QuickIndexBar) findViewById(R.id.quickIndex);
        listview = (ListView) findViewById(R.id.listview);
        tv_word = (TextView) findViewById(R.id.tv_word);

        quickIndex.setOnLetterChangeListener(this);

        //填充数据
        prepareData();
        //排序
        Collections.sort(friends);

        listview.setAdapter(new FriendAdapter(friends));

//        Log.e("tag",PinYinUtil.getPinYin("刘 德 华 "));//LIUDEHUA
//        Log.e("tag",PinYinUtil.getPinYin("a刘a德华a"));//aLIUaDEHUAa
//        Log.e("tag",PinYinUtil.getPinYin("刘德华,。"));//LIUDEHUA

    }

    @Override
    public void onLetterChange(String letter) {
        //根据当前触摸的字母去集合中查找首字母和触摸字母相同的条目,然后置顶
        for (int i = 0; i < friends.size(); i++) {
            String word = friends.get(i).pinyin.substring(0, 1);
            if (word.equals(letter)) {
                //说明找到了,那么就置顶
                listview.setSelection(i);
                break;//找到就立即中断
            }
        }

        //显示当前的字母
        showCurrentWord(letter);
    }

    @Override
    public void onRelease() {
//        tv_word.setVisibility(View.GONE);

        new Handler().postDelayed(
                new Runnable() {
                    @Override
                    public void run() {
                        ViewCompat.animate(tv_word).scaleX(0f).scaleY(0f)
                                .setDuration(500).start();
                    }
                }, 500);

    }

    boolean isRunAnim = false;

    /**
     * 显示当前的字母
     *
     * @param letter
     */
    private void showCurrentWord(String letter) {
        tv_word.setText(letter);
//        tv_word.setVisibility(View.VISIBLE);

        if (isRunAnim) {
            //如果正在执行放大动画,那么就不要执行了
            return;
        }
        ViewCompat.animate(tv_word).scaleX(1f).scaleY(1f)
                .setInterpolator(new OvershootInterpolator(3))
                .setListener(new ViewPropertyAnimatorListenerAdapter() {
                    @Override
                    public void onAnimationStart(View view) {
                        isRunAnim = true;
                    }

                    @Override
                    public void onAnimationEnd(View view) {
                        isRunAnim = false;
                    }
                })
                .setDuration(500).start();
    }

    // 虚拟数据
    private void prepareData() {
        friends.add(new Friend("李伟"));
        friends.add(new Friend("张三"));
        friends.add(new Friend("阿三"));
        friends.add(new Friend("阿四"));
        friends.add(new Friend("段誉"));
        friends.add(new Friend("段正淳"));
        friends.add(new Friend("张三丰"));
        friends.add(new Friend("陈坤"));
        friends.add(new Friend("林俊杰1"));
        friends.add(new Friend("陈坤2"));
        friends.add(new Friend("王二a"));
        friends.add(new Friend("林俊杰a"));
        friends.add(new Friend("张四"));
        friends.add(new Friend("林俊杰"));
        friends.add(new Friend("王二"));
        friends.add(new Friend("王二b"));
        friends.add(new Friend("赵四"));
        friends.add(new Friend("杨坤"));
        friends.add(new Friend("赵子龙"));
        friends.add(new Friend("杨坤1"));
        friends.add(new Friend("李伟1"));
        friends.add(new Friend("宋江"));
        friends.add(new Friend("宋江1"));
        friends.add(new Friend("李伟3"));
    }
}
import android.support.annotation.Nullable;
import android.text.TextUtils;

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;

public class PinYinUtil  {
    /**
     * 获取汉字的拼音
     * @param chinese
     * @return
     */
    public static String getPinYin(String chinese){
        if(TextUtils.isEmpty(chinese))return null;

        //拼音转换的格式化,主要控制字母的大小写,以及是否需要声调
        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
        format.setCaseType(HanyuPinyinCaseType.UPPERCASE);//设置大写字母
        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);//不需要声调

        //ps:由于不支持对多个汉字进行获取,所以要将字符串转为字符数组,对单个汉字进行获取
        //最后,将每个字的拼音拼接起来,就是所有汉字的拼音
        StringBuilder builder = new StringBuilder();

        char[] chars = chinese.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];

            //1.要进行过滤空格,选择忽略
            if(Character.isWhitespace(c)){
                continue;
            }

            //2.要判断是否是中文,粗略的判断一下:由于一个汉字2个字节,
            //一个字节范围是-128~127,因此汉字肯定大于127
            if(c > 127){
                //有可能是汉字,就利用pinyin4j进行获取
                try {
                    //由于多音字的存在,所以返回的是数组,比如单:[chan, dan, shan]
                    String[] arr = PinyinHelper.toHanyuPinyinStringArray(c, format);
                    if(arr!=null){
                        //此处只能用第0个,原因:
                        //1.首先大部分汉字只有一个读音,多音字属于少数
                        //2.其次,我们也确实无能为力去判断应该用哪个,要判断一个汉字在一串文字
                        //中的精确读音,至少需要几个技术:a.分词算法 b.非常庞大的分词数据库
                        builder.append(arr[0]);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    //说明不是正确的汉字,选择忽略
                }
            }else {
                //肯定不是汉字,一般是ASCII码表中的字母,对于这个情况,我们选择
                //直接拼接
                builder.append(c);
            }
        }
        return  builder.toString();
    }
}
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.ArrayList;

import butterknife.Bind;
import butterknife.ButterKnife;

public class FriendAdapter extends BaseAdapter {
    ArrayList<Friend> list;

    public FriendAdapter(ArrayList<Friend> list) {
        this.list = list;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if(convertView==null){
            convertView = View.inflate(parent.getContext(), R.layout.adapter_friend, null);
            holder = new ViewHolder(convertView);
            convertView.setTag(holder);
        }else {
            holder = (ViewHolder) convertView.getTag();
        }

        //绑定数据
        Friend friend = list.get(position);

        String letter = friend.pinyin.substring(0, 1);
        if(position>0){
            //获取上一个条目的首字母
            String last = list.get(position - 1).pinyin.substring(0, 1);
            if(letter.equals(last)){
                //说明需要隐藏当前的
                holder.tvLetter.setVisibility(View.GONE);
            }else {
                //说明不一样,需要显示
                holder.tvLetter.setVisibility(View.VISIBLE);
                holder.tvLetter.setText(letter);
            }
        }else {
            //说明是=0,就是第一天
            holder.tvLetter.setVisibility(View.VISIBLE);
            holder.tvLetter.setText(letter);
        }

        holder.tvName.setText(friend.name);

        return convertView;
    }

    static class ViewHolder {
        @Bind(R.id.tv_letter)
        TextView tvLetter;
        @Bind(R.id.tv_name)
        TextView tvName;

        ViewHolder(View view) {
            ButterKnife.bind(this, view);
        }
    }
}

Listview右侧 IndexBar的更多相关文章

  1. [Android分享] 【转帖】Android ListView的A-Z字母排序和过滤搜索功能

      感谢eoe社区的分享   最近看关于Android实现ListView的功能问题,一直都是小伙伴们关心探讨的Android开发问题之一,今天看到有关ListView实现A-Z字母排序和过滤搜索功能 ...

  2. Android 实现ListView的A-Z字母排序和过滤搜索功能,实现汉字转成拼音

    转载:http://blog.csdn.net/xiaanming/article/details/12684155 转载请注明出处:http://blog.csdn.net/xiaanming/ar ...

  3. Android开源项目发现---ListView篇(持续更新)

    资料转载地址:https://github.com/Trinea/android-open-project 1. android-pulltorefresh 一个强大的拉动刷新开源项目,支持各种控件下 ...

  4. 【转】Android开源项目发现---ListView篇(持续更新)

    原文网址:http://blog.csdn.net/krislight/article/details/20211045 资料转载地址:https://github.com/Trinea/androi ...

  5. Android ListView滚动条配置完全解析

    滚动条的相关显示效果 先来看下ListView的滚动条有哪些显示效果. 滚动条自身的外观 这点不用说,就是滚动条自身的颜色,形状等. Track的外观 默认的ListView是没有设置Track的.为 ...

  6. ListView 字母导航排序

    一.概述 ListView字母导航排序,网上已经有很多代码和博客了, 这篇博文也是照搬网上的.  之所以写到这里,不是为了说明什么,只是为了以后自己查阅方便.本来公司要求实现expandablelis ...

  7. Android高手速成--第一部分 个性化控件(View)

    第一部分 个性化控件(View) 主要介绍那些不错个性化的View,包括ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.Pro ...

  8. 据说年薪30万的Android程序员必须知道的帖子

    Android中国开发精英 目前包括: Android开源项目第一篇--个性化控件(View)篇       包括ListView.ActionBar.Menu.ViewPager.Gallery.G ...

  9. Android开源项目分类汇总

    目前包括: Android开源项目第一篇——个性化控件(View)篇   包括ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView. ...

随机推荐

  1. dbcp的配置

    tomcat的 配置,进入conf->context.xml <Resource name="mysql"     auth="Container" ...

  2. 基于AngularJS的前端云组件最佳实践

    AngularJS是google设计和开发的一套前端开发框架,他能帮助开发人员更便捷地进行前端开发.AngularJS是为了克服HTML在构建应用上的不足而设计的,它非常全面且简单易学习,因此Angu ...

  3. 在现有代码中通过async/await实现并行

    在现有代码中通过async/await实现并行 一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色 ...

  4. wcf跨机器访问的问题

    wcf跨机器访问的问题 在wcf跨机器的访问中遇到了各种无法访问的问题,本人也是在通过个人解决问题的基础上发表一下自己的经验,如果还有其他方面可能影响wcf跨机器的问题,还希望大家多多发言! 好了废话 ...

  5. Web Api的安全性

    Web Api的安全性 系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 这一篇文章我们主要来探讨一下Web Api的安全性,到目前为止所有的 ...

  6. 【IOS开发】SimPholders的使用

    推荐一个Xocde开发工具 “SimPholders”,能够快速访问到你的模拟器文件夹,最重要的是完全免费! 官方地址

  7. ASP.NET虚拟路径小结

    一.虚拟路径的概念 “虚拟路径”是指请求 URL 中跟在服务器标识符后面的部分举例. 如绝对路径:http://www.mysite.com/MyApp/Default.aspx,其对应的虚拟路径为: ...

  8. SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表

    SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表 SSRS 系列 - 使用带参数的 MDX 查询实现一个分组聚合功能的报表 2013-10-09 23:09 by BI Wor ...

  9. Web API 入门指南

    Web API 入门指南 - 闲话安全2013-09-21 18:56 by 微软互联网开发支持, 231 阅读, 3 评论, 收藏, 编辑 Web API入门指南有些朋友回复问了些安全方面的问题,安 ...

  10. wcf传输Dataset大数据量 -压缩(一)

    wcf传输Dataset大数据量 -压缩(一) 由于WCF不能传输DataTable(不能序列化),所以更多项目中都会使用DataSet作为查询集合的首选返回类型,但是由于DataSet会生成很多的状 ...