仿联系人定位悬浮

效果图



导包:

compile 'com.android.support:recyclerview-v7:23.1.1'
compile 'ca.barrenechea.header-decor:header-decor:0.2.6'

这里还用到一个Jar包

链接: https://pan.baidu.com/s/1X-dcGEomFlmMWu3QYx_iOg 密码: xi73
免费下载


1.直接复制拼音工具类

public class PinYinUtils {

    public static String getPinYin(String text){
        char[] chars = text.toCharArray();

        StringBuilder sb = new StringBuilder();

        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();

        //取消音调
        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        //大写
        format.setCaseType(HanyuPinyinCaseType.UPPERCASE);

        for ( char ch : chars ) {
            if(Character.isWhitespace(ch)){
                //如果是空格
                continue;
            }

            if(ch > 128 || ch < -127){
                try{
                    //数组是有多音字
                    String[] array = PinyinHelper.toHanyuPinyinStringArray(ch, format);
                    sb.append(array[0]);

                }catch (BadHanyuPinyinOutputFormatCombination e){
                    e.getMessage();
                }
            }else{
                //#$%^
                return "#";
            }
        }

        return sb.toString();

    }

}


2.ToastUtils   这个随意可以不复制

public class Utils {

    private static Toast toast;

    public static void showToast(Context context, String text){
        if (toast == null)
        toast = Toast.makeText(context, text, Toast.LENGTH_SHORT);
        toast.setText(text);
        toast.show();
    }
}

3.我们先自定义一个右边的字母索引

public class QuickIndexBar extends View {
    private Paint paint;
    private float mCellHeight;
    private int mWidth;

    //26英文字母
    private static final String[] LETTERS = new String[]{"#","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"};

    private int mHeight;
    private float mTextHeight;
    private int currentIndex = -1;

    private OnLetterChangeListener onLetterChangeListener;

    public OnLetterChangeListener getOnLetterChangeListener() {
        return onLetterChangeListener;
    }

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

    //暴露接口
    public interface OnLetterChangeListener{
        void onLetterChange(String letter);
        void onReset();
    }

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

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

    public QuickIndexBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();

//        画笔默认是 黑色  设置为白色

        //设置字体大小
        paint.setTextSize(dip2px(context, 14));

        //抗锯齿
        paint.setAntiAlias(true);

        // 获取字体的高度
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();

        //  下边界  - 上边界

        //ceil 天花板    0.1  1
        mTextHeight = (float) Math.ceil(fontMetrics.descent - fontMetrics.ascent);
    }

    // 测量完成  改变的时候调用
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        //获取测量后的宽度和高度
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();

        //每个字母的高度
        mCellHeight = mHeight * 1.0f / LETTERS.length;
    }

    // 怎么画
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //遍历并绘制26英文字母
        for (int i = 0; i < LETTERS.length; i++) {
            String text = LETTERS[i];

            //测量字体宽度
            float mTextWidth = paint.measureText(text);

            //获取字母的xy坐标,坐标默认为字母左下角
            float x = mWidth / 2 - mTextWidth / 2;
            float y = mCellHeight / 2 + mTextHeight / 2 + mCellHeight * i;

            //判断当前索引并绘制相应的颜色
            if (currentIndex == i){
                //当索引为当前的字母时绘制的颜色
                paint.setColor(Color.parseColor("#000000"));
            }else{
                paint.setColor(Color.parseColor("#FF9696"));
            }
            // 字.画字();
            canvas.drawText(text, x, y, paint);
        }
    }

    //触摸事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 计算当前点击的 字母
                float downY = event.getY();
                // 1.1  ---  1  1.4 --- 1  1.5 --- 1
                currentIndex = (int) (downY / mCellHeight);
                if (currentIndex < 0 || currentIndex > LETTERS.length - 1) {
                } else {
//                    Utils.showToast(getContext(), LETTERS[currentIndex]);
                    if (onLetterChangeListener != null){
                        onLetterChangeListener.onLetterChange(LETTERS[currentIndex]);
                    }
                }

                //重新绘制
//                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算当前点击的 字母
                float moveY = event.getY();
                currentIndex = (int) (moveY / mCellHeight); // 1.1  ---  1  1.4 --- 1  1.5 --- 1

                if (currentIndex < 0 || currentIndex > LETTERS.length - 1) {
                } else {
                    if (onLetterChangeListener != null){
                        onLetterChangeListener.onLetterChange(LETTERS[currentIndex]);
                    }
                }
                //重新绘制
