自定义View,实现侧边索引

编程入门 行业动态 更新时间:2024-10-07 07:31:38

自定义View,实现<a href=https://www.elefans.com/category/jswz/34/1757136.html style=侧边索引"/>

自定义View,实现侧边索引

侧边索引的实现思路:

侧拉索引:音乐APP,即时通讯,电商选择城市,短信验证选择城市都有这个类型自定义控件
实现步骤:
1.绘制A-Z的字母列表(自绘式自定义控件)
2.响应触摸事件
3.提供监听回调
4.获取汉字的拼音,首字母(pinyin4J通过汉字得到她的拼音,只能一个字符一个字符去转换成拼音)
5.根据拼音排序
6.根据首字母分组
7.把监听回调和ListView结合起来
掌握解决问题的思路:把复杂的东西简单化,把复杂的东西分成尽可能小的模块把握住模块的关键点,一步一个脚印的去做,最终就可以实现复杂的效果

首先创建XML布局

<RelativeLayout xmlns:android=""
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"><ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/mylist"></ListView><com.example.customview.ui.QuickIndexBar
        android:id="@+id/action_bar"
        android:layout_width="30dp"
        android:layout_height="match_parent"
        android:layout_alignParentEnd="true"
        android:background="#ff0000" /></RelativeLayout>
新建一个item_person.xml

<LinearLayout xmlns:android=""
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" ><TextView
        android:id="@+id/tv_index"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="#666666"
        android:gravity="center_vertical"
        android:paddingLeft="15dp"
        android:text="A"
        android:textColor="#FFFFFF"
        android:textSize="18sp" /><TextView
        android:id="@+id/tv_name"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical"
        android:paddingLeft="15dp"
        android:text="宋江"
        android:textSize="22sp" /></LinearLayout>
创建工具类,根据汉字拿到拼音

public class HaoHan implements  Comparable<HaoHan>{private String name;private String pinyin;public HaoHan(String name) {this.name = name;//使用工具类,根据汉字拿到拼音
        this.pinyin= PinyinUtil.getPingyin(name);}public String getName() {return name;}public String getPinyin() {return pinyin;}@Override
    public int compareTo(HaoHan haoHan) {return this.pinyinpareTo(haoHan.pinyin);}
}
快速索引栏实现思路
* 1.继承VIew,复写构造方法,初始化画笔
* 2.在onDrawer方法里绘制字符
* 3.在onMeasure方法里测量高度
* 4.在onTouchEvent事件知道用户具体按住了那个字母
* 5.定义抽象方法,实现监听回调
新建一个自定义类,继承VIew,实现快速索引栏

public class QuickIndexBar extends View {private Paint paint;//A.要绘制的内容
    private static final String[] LETTERS = new String[]{"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 int cellWidth;private float cellHeight;private float y;private int currentIndex;public QuickIndexBar(Context context) {this(context, null);}public QuickIndexBar(Context context, AttributeSet attrs) {this(context, attrs, 0);}public QuickIndexBar(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);//初始化画笔
        initPaint();}private void initPaint() {//创建一个抗锯齿的画笔
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);//画笔文本加粗
        paint.setTypeface(Typeface.DEFAULT_BOLD);//颜色
        paint.setColor(Color.WHITE);}//完成侧拉索引
    @Override
    protected void onDraw(Canvas canvas) {//遍历了26个字母,进行坐标计算,进行绘制
        for (int i = 0; i < LETTERS.length; i++) {//从数组,根据i取出字母
            String letter = LETTERS[i];//计算x坐标
            float x = cellWidth * 0.5f - paint.measureText(letter) * 0.5f;//计算y坐标
            float y = cellHeight * 0.5f + paint.measureText(letter) * 0.5f + i * cellHeight;canvas.drawText(letter, x, y, paint);}}//完成侧拉索引的测量,得到单元格的宽高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取控件的宽高
        int mHeight = getMeasuredHeight();cellWidth = getMeasuredWidth();//获取单元格的高度,由自定义控件总高度,除以所有字母所占用的高度
        cellHeight = mHeight * 1.0f / LETTERS.length;//为了精确,避免四舍五入,我们把数转换为小数

    }//重写触摸事件,返回值为True,方起效果

    //记录用户上一次按下的位置,以便进行判断这一次所按住的位置是否还是上一次的位置,如果是的话,不做任何处理
    private int lastIndex=-1;@Override
    public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {//计算用户按到哪个字母的范围,主要是Y轴
            case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_MOVE://获取被点击到的字母索引
                y = event.getY();currentIndex = (int) (y / cellHeight);//为了防止一个字母按下,不停地重复调用,将进行判断,判断是否还是按着上一个字母,是的话就不做任何处理提供程序的性能
                if (currentIndex!=lastIndex){//为了防止角标越界,我们只在用户按住的Y轴值大于0,小于数组长度方执行
                    if (currentIndex>=0&&currentIndex<LETTERS.length){String letter = LETTERS[currentIndex];//设置回调的监听
                        if (mOnLetterUpdateListener!=null){mOnLetterUpdateListener.onLetterUpdate(letter);}lastIndex=currentIndex;}}break;case MotionEvent.ACTION_UP:break;}return true;}//c.定义接口
    public interface OnLetterUpdateListener {void onLetterUpdate(String string);}
