自定义快速索引,仿微信好友查询列表

自定义视图,快速索引

先从自定义控件代码来看吧

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.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

/**
 * 自定义View 快速索引
 * @author 李帅帅
 * 
 */
public class QuickIndexView extends View {
    public QuickIndexView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setAntiAlias(true);
    }

    private float itemWidth;// item的宽度
    private float itemHight;// item的高度

    private String[] indexArr = { "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 Paint paint;

    /**
     * 测量
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        itemWidth = this.getWidth();
        itemHight = this.getHeight() / 26f;
    }

    /**
     * 画图
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < indexArr.length; i++) {
            if (i != index) {// 如果i不等于选中的item时
                paint.setColor(Color.BLACK);
                paint.setTextSize(16);
            } else {
                paint.setColor(Color.RED);// 当前操作的字母
                paint.setTextSize(25);
            }
            String word = indexArr[i];
            // 得到word的宽高
            Rect bounds = new Rect();
            paint.getTextBounds(word, 0, word.length(), bounds);
            int wordWidth = bounds.width();
            int wordHight = bounds.height();
            // 定义左下角的坐标
            float x = itemWidth / 2 - wordWidth / 2;
            float y = itemHight / 2 + wordHight / 2 + i * itemHight;
            // 画出文本
            canvas.drawText(word, x, y, paint);
        }
    }

    private int index = -1;// 操作的字母下标

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:// 按下
        case MotionEvent.ACTION_MOVE:// 移动
            float eventY = event.getY();
            int eventindex = (int) (eventY / itemHight);
            if (index != eventindex) {// 如果操作的index变化了
                // 强制更新
                invalidate();
                index = eventindex;
                // 通知activity更新textView
                if (onIndexChangeListener != null) {
                    if(index<26&&index>0){
                    onIndexChangeListener.OnIndexChanged(indexArr[index]);
                    }
                }
            }
            break;
        case MotionEvent.ACTION_UP:
            index = -1;
            invalidate();
            //当离开的时候
            if (onIndexChangeListener != null) {
//              onIndexChangeListener.OnUp();
            }
            break;
        default:
            break;
        }
        return true;// 消费
    }

    /**
     * 定义一个接口
     * 
     * @author 李帅帅
     */
    private OnIndexChangeListener onIndexChangeListener;

    public interface OnIndexChangeListener {
        // 当操作的下标改变时,按下时调用
        public void OnIndexChanged(String word);
//      //当离开时调用
//      public void OnUp();
    }

    public void setOnIndexChangeListener(OnIndexChangeListener onIndexChangeListener) {
        this.onIndexChangeListener = onIndexChangeListener;
    }

}

第一步我们写一个类来继承View 然后在重写测量方法。再次方法中获得item的宽高

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        itemWidth = this.getWidth();
        itemHight = this.getHeight() / 26f;
    }

第二步:就到我们从写绘制的方法了,在此方法中我们根据选中角标的位置来让绘制字母颜色,以及字母的宽高

private int index = -1;// 操作的字母下标
@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < indexArr.length; i++) {
            if (i != index) {// 如果i不等于选中的item时
                paint.setColor(Color.BLACK);
                paint.setTextSize(16);
            } else {
                paint.setColor(Color.RED);// 当前操作的字母
                paint.setTextSize(25);
            }
            String word = indexArr[i];
            // 得到word的宽高
            Rect bounds = new Rect();
            paint.getTextBounds(word, 0, word.length(), bounds);
            int wordWidth = bounds.width();
            int wordHight = bounds.height();
            // 定义左下角的坐标
            float x = itemWidth / 2 - wordWidth / 2;
            float y = itemHight / 2 + wordHight / 2 + i * itemHight;
            // 画出文本
            canvas.drawText(word, x, y, paint);
        }
    }

第三步: 也是最为重要的一步,就是onTouchEvent此方法了。在此方法中我们会判断down move up ,当我们按下和移动的时候动态的获得Y/itemheight ,这就是我们选中字母的坐标,如果选中的角标变化了,那么就调用这个invalidate();方法,让其强制重绘,当然在此方法中我们会写一个接口,供页面回调,也是参照源码监听来写的,up的时候也让其强制重绘,记得角标改成非法值.

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:// 按下
        case MotionEvent.ACTION_MOVE:// 移动
            float eventY = event.getY();
            int eventindex = (int) (eventY / itemHight);
            if (index != eventindex) {// 如果操作的index变化了
                // 强制更新
                invalidate();
                index = eventindex;
                // 通知activity更新textView
                if (onIndexChangeListener != null) {
                    if(index<26&&index>0){
                    onIndexChangeListener.OnIndexChanged(indexArr[index]);
                    }
                }
            }
            break;
        case MotionEvent.ACTION_UP:
            index = -1;
            invalidate();
            //当离开的时候
            if (onIndexChangeListener != null) {
//              onIndexChangeListener.OnUp();
            }
            break;
        default:
            break;
        }
        return true;// 消费
    }

