十四、自定义联系人导航(上)---字母导航栏

联系人的界面需求是这样的:所有的联系人以首字母分组,右边是字母导航栏,滑动或者单击相应的字母,跳转到对应的分组。

首先实现这个自定义字母导航栏。

public class TextNavigationView extends View{

    private int mWidth, mHeight; // 控件宽高
    private int position = -1; // 点击位置
    private NavigationListener mNavigationListener;

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

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

    public TextNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode == MeasureSpec.EXACTLY) {
            // 精确赋值
            mWidth = widthSize;
        } else {
            // 模糊赋值
            mWidth = widthSize / 2; // 正常来说,这个地方的值需要我去计算的,mWidth = getPaddingLeft() + 实际宽度 + getPaddingRight()
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            // 精确赋值
            mHeight = heightSize;
        } else {
            // 模糊赋值
            mHeight = heightSize / 2; // 正常来说,这个地方的值需要我去计算的,mWidth = getPaddingTop() + 实际高度 + getPaddingBottom()
        }

        setMeasuredDimension(mWidth, mHeight);
    }

    private boolean isSlide; // 是否正常滑动
    private int yStart; // y开始坐标
    private int mTouchSlop; // 最小滑动距离
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                yStart = y;
                isSlide = false;
                break;
            case MotionEvent.ACTION_MOVE:
                if (y - yStart > mTouchSlop) {
                    isSlide = true;
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                position = y / (mHeight / NameUtil.firstPinyins.size());
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                position = y / (mHeight / NameUtil.firstPinyins.size());
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                if (mNavigationListener!=null && position>=0 && position<NameUtil.firstPinyins.size()) {
                    mNavigationListener.onSelect(NameUtil.firstPinyins.get(position));
                }
                position = -1; // 回归初始值
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Paint mPaint = new Paint();
        mPaint.setTextSize(40);
        for (int i=0; i<NameUtil.firstPinyins.size(); i++) {
            if (i == position) {
                // 选中
                mPaint.setColor(getContext().getResources().getColor(R.color.font_select));
            } else {
                // 未选中
                mPaint.setColor(getContext().getResources().getColor(R.color.font_not_select));
            }
            canvas.drawText(NameUtil.firstPinyins.get(i), 0, i * (mHeight / NameUtil.firstPinyins.size()), mPaint);
        }
    }

    public void setNavigationListener(NavigationListener mNavigationListener) {
        this.mNavigationListener = mNavigationListener;
    }

    /**
     * 导航栏回调监听
     */
    public interface NavigationListener {
        void onSelect(String text);
    }
}

姓名封装:

public class NameInfo {

    private String name; // 名字
    private String namePinyin; // 名字拼音
    private String firstPinyin; // 名字拼音第一个字母

    public String getName() {
        return name;
    }

    public String getNamePinyin() {
        return namePinyin;
    }

    public String getFirstPinyin() {
        return firstPinyin;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setNamePinyin(String namePinyin) {
        this.namePinyin = namePinyin;
    }

    public void setFirstPinyin(String firstPinyin) {
        this.firstPinyin = firstPinyin;
    }
}

姓名工具类:

public class NameUtil {

    // TODO: 2016/8/15 假设数据源
    private static String[] stringCitys = new String[]{
            "合肥", "张家界", "宿州", "淮北", "阜阳", "蚌埠", "淮南", "滁州",
            "马鞍山", "芜湖", "铜陵", "安庆", "安阳", "黄山", "六安", "巢湖",
            "池州", "宣城", "亳州", "明光", "天长", "桐城", "宁国",
            "徐州", "连云港", "宿迁", "淮安", "盐城", "扬州", "泰州",
            "南通", "镇江", "常州", "无锡", "苏州", "江阴", "宜兴",
            "邳州", "新沂", "金坛", "溧阳", "常熟", "张家港", "太仓",
            "昆山", "吴江", "如皋", "通州", "海门", "启东", "大丰",
            "东台", "高邮", "仪征", "江都", "扬中", "句容", "丹阳",
            "兴化", "姜堰", "泰兴", "靖江", "福州", "南平", "三明",
            "复兴", "高领", "共兴", "柯家寨", "匹克", "匹夫", "旗舰", "启航",
            "如阳", "如果", "科比", "韦德", "诺维斯基", "麦迪", "乔丹", "姚明"
    };

    public static List<String> firstPinyins; // 首字母列表
    public static List<NameInfo> nameInfos; // 名字列表

    /**
     * 汉字转换成拼音
     * @param name
     * @return
     */
    public static String toPinyin(CharSequence name) {
        if (name == null)
            return null;

        StringBuffer sb = new StringBuffer();
        for (int i=0; i<name.length(); i++) {
            sb.append(Pinyin.toPinyin(name.charAt(i)));
        }
        return sb.toString();
    }

    static {
        firstPinyins = new ArrayList<>();
        nameInfos = new ArrayList<>();

        String pinyin;
        for (int i=0; i<stringCitys.length; i++) {
            pinyin = toPinyin(stringCitys[i]);
            NameInfo nameInfo = new NameInfo();
            nameInfo.setName(stringCitys[i]);
            nameInfo.setNamePinyin(pinyin);
            nameInfo.setFirstPinyin(pinyin.charAt(0) + "");
            if (!firstPinyins.contains(pinyin.charAt(0) + "")) {
                // 去重
                firstPinyins.add(pinyin.charAt(0) + "");
            }
            nameInfos.add(nameInfo);
        }
        // 排序
        Collections.sort(firstPinyins);
        Collections.sort(nameInfos, new PinyinComparator());
    }

    /**
     * 拼音排序比较
     */
    static class PinyinComparator implements Comparator<NameInfo> {

        @Override
        public int compare(NameInfo first, NameInfo second) {
            return first.getNamePinyin().compareTo(second.getNamePinyin());
        }
    }

}
XML文件中布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:keng="http://schemas.android.com/apk/res-auto"
    style="@style/match_match">

    <com.hy.keng.ui.view.TitleBarView
        style="@style/title_bar_view"
        keng:titleText="@string/contact"
        />

    <com.hy.keng.ui.view.TextNavigationView
        android:id="@+id/navigation"
        android:layout_width="25dp"
        android:layout_height="350dp"
        android:layout_centerVertical="true"
        android:layout_alignParentRight="true"
        />

</RelativeLayout>
在主文件中使用和设置监听:

        mTextNavigationView = (TextNavigationView) view.findViewById(R.id.navigation);
        mTextNavigationView.setNavigationListener(new TextNavigationView.NavigationListener() {
            @Override
            public void onSelect(String text) {
                showToast(text);
            }
        });
当滑动到选定字母时,toast显示出来,效果图如下(只显示导航栏,还没有显示联系人内容):

还可优化的问题:

1、现在的布局的宽高是在布局中写死的,后续应该根据字母的多少自动布局;

2、字母居中对齐,而不是居左对齐


参考:

http://blog.csdn.net/tyk0910/article/details/52066891?utm_source=tuicool&utm_medium=referral

http://blog.csdn.net/harvic880925/article/details/38875149

http://blog.csdn.net/harvic880925/article/details/38926877

http://blog.csdn.net/harvic880925/article/details/50423762

http://blog.csdn.net/lmj623565791/article/details/24252901/

http://www.trinea.cn/android/touch-event-delivery-mechanism/

http://blog.csdn.net/awangyunke/article/details/22047987

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值