编程

Android自定义A-Z 列表

2020-01-19  本文已影响0人  小白彡

项目中需要用到A-Z的列表,点击字母可以滚动到列表中对应字母位置,在百度谷歌上转了一圈,没看见合适的,又只能自己来了。。。。。。。。

先看下怎么使用:
xml中:

<com.example.siderquickbar.SiderQuickBarView
        android:id="@+id/siderbar_letters"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:siderQuickItemTypebgColor="#3F88FF"
        app:sliderLetterColor="#808080"
        app:sliderLetterWidth="12sp"
        app:siderQuickItemTypeTextWidth="14sp"
        app:siderQuickItemTypeTextColor="#FFFFFF"/>

java中:

public class MainActivity extends AppCompatActivity {
    private String[] str = new String[]{"一","二","三","死","一","一","一","一",
            "商店","一","重新","我","as","第","是","把","留",
            "阿萨德","as","都是","一","一","商店","商店","发","一",
            "一","阿萨德","一","商店",};


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        List<ItemBeans> list = new ArrayList<>();
        for(int i=0;i<str.length;i++){
            ItemBeans itemBeans = new ItemBeans();
            itemBeans.setType(i+1);
            itemBeans.setValues(str[i]);
            list.add(itemBeans);
        }

        SiderQuickBarView siderQuickBarView = findViewById(R.id.siderbar_letters);
        siderQuickBarView.setLetters(list)
                         .setOnItemClickListener(letterBean ->
                        Toast.makeText(this,"type==>"+letterBean.getType()+"  value==>"+letterBean.getLetterName(),Toast.LENGTH_SHORT).show()
                );
    }
}

自定义属性:

 <!--右侧字体颜色-->
        <attr name="sliderLetterColor" format="color"> </attr>
        <!--右侧字体大小-->
        <attr name="sliderLetterWidth" format="dimension"> </attr>
        <!--分类字母背景颜色-->
        <attr name="siderQuickItemTypebgColor" format="color"> </attr>
        <!--分类字母颜色-->
        <attr name="siderQuickItemTypeTextColor" format="color"> </attr>
        <!--分类字母大小-->
        <attr name="siderQuickItemTypeTextWidth" format="dimension"> </attr>
        <!--列表项颜色-->
        <attr name="itemColor" format="color"> </attr>

只需要把任意文字转成list ,按规则传入,即可自动按字母排序,点击后可返回当前字母和字母对应的key值。

QQ图片20200119200829.jpg QQ图片20200119200837.jpg

下面说一下实现的思路:
1.控件由两部分构成:下面的RecyclerView和右侧的自定义字母,使用的是帧布局。
2.将输入的文字按字母分类。
3.将分类好的字母放入新的list中,字母和字母对应的文字用type进行区分。
4.为RecyclerView创建适配器,添加点击事件。
5.为字母添加点击事件,并使RecyclerView滑动。
6.封装自定义属性。

好了,放一下主要代码:

public class SiderQuickBarView extends FrameLayout {
    private RecyclerView rvLetter;
    private SliderLetterView sliderLetterView;
    private GuideBbar guideBbar;
    private Context context;
    private List<LetterBean> letterBeans;
    private List<CityBean> cityBeans;

    //自定义属性
    private int siderQuickItemTypebgColor;
    private int siderQuickItemTypeTvColor;
    private float siderQuickItemTypeTextWidth;

    private int sliderLetterColor;
    private int sliderLetterWidth;

    private OnItemClickListener onItemClickListener;

    public SiderQuickBarView(Context context) {
        super(context);
        initView(context);
    }