最后这个是我们写的接口

    /**
     * 定义一个接口
     * 
     * @author 李帅帅
     */
    private OnIndexChangeListener onIndexChangeListener;

    public interface OnIndexChangeListener {
        // 当操作的下标改变时,按下时调用
        public void OnIndexChanged(String word);
//      //当离开时调用
//      public void OnUp();
    }

    public void setOnIndexChangeListener(OnIndexChangeListener onIndexChangeListener) {
        this.onIndexChangeListener = onIndexChangeListener;
    }

MainActivity的代码附上

/**
 * 页面一
 * 
 * @author admin
 * 
 */
public class OneFragment extends Fragment {

    private TextView mTextView;
    private QuickIndexView mQIV;
    private ListView mListView;
    private List<Friend> data = new ArrayList<Friend>();
    private FriendAdapter adapter;
    private Context mContext;

    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            // 隐藏自己
            mTextView.setVisibility(View.GONE);
        }
    };

    public OneFragment(FragmentActivity mContext) {
        this.mContext = mContext;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_one, container, false);
        initData();
        initViews(view);
        setListener();
        return view;
    }

    /**
     * 初始化试图
     */
    private void initViews(View view) {
        // 初始化
        mTextView = (TextView) view.findViewById(R.id.tv_main_word);
        mListView = (ListView) view.findViewById(R.id.lv_main);
        mQIV = (QuickIndexView) view.findViewById(R.id.qiv_main);
        // 显示列表
        adapter = new FriendAdapter(mContext, data);
        mListView.setAdapter(adapter);
    }

    /**
     * 设置监听
     */
    private void setListener() {
        // 设置监听
        mQIV.setOnIndexChangeListener(new QuickIndexView.OnIndexChangeListener() {

            @Override
            public void OnIndexChanged(String word) {
                mTextView.setText(word);
                mTextView.setVisibility(View.VISIBLE);

                // 移除未处理的消息
                handler.removeCallbacksAndMessages(null);
                handler.sendEmptyMessageDelayed(1, 1000);

                // 遍历data
                for (int i = 0; i < data.size(); i++) {
                    // 得到首字母
                    String firstPinYin = data.get(i).getPinyin().substring(0, 1);
                    if (word.equals(firstPinYin)) {
                        mListView.setSelection(i);
                        return;
                    }
                }
            }
        });
        //设置点击监听
        mListView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                    long arg3) {
                Toast.makeText(mContext, data.get(arg2).getName(), 0).show();
            }
        });
    }

    /**
     * 准备数据
     */
    private void initData() {
        data.add(new Friend("张晓飞"));
        data.add(new Friend("杨光福"));
        data.add(new Friend("胡继群"));
        data.add(new Friend("刘畅"));
        data.add(new Friend("钟泽兴"));
        data.add(new Friend("尹革新"));
        data.add(new Friend("安传鑫"));
        data.add(new Friend("张骞壬"));
        data.add(new Friend("温松"));
        data.add(new Friend("李凤秋"));
        data.add(new Friend("刘甫"));
        data.add(new Friend("娄全超"));
        data.add(new Friend("张猛"));
        data.add(new Friend("王英杰"));
        data.add(new Friend("李振南"));
        data.add(new Friend("孙仁政"));
        data.add(new Friend("唐春雷"));
        data.add(new Friend("牛鹏伟"));
        data.add(new Friend("姜宇航"));
        data.add(new Friend("刘挺"));
        data.add(new Friend("张洪瑞"));
        data.add(new Friend("张建忠"));
        data.add(new Friend("侯亚帅"));
        data.add(new Friend("刘帅"));
        data.add(new Friend("乔竞飞"));
        data.add(new Friend("徐雨健"));
        data.add(new Friend("吴亮"));
        data.add(new Friend("王兆霖"));
        data.add(new Friend("阿三"));
        Collections.sort(data);
    }

}

