Android 地区快速索引

直接上效果图,先有个直观感受!
这里写图片描述 这里写图片描述

功能主要就是点击右边的字母索引,就可以跳到相应字母开头的地区列表并置顶。我们主要工作是自定义右边的字母索引这个View。

下面罗列一下相关知识点,后面直接贴代码。
(1)自定义View的基础知识,这里是继承View来实现
(2)自定义View中的接口回调(View状态变化时执行回调)
(3)RecyclerView的使用,当然也可以用ListView。

下面直接上代码:
首先是自定义LetterView.java:

public class LetterView extends View {

    private final String[] letters = {"A", "B", "C", "D", "E","F", "G","H","I","G","K","L","M","N","O","P","Q","R", "S","T","U","V","W","X","Y","Z"};
    private Paint mPaint;
    private int itemWidth;
    private int itemHeight;

    private int touchIndex = -1;  // 手指按住的字母索引

    public LetterView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setColor(Color.WHITE);
        mPaint.setTextSize(40);
        mPaint.setTypeface(Typeface.DEFAULT_BOLD);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Rect rect = new Rect();
        itemWidth = getWidth();
        itemHeight = getHeight()/letters.length;
        mPaint.getTextBounds(letters[0], 0,1,rect);
        for (int i = 0; i < letters.length; i++) {
            if (touchIndex == i)
            {
                mPaint.setColor(Color.GRAY);
            }else {
                mPaint.setColor(Color.WHITE);
            }
            canvas.drawText(letters[i], itemWidth/2-rect.width()/2, (itemHeight/2+rect.height()/2)+(i*itemHeight), mPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:


                break;
            case MotionEvent.ACTION_MOVE:
                float Y = event.getY();
                int index = (int) (Y/itemHeight);
                if (index != touchIndex)
                {
                    touchIndex = index;
                    invalidate();
                    if (onIndexChangeListener != null)
                    {
                        onIndexChangeListener.onIndexChange(letters[index]);
                    }
                }

                break;
            case MotionEvent.ACTION_UP:
                touchIndex = -1;
                invalidate();
                break;
        }

        return true;

    }

    public interface OnIndexChangeListener
    {
        void onIndexChange(String letter);
    }

    public OnIndexChangeListener onIndexChangeListener;

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

这个自定义View还是很简单的,主要注意字母位置的计算,在canvas上绘制文本是坐标是从左下角开始的。下面是一张示意图:
这里写图片描述
onTouchEvent方法里面主要实现了按下字母变色,抬起时恢复颜色的功能。

RegionActivity.java

public class RegionActivity extends Activity {
    private TextView mTextView;
    private LetterView letterView;
    private Handler handler;
    private RecyclerView mRecyclerView;
    private LinearLayoutManager mLinearLayoutManager;
    private boolean move;
    private ArrayList<RegionInfo> datas;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.letters);
        mTextView = (TextView) findViewById(R.id.letter);
        letterView = (LetterView) findViewById(R.id.letter_list);
        mRecyclerView = (RecyclerView) findViewById(R.id.recycler);


        handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                mTextView.setVisibility(View.GONE);
            }
        };
        letterView.setOnIndexChangeListener(new LetterView.OnIndexChangeListener() {
            @Override
            public void onIndexChange(String letter) {
                mTextView.setVisibility(View.VISIBLE);
                mTextView.setText(letter);
                handler.sendEmptyMessageDelayed(0, 3000);
                updateView(letter);
            }

        });

        datas = new ArrayList<>();
        datas.add(new RegionInfo("武汉"));
        datas.add(new RegionInfo("孝感"));
        datas.add(new RegionInfo("济南"));
        datas.add(new RegionInfo("北京"));
        datas.add(new RegionInfo("上海"));
        datas.add(new RegionInfo("深圳"));
        datas.add(new RegionInfo("广州"));
        datas.add(new RegionInfo("兰州"));
        datas.add(new RegionInfo("天津"));
        datas.add(new RegionInfo("安庆"));
        datas.add(new RegionInfo("合肥"));
        datas.add(new RegionInfo("六安"));
        datas.add(new RegionInfo("杭州"));
        datas.add(new RegionInfo("南京"));
        datas.add(new RegionInfo("苏州"));
        datas.add(new RegionInfo("成都"));
        datas.add(new RegionInfo("厦门"));
        datas.add(new RegionInfo("宁波"));
        datas.add(new RegionInfo("宁夏"));
        datas.add(new RegionInfo("郑州"));
        datas.add(new RegionInfo("福州"));
        datas.add(new RegionInfo("青岛"));
        datas.add(new RegionInfo("大连"));
        datas.add(new RegionInfo("哈尔滨"));
        datas.add(new RegionInfo("西安"));
        datas.add(new RegionInfo("石家庄"));
        datas.add(new RegionInfo("南昌"));
        datas.add(new RegionInfo("九江"));
        datas.add(new RegionInfo("吉林"));
        datas.add(new RegionInfo("沈阳"));
        datas.add(new RegionInfo("安康"));

        Collections.sort(datas, new Comparator<RegionInfo>() {
            @Override
            public int compare(RegionInfo lhs, RegionInfo rhs) {
                return lhs.getCode().compareTo(rhs.getCode());
            }
        });
        mRecyclerView.setAdapter(new MyAdapter(this, datas));
        mLinearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        mRecyclerView.setLayoutManager(mLinearLayoutManager);
    }

    private void updateView(String word)
    {
        for (int i = 0; i < datas.size(); i++)
        {
            RegionInfo regionInfo = datas.get(i);
            String code = regionInfo.getCode().substring(0,1);
            if (word.equals(code))
            {
                /**
                 * 准确定位到指定位置,并且将指定位置的item置顶,
                 * 若直接调用scrollToPosition(...)方法,则不会置顶。
                 **/
                mLinearLayoutManager.scrollToPositionWithOffset(i, 0);
                //从下向上填充
//                mLinearLayoutManager.setStackFromEnd(true);
                break;
            }
        }
    }
}

地区列表使用的RecyclerView,当然使用ListView也没问题,定位item也很简单,使用setSelection方法就可以搞定了。

RegionInfo.java

public class RegionInfo {
    private String addr;
    private String code;

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public RegionInfo(String addr) {
        this.addr = addr;
        //需要引入pinyin4j-2.5.0.jar
        this.code = PinYinUtil.getFirstPinYin(addr);
    }
}

RecyclerView的适配器:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    private Context context;
    private ArrayList<RegionInfo> datas;

    public MyAdapter(Context context, ArrayList<RegionInfo> list)
    {
        this.context = context;
        this.datas = list;
    }


    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = View.inflate(context, R.layout.item_recycler, null);
        return new MyViewHolder(view);
    }

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

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.setIsRecyclable(false); //解决itemView重用的问题,禁止会降低效率
        RegionInfo regionInfo = datas.get(position);
        String code = regionInfo.getCode().substring(0,1);
        if (position == 0)
        {
            holder.word.setText(code);
        }else {

            String preCode = datas.get(position-1).getCode().substring(0,1);
            if (preCode.equals(code))
            {
                holder.word.setVisibility(View.GONE);
            }else {
                holder.word.setText(code);
            }
        }
        holder.addr.setText(regionInfo.getAddr());

    }

    class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView addr ;
        public TextView word;
        public MyViewHolder(View itemView) {
            super(itemView);
            addr = (TextView) itemView.findViewById(R.id.tv_item);
            word = (TextView) itemView.findViewById(R.id.word);
        }
    }
}

这里基本就是全部代码了,布局文件很简单,按上面的思路做出来没问题了。

新年快到,这应该也是年前最后一次发文,提前祝大家新年快乐,这一年感觉还是充实了不少,2017希望与大家一起努力一起进步!

欢迎关注公众号。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值