自定义快速查找字母控件

效果图如下:

首先看看布局文件,自定义的控件中包含一个 ListView,用于显示具体的数据内容:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_marginTop="20dp"
    android:background="#fffafafa"
    android:orientation="vertical" >
    <com.common.myletterview.LetterFilterListView
        android:id="@+id/letterView"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent" >
        <ListView
            android:id="@+id/listView"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:cacheColorHint="#00000000"
            android:divider="@null"/>
    </com.common.myletterview.LetterFilterListView>
</LinearLayout>

加载完 xml 中的 View 后,在自定义控件中添加靠右显示的一列字母控件、点击那列字母后的显示字母控件,这两个子控件均继承自 View,均采用自绘。

右边一列字母显示控件的绘制如下:

/**
   * 初始化.
   */
  private void init() {
   mLetters = new char[] { ‘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‘, ‘#‘ };
   mPaint = new Paint();
   mPaint.setColor(Color.parseColor("#949494"));
   mPaint.setTypeface(Typeface.DEFAULT_BOLD);
   mPaint.setTextSize(22);
   mPaint.setAntiAlias(true);
   mPaint.setTextAlign(Paint.Align.CENTER);
  }
  @Override
  protected void onDraw(Canvas canvas) {
   super.onDraw(canvas);
   float height = getHeight();
   mSingleHeight = height / mLetters.length;
   mWidthCenter = getMeasuredWidth() / (float) 2;
   for (int i = 0; i < mLetters.length; i++) {
    canvas.drawText(String.valueOf(mLetters[i]), mWidthCenter, mSingleHeight + (i * mSingleHeight), mPaint);
   }
  }

自定义实体实现每项内容与内容首字母的捆绑:

public class Letter implements Comparable<Letter> {
 private int id;
 private String name;//名字
 private String firstLetter;//名字首字母
 
 public Letter() {
  super();
 }
 public Letter(int id, String name, String firstLetter) {
  super();
  this.id = id;
  this.name = name;
  this.firstLetter = firstLetter;
 }
 public int getId() {
  return id;
 }
 public void setId(int id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getFirstLetter() {
  return firstLetter;
 }
 public void setFirstLetter(String firstLetter) {
  this.firstLetter = firstLetter;
 }
 
 @Override
 public int compareTo(Letter another) {
  if (this.getFirstLetter().equals("@")
    || another.getFirstLetter().equals("#")) {
   return -1;
  } else if (this.getFirstLetter().equals("#")
    || another.getFirstLetter().equals("@")) {
   return 1;
  } else {
   return this.getFirstLetter().compareTo(another.getFirstLetter());
  }
 }
}

适配器实现 SectionIndexer 接口,接口中的两个方法实现是关键,具体如下:

 /**
  * 根据ListView的当前位置获取分类的首字母的Char ascii值
  */
 public int getSectionForPosition(int position) {
  return mList.get(position).getFirstLetter().charAt(0);
 }
 /**
  * 根据分类的首字母的Char ascii值获取其第一次出现该首字母的位置
  */
 public int getPositionForSection(int section) {
  for (int i = 0; i < getCount(); i++) {
   String sortStr = mList.get(i).getFirstLetter();
   char firstChar = sortStr.toUpperCase().charAt(0);
   if (firstChar == section) {
    return i;
   }
  }
  return -1;
 }

适配器每个 Item 包括字母行跟内容行,如果当前位置等于该分类首字母的Char的位置 ,则显示字母行跟内容行,否则只显示出内容行,具体代码如下:

 //根据position获取分类的首字母的Char ascii值
  int section = getSectionForPosition(position);
  
  //如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现
  if(position == getPositionForSection(section)){
   viewHolder.tvLetter.setVisibility(View.VISIBLE);
   viewHolder.tvLetter.setText(letter.getFirstLetter());
  }else{
   viewHolder.tvLetter.setVisibility(View.GONE);
  }

在点击右边字母列表的时候,根据点击的位置重新计算点击了那个字母,从而作相应的定位,并显示选中的字母:

 public boolean onTouchEvent(MotionEvent event) {
   super.onTouchEvent(event);
   int index = 0;//点击的位置在 mLetters 中的索引
   int i = (int) event.getY();
   int div = (int) mSingleHeight;
   /** 重新计算出索引 */
   if (div != 0) {
    index = i / div;
   }
   if (index >= mLetters.length) {
    index = mLetters.length - 1;
   } else if (index < 0) {
    index = 0;
   }
   switch (event.getAction()) {
   case MotionEvent.ACTION_UP:
    break;
   case MotionEvent.ACTION_DOWN:
   case MotionEvent.ACTION_MOVE:
    mLetterSelectedView.setViewY(mSingleHeight + (index * mSingleHeight));//设置选中字母显示的y轴位置
    mLetterSelectedView.setSelectedLetter(mLetters[index]);//设置选中的字母
    if (mLetterSelectedView.getVisibility() == View.GONE)//若控件为隐藏状态,则显示
     mLetterSelectedView.setVisibility(View.VISIBLE);
    /** 显示1s后消失*/
    mLetterSelectedView.postDelayed(new Runnable() {
     @Override
     public void run() {
      // TODO Auto-generated method stub
      mLetterSelectedView.setVisibility(View.GONE);
     }
    }, 1000);
    
    if (mListView.getAdapter() != null) {
     ListAdapter listAdapter = (ListAdapter) mListView.getAdapter();
     if (mSectionIndexter == null) {
      mSectionIndexter = (SectionIndexer) listAdapter;
     }
     int position = mSectionIndexter.getPositionForSection(mLetters[index]);
     if (position == -1) {//列表中没有首字母为选中字母的的项
      return true;
     }
     mListView.setSelection(position);
    }
   }
   return true;
  }

仅仅看此博客可能很难理解具体的实现,大家可以结合完整的代码查看,完整代码:自定义快速查找字母控件

时间: 2024-10-24 21:53:03

自定义快速查找字母控件的相关文章

android - 自定义(组合)控件 + 自定义控件外观

转载:http://www.cnblogs.com/bill-joy/archive/2012/04/26/2471831.html android - 自定义(组合)控件 + 自定义控件外观 Android自定义View实现很简单 继承View,重写构造函数.onDraw,(onMeasure)等函数. 如果自定义的View需要有自定义的属性,需要在values下建立attrs.xml.在其中定义你的属性. 在使用到自定义View的xml布局文件中需要加入xmlns:前缀="http://sc

初步探讨WPF的ListView控件(涉及模板、查找子控件)

本文结合模板的应用初步介绍ListView的应用 一.Xaml中如何建立数据资源 大部分数据都会来自于后台代码,如何Xaml同样的建立数据源呢?比如建立一个学生List: 首先引入命名空间: xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" 然后代码如下:   <c:ArrayList x:Key="stuList">            <local:Student Id=

自定义下拉刷新控件

一.功能效果 1.在很多app中,在信息展示页面,当我们向下拖拽时,页面会加载最新的数据,并有一个短暂的提示控件出现,有些会有加载进度条,有些会记录加载日期.条目,有些还带有加载动画.其基本实现原理都相仿,本文中将探讨其实现原理,并封装出一个简单的下拉刷新控件 2.自定义刷新工具简单的示例 二.系统提供的下拉刷新工具 1.iOS6.0以后系统提供了自己的下拉刷新的控件:UIRefreshControl .例如,refreshControl,作为UITableViewController中的一个属

获取 AlertDialog自定义的布局 的控件

AlertDialog自定义的布局 效果图: 创建dialog方法的代码如下: 1 LayoutInflater inflater = getLayoutInflater(); 2 View layout = inflater.inflate(R.layout.dialog, 3 (ViewGroup) findViewById(R.id.dialog)); 4 new AlertDialog.Builder(this).setTitle("自定义布局").setView(layout

关于UITableView选中效果以及自定义cell上的控件响应事件

tableView默认的点击效果是:点击cell:A,出现点击效果,点另一个cell:B的时候,A的点击效果才会消失. 1.对于tableView,比较常用的效果,是点击表格行,出现效果,点击完毕,效果消失 那么就要在代码里做一些设置.代码如下: -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath

使用VideoView自定义一个播放器控件

介绍 最近要使用播放器做一个简单的视频播放功能,开始学习VideoView,在横竖屏切换的时候碰到了点麻烦,不过在查阅资料后总算是解决了.在写VideoView播放视频时候定义控制的代码全写在Actvity里了,写完一看我靠代码好乱,于是就写了个自定义的播放器控件,支持指定大小,可以横竖屏切换,手动左右滑动快进快退.好了,下面开始. 效果图 效果图有点卡,我也不知道为啥..... VideoView介绍 这个是我们实现视频播放最主要的控件,详细的介绍大家百度就去看,这里介绍几个常用的方法. 用于

自定义数字加减控件

1_自定义数字加减控件的要求 创建Module -NumberAddSubView A_输入的只能是数字,而且不能通过键盘输入 B_通过加减按钮操作数字 C_监听加减按钮 D_数组有最小值和最大值的限制 E_自定义属性 2.提供接口,让外界监听到数字的变化 1_设置接口 @Override public void onClick(View v) { if (v.getId() == R.id.btn_sub) { //Toast.makeText(mContext,"减",Toast.

查找子控件和父控件方法

一.查找某种类型的子控件,并返回一个List集合 public List<T> GetChildObjects<T>(DependencyObject obj, Type typename) where T : FrameworkElement        {            DependencyObject child = null;            List<T> childList = new List<T>(); for (int i

Android自定义View之组合控件 ---- LED数字时钟

先上图 LEDView效果如图所示. 之前看到一篇博客使用两个TextView实现了该效果,于是我想用自定义控件的方式实现一个LEDView,使用时即可直接使用该控件. 采用组合控件的方式,将两个TextView叠放在一起,再使用digital-7.ttf字体来显示数据,从而达到LED的效果.代码如下: LEDView.class package ione.zy.demo; import java.io.File; import java.util.Calendar; import java.u