//定义接口对象
    private OnLetterUpdateListener mOnLetterUpdateListener;//暴露方法,让外界传过来一个实现接口的类对象
    public void setmOnLetterUpdateListener(OnLetterUpdateListener onLetterUpdateListener) {mOnLetterUpdateListener = onLetterUpdateListener;}
}
工具类实现拼音

public class PinyinUtil {
public static String getPingyin(String string){HanyuPinyinOutputFormat format=new HanyuPinyinOutputFormat();//不要音标
    format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);//设置转换出大写字母
    format.setCaseType(HanyuPinyinCaseType.UPPERCASE);//我把得到的字符串,改为了字符数组,pinying4j只能一个字符一个字符去传唤拼音
    char[] chars = string.toCharArray();//创建了一个装字符的容器,stringBuilder()
    StringBuffer sb = new StringBuffer();for (int x=0;x<chars.length;x++){char c = chars[x];//如果时空格,跳过当前循环
        if (Character.isWhitespace(c)){continue;}//是不是汉字,如果不是汉字,直接拼写
        if (c>-128&&c<127){sb.append(c);}//是汉字,那么我们就获取拼音
        else{try {//获取某个字符对应的拼音,可以获取到多音字,
                String s = PinyinHelper.toHanyuPinyinStringArray(c, format)[0];sb.append(s);} catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) {badHanyuPinyinOutputFormatCombination.printStackTrace();}}}return sb.toString();
}
}
创建吐司的工具类,防止好久没有更新文字的问题

public class toastUtil {private static Toast toast;public static void showToast(Context context,String msg){if (toast==null){toast=Toast.makeText(context,"",Toast.LENGTH_SHORT);}toast.setText(msg);toast.show();}
}
创建文本类

public class cheeses {public static final String[] NAMES = new String[]{"宋江", "卢俊义", "吴用","公孙胜", "关胜", "林冲", "秦明", "呼延灼", "花荣", "柴进", "李应", "朱仝", "鲁智深","武松", "董平", "张清", "杨志", "徐宁", "索超", "戴宗", "刘唐", "李逵", "史进", "穆弘","雷横", "李俊", "阮小二", "张横", "阮小五", " 张顺", "阮小七", "杨雄", "石秀", "解珍"," 解宝", "燕青", "朱武", "黄信", "孙立", "宣赞", "郝思文", "韩滔", "彭玘", "单廷珪","魏定国", "萧让", "裴宣", "欧鹏", "邓飞", " 燕顺", "杨林", "凌振", "蒋敬", "吕方","郭 盛", "安道全", "皇甫端", "王英", "扈三娘", "鲍旭", "樊瑞", "孔明", "孔亮", "项充","李衮", "金大坚", "马麟", "童威", "童猛", "孟康", "侯健", "陈达", "杨春", "郑天寿","陶宗旺", "宋清", "乐和", "龚旺", "丁得孙", "穆春", "曹正", "宋万", "杜迁", "薛永", "施恩","周通", "李忠", "杜兴", "汤隆", "邹渊", "邹润", "朱富", "朱贵", "蔡福", "蔡庆", "李立","李云", "焦挺", "石勇", "孙新", "顾大嫂", "张青", "孙二娘", " 王定六", "郁保四", "白胜","时迁", "段景柱", "易宸锋"};
}
判断当前首字母和上一个条目首字母是否一致,不一致时,就显示全部的界面,一致时,就隐藏第一个界面