    public SiderQuickBarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SiderQuickBarView);
        siderQuickItemTypebgColor = array.getColor(R.styleable.SiderQuickBarView_siderQuickItemTypebgColor, ContextCompat.getColor(context,R.color.colorAccent));
        siderQuickItemTypeTvColor = array.getColor(R.styleable.SiderQuickBarView_siderQuickItemTypeTextColor,ContextCompat.getColor(context,R.color.whilte));
        siderQuickItemTypeTextWidth = array.getDimensionPixelSize(R.styleable.SiderQuickBarView_siderQuickItemTypeTextWidth,20);

        sliderLetterColor = array.getColor(R.styleable.SiderQuickBarView_sliderLetterColor,ContextCompat.getColor(context,R.color.black));
        sliderLetterWidth = array.getDimensionPixelSize(R.styleable.SiderQuickBarView_sliderLetterWidth,50);
        initView(context);
        array.recycle();
    }

    private void initView(Context context) {
        this.context = context;
        //左边列表
        rvLetter = new RecyclerView(context);
        rvLetter.setLayoutManager(new LinearLayoutManager(context));
        LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
        rvLetter.setLayoutParams(layoutParams);

        //右边字母列表
        sliderLetterView = new SliderLetterView(context,sliderLetterColor,sliderLetterWidth);
        LayoutParams layoutParams1 = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
        layoutParams1.gravity = Gravity.END;
        layoutParams1.bottomMargin = 10;
        sliderLetterView.setLayoutParams(layoutParams1);

        //GuiBar
        guideBbar = new GuideBbar(context);
        FrameLayout.LayoutParams layoutParams2  = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
        layoutParams2.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
        layoutParams2.bottomMargin = 100;
        guideBbar.setLayoutParams(layoutParams2);

        addView(rvLetter);
        addView(sliderLetterView);
//        addView(guideBbar);
        //字母监听
        setListener();
    }

    private void setListener() {
        sliderLetterView.setOnLetterTouchListener(letter -> {
            for(int i=0;i<letterBeans.size();i++){
                if(letter.equals(letterBeans.get(i).getLetterName())){
                    moveToPosition(i);
                    break;
                }
            }
            Toast.makeText(context,letter,Toast.LENGTH_SHORT).show();
        });

        rvLetter.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                double scrollY = recyclerView.computeVerticalScrollOffset();
                double s = recyclerView.computeVerticalScrollExtent();
                double sum = recyclerView.computeVerticalScrollRange()-s;
                double f = scrollY/sum;
                guideBbar.setGuideX(f);
            }
        });

    }

    private void moveToPosition(int position) {
        if (position != -1) {
            rvLetter.scrollToPosition(position);
            LinearLayoutManager mLayoutManager =
                    (LinearLayoutManager) rvLetter.getLayoutManager();
            assert mLayoutManager != null;
            mLayoutManager.scrollToPositionWithOffset(position, 0);
        }
    }

    public SiderQuickBarView setLetters(List<ItemBeans> letters){
        Map<String, List<ItemBeans>> letterMap = letters.stream().collect(Collectors.groupingBy(letter -> PinYinUtils.getFirstLetter(letter.getValues())));
        Iterator<Map.Entry<String, List<ItemBeans>>> itLetter = letterMap.entrySet().iterator();
        cityBeans = new ArrayList<>();
        letterBeans = new ArrayList<>();
        Iterator<String> keySet = letterMap.keySet().iterator();
        while (keySet.hasNext()){
            CityBean cityBean = new CityBean();
            cityBean.setKey(keySet.next());
            cityBeans.add(cityBean);
        }
        //对字母排序
        SiderComparator siderComparator = new SiderComparator();
        Collections.sort(cityBeans,siderComparator);

        cityBeans.forEach(cityBean -> {
            List<LetterBean> contentList = new ArrayList<LetterBean>();
            List<ItemBeans> itemBeans = letterMap.get(cityBean.getKey());
            LetterBean letterBean = new LetterBean();
            letterBean.setLetterType(LetterType.title);
            letterBean.setLetterName(cityBean.getKey());
            contentList.add(letterBean);
            itemBeans.forEach(item -> {
                LetterBean letterBean1 = new LetterBean();
                letterBean1.setLetterType(LetterType.contet);
                letterBean1.setType(item.getType());
                letterBean1.setLetterName(item.getValues());
                contentList.add(letterBean1);
            });
            cityBean.setBeanList(contentList);
        });

        cityBeans.forEach(cityBean -> {
            letterBeans.addAll(cityBean.getBeanList());
        });
        LetterListAdapter letterListAdapter = new LetterListAdapter(context, letterBeans, siderQuickItemTypeTvColor, siderQuickItemTypebgColor, siderQuickItemTypeTextWidth);
        rvLetter.setAdapter(letterListAdapter);
        letterListAdapter.setOnItemClickListener((adapter, position) -> {
            LetterListAdapter la = (LetterListAdapter) adapter;
            LetterBean item = la.getItem(position);
            if(onItemClickListener != null){
                onItemClickListener.onClick(item);
            }

        });
        return this;
    }

    interface OnItemClickListener{
        void onClick(LetterBean letterBean);
    }

    public SiderQuickBarView setOnItemClickListener(OnItemClickListener onItemClickListener){
        this.onItemClickListener = onItemClickListener;
        return  this;
    }


    class SiderComparator implements Comparator<CityBean> {

        @Override
        public int compare(CityBean cityBean1, CityBean cityBean2) {
            return cityBean1.getKey().compareTo(cityBean2.getKey());
        }
    }


    @Retention(RetentionPolicy.SOURCE)
    @IntDef({LetterType.title,LetterType.contet})
    public @interface LetterType{
        int title = 0;
        int contet = 1;
    }
}

