直接上效果图,先有个直观感受!
功能主要就是点击右边的字母索引,就可以跳到相应字母开头的地区列表并置顶。我们主要工作是自定义右边的字母索引这个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希望与大家一起努力一起进步!
欢迎关注公众号。