public class HaoHanAdapter extends BaseAdapter {private ArrayList<HaoHan> persons = new ArrayList<>();private final Context context;public HaoHanAdapter(ArrayList<HaoHan> persons, Context context) {this.persons = persons;this.context = context;}@Override
    public int getCount() {return persons.size();}@Override
    public Object getItem(int i) {return null;}@Override
    public long getItemId(int i) {return 0;}@Override
    public View getView(int position, View convertView, ViewGroup viewGroup) {View view;if (convertView == null) {view = View.inflate(context, R.layout.item_person, null);} else {view = convertView;}TextView tv_index = (TextView) view.findViewById(R.id.tv_index);TextView tv_name = (TextView) view.findViewById(R.id.tv_name);HaoHan haoHan = persons.get(position);//当前的首字母
        String currentStr = haoHan.getPinyin().charAt(0) + "";String indexStr=null;//如果是第一个名字,直接显示
        if (position == 0) {indexStr = currentStr;} else {//判断当前首字母和上一个条目的首字母是否一致,不一致显示完整的item界面
            String lastStr = persons.get(position - 1).getPinyin().charAt(0) + "";//判断两个参数是否一致,不一致就执行赋值的逻辑
            if (!TextUtils.equals(lastStr,currentStr)){//不一致时候赋值indexStr
                indexStr=currentStr;}}tv_index.setVisibility(indexStr!=null?View.VISIBLE:View.GONE);tv_index.setText(currentStr);tv_name.setText(haoHan.getName());return view;}
}
最后再MainActivity中实现

public class MainActivity extends AppCompatActivity {private ListView lv;private ArrayList<HaoHan> persons;@Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);QuickIndexBar bar= (QuickIndexBar) findViewById(R.id.action_bar);bar.setmOnLetterUpdateListener(new QuickIndexBar.OnLetterUpdateListener() {@Override
            public void onLetterUpdate(String string) {toastUtil.showToast(MainActivity.this,string);}});//view层
        lv = (ListView) findViewById(R.id.mylist);//d.model层,创建集合
        persons = new ArrayList<>();//d.填充并排列数据
        fillAndSortData(persons);//d.Controller层,设置适配器
        lv.setAdapter(new HaoHanAdapter(persons,this));//根据用户按住的字符,自动跳到对应的listveiw条目上
        bar.setmOnLetterUpdateListener(new QuickIndexBar.OnLetterUpdateListener() {@Override
            public void onLetterUpdate(String letter) {for (int x=0;x<persons.size();x++){String l = persons.get(x).getPinyin().charAt(0) + "";if (TextUtils.equals(letter,l)){//找到第一个首字母是letter条目
                        lv.setSelection(x);break;}}}});}/**
     * 填充数据并进行排序
     * @param persons
     */
    private void fillAndSortData(ArrayList<HaoHan> persons) {//填充
        for (int x=0;x<cheeses.NAMES.length;x++){String name = cheeses.NAMES[x];persons.add(new HaoHan(name));}//排序
        Collections.sort(persons);}
}


好了,这就可以实现一个自定义VIew的侧边索引了!

大家可以做一下!

更多推荐

自定义View,实现侧边索引

本文发布于:2024-02-28 08:02:56,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1768930.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:侧边   自定义   索引   View

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!