注意:我这里是接着上一篇架构的第一个OneFragment写的,如果是Activity页面,那么onCreateView就相当于onCreate方法,就是加载一个布局,然后其他类似.

fragment_one.Xml 代码,就是main_activity.xml

<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"
    tools:context="${relativePackage}.${activityClass}" >

    <ListView
        android:id="@+id/lv_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </ListView>

   <com.example.fragmenttest.views.QuickIndexView
        android:id="@+id/qiv_main"
        android:layout_width="30dp"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:background="@android:color/transparent" />

    <TextView
        android:id="@+id/tv_main_word"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:background="#66666666"
        android:text="A" 
        android:textColor="#EADAA9"
        android:textSize="40sp"
        android:gravity="center"
        android:visibility="gone"/>


</RelativeLayout>

FriendAdapter代码

import java.util.List;

import com.example.fragmenttest.R;
import com.example.fragmenttest.model.Friend;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class FriendAdapter extends BaseAdapter {
    private List<Friend> mDatas;
    private Context mContext;

    public FriendAdapter(Context mContext, List<Friend> data) {
        mDatas = data;
        this.mContext = mContext;
    }

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

    @Override
    public Object getItem(int position) {
        return mDatas.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = View.inflate(mContext, R.layout.item_one_fragment, null);
            holder.wordTV = (TextView) convertView .findViewById(R.id.tv_main_word);
            holder.nameTV = (TextView) convertView .findViewById(R.id.tv_item_name);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        Friend friend = mDatas.get(position);
        String word = friend.getPinyin().substring(0, 1);
        holder.wordTV.setText(word);
        holder.nameTV.setText(friend.getName());

        // 如果下标为0,那么让其显示
        if (position == 0) {
            holder.wordTV.setVisibility(View.VISIBLE);
        } else {
            // 取出上一个friend,并得到第一个word
            String preWord = mDatas.get(position - 1).getPinyin().substring(0, 1);
            if (word.equals(preWord)) {// 如果相同,那么就隐藏
                holder.wordTV.setVisibility(View.GONE);
            } else {// 如果不同,显示
                holder.wordTV.setVisibility(View.VISIBLE);
            }

        }
        return convertView;
    }

    class ViewHolder {
        public TextView wordTV;
        public TextView nameTV;
    }

}

item_one_fragment布局代码

<?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" >

    <TextView
        android:id="@+id/tv_main_word"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="A"
        android:textSize="18sp"
        android:background="#66666666" 
        android:padding="5dp"/>
    <TextView
        android:id="@+id/tv_item_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="阿三"
        android:textSize="18sp"
        android:padding="5dp"/>

</LinearLayout>

忘记了一个重要的工具类,中文转换为字母,当然我们需要下载一个pinyin4j-2.5.0.jar包

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;

/**
 * 将汉字转换为拼音
 * @author 李帅帅
 *
 */
public class PinYinUtils {
    /**
     * 得到指定汉字的拼音
     * 注意:不应该被频繁调用,它消耗一定内存
     * @param hanzi
     * @return
     */
    public static String getPinYin(String hanzi){
        String pinyin = "";

        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();//控制转换是否大小写,是否带音标
        format.setCaseType(HanyuPinyinCaseType.UPPERCASE);//大写
        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);

        //由于不能直接对多个汉字转换,只能对单个汉字转换
        char[] arr = hanzi.toCharArray();
        for (int i = 0; i < arr.length; i++) {
            if(Character.isWhitespace(arr[i]))continue;//如果是空格,则不处理,进行下次遍历

            //汉字是2个字节存储,肯定大于127,所以大于127就可以当为汉字转换
            if(arr[i]>127){
                try {
                    //由于多音字的存在,单 dan shan
                    String[] pinyinArr = PinyinHelper.toHanyuPinyinStringArray(arr[i], format);

                    if(pinyinArr!=null){
                        pinyin += pinyinArr[0];
                    }else {
                        pinyin += arr[i];
                    }
                } catch (BadHanyuPinyinOutputFormatCombination e) {
                    e.printStackTrace();
                    //不是正确的汉字
                    pinyin += arr[i];
                }
            }else {
                //不是汉字,
                pinyin += arr[i];
            }
        }
        return pinyin;
    }
}

这里写图片描述
这里写图片描述

希望能够帮到大家,谢谢

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值