//                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                currentIndex = -1;
                if (onLetterChangeListener != null){
                    onLetterChangeListener.onReset();
                }

                break;
        }
        //重新绘制
        invalidate();

        //   返回true  为了收到 move  & up 事件
        return true;
    }



    /**
     * 根据手机的分辨率从 dip 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
}


4.然后到主函数

public class MainActivity extends AppCompatActivity {

    private android.support.v7.widget.RecyclerView rv;
    private QuickIndexBar QIBar;

    public static final String[] NAMES = new String[] { "宋江", "卢俊义", "吴用",
            "公孙胜", "关胜", "林冲", "秦明", "呼延灼", "花荣", "柴进", "李应", "朱仝", "鲁智深",
            "武松", "董平", "张清", "杨志", "徐宁", "索超", "戴宗", "刘唐", "李逵", "史进", "穆弘",
            "雷横", "李俊", "阮小二", "张横", "阮小五", " 张顺", "阮小七", "杨雄", "石秀", "解珍",
            " 解宝", "燕青", "朱武", "黄信", "孙立", "宣赞", "郝思文", "韩滔", "彭玘", "单廷珪",
            "魏定国", "萧让", "裴宣", "欧鹏", "邓飞", " 燕顺", "杨林", "凌振", "蒋敬", "吕方",
            "郭 盛", "安道全", "皇甫端", "王英", "扈三娘", "鲍旭", "樊瑞", "孔明", "孔亮", "项充",
            "李衮", "金大坚", "马麟", "童威", "童猛", "孟康", "侯健", "陈达", "杨春", "郑天寿",
            "陶宗旺", "宋清", "乐和", "龚旺", "丁得孙", "穆春", "曹正", "宋万", "杜迁", "薛永", "施恩",
            "周通", "李忠", "杜兴", "汤隆", "邹渊", "邹润", "朱富", "朱贵", "蔡福", "蔡庆", "李立",
            "李云", "焦挺", "石勇", "孙新", "顾大嫂", "张青", "孙二娘", " 王定六", "郁保四", "白胜",
            "时迁", "段景柱" };

    private List<ContactsBean> namelist = new ArrayList<>();
    private LinearLayoutManager manager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        this.rv = (RecyclerView) findViewById(R.id.id_recyclerview);
        QIBar = (QuickIndexBar) findViewById(R.id.qib);


        ContactsBean bean;
        for (int i = 0; i < NAMES.length; i++) {
            bean = new ContactsBean(NAMES[i]);
            namelist.add(bean);
        }
        //对集合进行排序
        Collections.sort(namelist);

        //条目间的间隔线
        DividerDecoration divider = new DividerDecoration.Builder(MainActivity.this)
                .setHeight(R.dimen.default_divider_height)
                .setColorResource(R.color.colorAccent)
                .build();

        manager = new LinearLayoutManager(MainActivity.this);

        rv.setHasFixedSize(true);
        rv.setLayoutManager(manager);
        rv.addItemDecoration(divider);

        final ContactsAdapter adapter = new ContactsAdapter(MainActivity.this, namelist);
        adapter.setOnItemClickListener(new ContactsAdapter.OnItemClickListener() {
            @Override
            public void onClick(int position,String name) {
                Toast.makeText(MainActivity.this,"您点击了"+position+"行"+name,Toast.LENGTH_SHORT).show();
            }
            @Override
            public void onLongClick(int position,String name) {
                Toast.makeText(MainActivity.this,"您长按点击了"+position+"行"+name,Toast.LENGTH_SHORT).show();
            }
        });

        //设置悬浮索引
        StickyHeaderDecoration decor = new StickyHeaderDecoration(adapter);

        rv.setAdapter(adapter);
        rv.addItemDecoration(decor, 1);


        //侧拉索引改变监听
        QIBar.setOnLetterChangeListener(new QuickIndexBar.OnLetterChangeListener() {
            @Override
            public void onLetterChange(String letter) {

                for (int i = 0; i < namelist.size(); i++) {

                    if(letter.equals(namelist.get(i).pinyin.charAt(0) + "")) {

                        int position = adapter.getPositionForSection(namelist.get(i).pinyin.charAt(0));
                        if(position != -1){
                            //滑动到指定位置
                            manager.scrollToPositionWithOffset(position,0);
                        }
                        break;
                    }
                }
            }

            @Override
            public void onReset() {

            }
        });
        QIBar.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        QIBar.setBackgroundColor(Color.parseColor("#ffe4e4"));
                        break;
                    case MotionEvent.ACTION_UP:
                        QIBar.setBackgroundColor(Color.argb(0,0,0,0));
                        break;
                }
                return false;
            }
        });
    }

}


5.然后到到主布局

<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.support.v7.widget.RecyclerView
        android:id="@+id/id_recyclerview"
        android:divider="#ffff0000"
        android:dividerHeight="10dp"
        android:background="#ffffff"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <fan.recyclerviewdemo.QuickIndexBar
        android:layout_alignParentRight="true"
        android:layout_width="23dp"
        android:layout_height="match_parent"
        android:padding="6dp"
        android:id="@+id/qib" />

</RelativeLayout>


6.再写一个Recycler适配器

public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.ViewHolder> implements
        StickyHeaderAdapter<ContactsAdapter.HeaderHolder> {

    private LayoutInflater mInflater;
    private List<ContactsBean> namelist;
    private OnItemClickListener mOnItemClickListener;

    private char lastChar = '\u0000';
    private int DisplayIndex = 0;

    public ContactsAdapter(Context context, List<ContactsBean> namelist) {
        mInflater = LayoutInflater.from(context);
        this.namelist = namelist;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        final View view = mInflater.inflate(R.layout.item_contacts_item, viewGroup, false);
        return new ViewHolder(view);
    }

    //条目文本填充
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int i) {
        viewHolder.text_item.setText(namelist.get(i).name);
        if( mOnItemClickListener!= null){
            viewHolder.itemView.setOnClickListener( new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mOnItemClickListener.onClick(i,namelist.get(i).name);
                }
            });
            viewHolder. itemView.setOnLongClickListener( new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    mOnItemClickListener.onLongClick(i,namelist.get(i).name);
                    return false;
                }
            });
        }
    }

    @Override
    public int getItemCount() {
        return namelist.size();
    }

    public long getHeaderId(int position) {

        //这里面的是如果当前position与之前position重复(内部判断)  则不显示悬浮标题栏  如果不一样则显示标题栏

        char ch = namelist.get(position).pinyin.charAt(0);

        if(lastChar == '\u0000'){

            lastChar = ch;

            return DisplayIndex;
        }else{

            if(lastChar == ch){

                return DisplayIndex;

            }else{

                lastChar = ch;
                DisplayIndex ++ ;
                return DisplayIndex;
            }

        }

    }

    public HeaderHolder onCreateHeaderViewHolder(ViewGroup parent) {
        final View view = mInflater.inflate(R.layout.item_contacts_head, parent, false);
        return new HeaderHolder(view);
    }

    //悬浮标题栏填充文本
    public void onBindHeaderViewHolder(HeaderHolder viewholder, int position) {
        viewholder.header.setText(namelist.get(position).pinyin.charAt(0) + "");
    }


    static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView text_item;
        public ImageView imageView_item;

        public ViewHolder(View itemView) {
            super(itemView);
            text_item = (TextView) itemView.findViewById(R.id.text_item);
            imageView_item= (ImageView) itemView.findViewById(R.id.image_item);
        }
    }

    static class HeaderHolder extends RecyclerView.ViewHolder {
        public TextView header;

        public HeaderHolder(View itemView) {
            super(itemView);

            header = (TextView) itemView;
        }
    }

    /**
     * 获得指定首字母的位置
     * @param ch
     * @return
     */
    public int getPositionForSection(char ch){

        for (int i = 0; i < getItemCount(); i++) {
            char firstChar = namelist.get(i).pinyin.charAt(0);
            if (firstChar == ch) {
                return i;
            }
        }
        return -1;

    }
    public interface OnItemClickListener{
        void onClick( int position,String name);
        void onLongClick( int position,String name);
    }
    public void setOnItemClickListener(OnItemClickListener onItemClickListener ){
        this. mOnItemClickListener=onItemClickListener;
    }

}

7.我们还需要头部和中间部分  然后创建这两个布局  

item_contacts_head

<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="20dp"
    android:background="#F4F4F4"
    android:textSize="12sp"
    android:gravity="center_vertical"
    android:paddingLeft="15dp"
    android:paddingRight="16dp"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#333333"
    tools:text="Sample text">

</TextView>

item_contacts_item

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:background="#ffffff">
    <ImageView
        android:id="@+id/image_item"
        android:layout_width="38dp"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"
        android:layout_marginLeft="15dp"/>

    <TextView
        android:id="@+id/text_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="35dp"
        android:gravity="center_vertical"
        android:textSize="13sp"
        tools:text="Sample text"
        />
</LinearLayout>


8.最后需要一个排序类

public class ContactsBean implements Comparable<ContactsBean>{

    public String name;
    public String pinyin;
    private ImageView image;

    public ContactsBean(String name){
        this.name = name;
        this.pinyin = PinYinUtils.getPinYin(name);
        this.image = image;

    }

    @Override
    public int compareTo(ContactsBean another) {
        return this.pinyin.compareTo(another.pinyin);
    }
}


免费下载


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值