这个就是整个控件了,继承自FrameLayout,包含一个RecyclerView和右边的自定义字母(忽略被注释的View)

这里最主要的就是将输入的字符串信息,按照字母分类,我这里用到了一个第三库:

 implementation 'com.belerweb:pinyin4j:2.5.1'

外加这个库的一个工具类:

public class PinYinUtils {
    /**
     * 将字符串中的中文转化为拼音,其他字符不变
     *
     * @param inputString
     * @return
     */
    public static String getPingYin(String inputString) {
        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
        format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        format.setVCharType(HanyuPinyinVCharType.WITH_V);

        char[] input = inputString.trim().toCharArray();
        String output = "";
        try {
            for (int i = 0; i < input.length; i++) {
                if (java.lang.Character.toString(input[i]).matches("[\\u4E00-\\u9FA5]+")) {
                    String[] temp = PinyinHelper.toHanyuPinyinStringArray(input[i], format);
                    output += temp[0];
                } else {
                    output += java.lang.Character.toString(input[i]);
                }
            }
        } catch (BadHanyuPinyinOutputFormatCombination e) {
            e.printStackTrace();
        }
        return output;
    }
    /**
     * 获取汉字串拼音首字母,英文字符不变
     * @param chinese 汉字串
     * @return 汉语拼音首字母
     */
    public static String getFirstSpell(String chinese) {
        StringBuffer pybf = new StringBuffer();
        char[] arr = chinese.toCharArray();
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > 128) {
                try {
                    String[] temp = PinyinHelper.toHanyuPinyinStringArray(arr[i], defaultFormat);
                    if (temp != null) {
                        pybf.append(temp[0].charAt(0));
                    }
                } catch (BadHanyuPinyinOutputFormatCombination e) {
                    e.printStackTrace();
                }
            } else {
                pybf.append(arr[i]);
            }
        }
        return pybf.toString().replaceAll("\\W", "").trim();
    }
    /**
     * 获取汉字串拼音,英文字符不变
     * @param chinese 汉字串
     * @return 汉语拼音
     */
    public static String getFullSpell(String chinese) {
        StringBuffer pybf = new StringBuffer();
        char[] arr = chinese.toCharArray();
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > 128) {
                try {
                    pybf.append(PinyinHelper.toHanyuPinyinStringArray(arr[i], defaultFormat)[0]);
                } catch (BadHanyuPinyinOutputFormatCombination e) {
                    e.printStackTrace();
                }
            } else {
                pybf.append(arr[i]);
            }
        }
        return pybf.toString();
    }

    /** 获取首字母
     * @param letter
     * @return
     */
    public static String getFirstLetter(String letter){
        StringBuffer pybf = new StringBuffer();
        char[] arr = letter.toCharArray();
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > 128) {
                try {
                    pybf.append(PinyinHelper.toHanyuPinyinStringArray(arr[i], defaultFormat)[0]);
                } catch (BadHanyuPinyinOutputFormatCombination e) {
                    e.printStackTrace();
                }
            } else {
                pybf.append(arr[i]);
            }
        }
        return pybf.substring(0,1);
    }
}

这个工具类的作用,可以将文字的首字母给取出来, 那么取出来干嘛呢??
下面我正好借助lamda表达式,根据首字母将数据进行分组:

Map<String, List<ItemBeans>> letterMap = letters.stream().collect(Collectors.groupingBy(letter -> PinYinUtils.getFirstLetter(letter.getValues())));

分完组之后的数据就是一个map集合 map的key就是 各数据对应的首字母,value就是各首字母对应的数据,再将map中的数据,放入一个新的list中,所以现在混乱数据就变成了一组组有序集合了,但现在还不够,因为是按照A-Z排序,目前不是按A-Z,可能是g,f,a,d 这样乱
排序的,只是把数据都放在了它们各自的首字母下面而已,那么,下面就借助Comparator这个接口让字母按照A-Z的顺序排序:

class SiderComparator implements Comparator<CityBean> {

        @Override
        public int compare(CityBean cityBean1, CityBean cityBean2) {
            return cityBean1.getKey().compareTo(cityBean2.getKey());
        }
    }

        //对字母排序
        SiderComparator siderComparator = new SiderComparator();
        Collections.sort(cityBeans,siderComparator);

排序完成之后,这个list就可以像普通的list一样放入RecyclerView的适配器中使用了。

下面看一下自定义的字母:

/**
 * 右边字母自定义
 * @author amggy
 */
public class SliderLetterView extends View {
    private Paint paint;
    private String[] letters;
    private double itemHeight;
    private int position;
    private OnLetterTouchListener onLetterTouchListener;

    private int paintColor;
    private int paintStrokWith;

    public SliderLetterView(Context context,int paintColor,int paintStrokWith) {
        super(context);
        this.paintColor = paintColor;
        this.paintStrokWith = paintStrokWith;
        initView(context);
    }

    public SliderLetterView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView(context);

    }

    public SliderLetterView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context) {
        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"};
        position = 0;
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setColor(paintColor);
        paint.setTextSize(paintStrokWith);
        paint.setTextAlign(Paint.Align.CENTER);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        double screenHeight = getHeight();
        itemHeight = screenHeight /letters.length;
        float letterX = (float)getWidth()/2;
        for(int i=0;i<letters.length;i++){
            canvas.drawText(letters[i],letterX,(float) ((i+1)*itemHeight),paint);
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                float eventY = event.getY();
                position = (int) (eventY/itemHeight);
                if(onLetterTouchListener != null){
                    onLetterTouchListener.onTouch(letters[position]);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                break;
            default:
                break;
        }
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int with = MeasureSpec.getSize(widthMeasureSpec);
        int withMode = MeasureSpec.getMode(widthMeasureSpec);

        int high = MeasureSpec.getSize(heightMeasureSpec);
        int highMode = MeasureSpec.getMode(heightMeasureSpec);

        int w = 0;
        int h = 0;

        switch(withMode){
            case MeasureSpec.EXACTLY:
                w = with;
                break;
            case MeasureSpec.AT_MOST:
                w = 80;
                break;
            default:
                break;
        }

        switch(highMode){
            case MeasureSpec.EXACTLY:
                h = high;
                break;
            case MeasureSpec.AT_MOST:
                h = getMeasuredHeight();
                break;
            default:
                break;
        }
        setMeasuredDimension(w,h);
    }


    public void setOnLetterTouchListener(OnLetterTouchListener onLetterTouchListener){
        this.onLetterTouchListener = onLetterTouchListener;
    }

    public interface OnLetterTouchListener{
        /** 返回选中字母
         * @param letter 字母
         */
        void onTouch(String letter);
    }
}

自定义字母继承自View,按照控件的高度,等分的绘制字母,添加onTouch事件,将触碰到的字母返回出去,给RecyclerView滚动到指定位置做准备。

思路大概就是这个样子,只要思路是对的,东西就好写了。。。。完

慢。提前祝各位新年快乐!!!!

上一篇下一篇

猜你喜欢

热点阅读