android 自定義view,字母排序(仿微信好友列表)

一:簡言

一個月沒有寫博客了,公司項目比較忙,最近發現公司用到一個知識點,所以抽時間通過博客的形式分享給大家,該知識點,模仿微信的好友列表,通過字母索引進行定位,該功能主要通過幾個知識點實現。下面會一一講解。

二:具體說下思路

  • 1 :整體用到的控件,list view(recyclerView原理一樣),自定義view,漢字轉拼音的utils工具類.
  • 2:創建一個自定義類繼承view,實現側面滑動字母顯示字母索引數據,說下自定義View,首先自定義類繼承View重寫三個構造方法,重寫
  • onMeasure()的方法用於測量View的寬高,onSizeChanged()方法改變當前控件大小的時候調用,onDarw()方法繪製View,
  • onTouchEvent()方法用於手勢監聽,自定義接口用於保存點擊了那個字母的值。
  • 3:創建一個person的實體bean,實現列表數據的展示,
  • 4。通過將漢字通過拼音的形式進行分類,添加一個pinyinj-2.5.0.jar,實現轉換,(稍後代碼會詳細介紹)。
  • 5:將數據顯示出來,實現效果;

三:實現效果

 

  • 四:通過代碼的形式介紹步驟:

1)首先實現實體bean(Person)

public class Person {

    private String name;

    private String pinyin;

    public Person(String name){
        this.name = name;
        this.pinyin = PinYinUtils.getPinYin(name);
    }

    public String getPinyin() {
        return pinyin;
    }

    public void setPinyin(String pinyin) {
        this.pinyin = pinyin;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", pinyin='" + pinyin + '\'' +
                '}';
    }
}

該類沒有講解的必要,直接刀幣下一個類:

2)拼音轉換工具類(PinYinUtils)

首先添加這個jar包(稍後分享)

然後創建該類(代碼如下)

public class PinYinUtils {
    /**
     * 得到指定漢字的拼音
     * 注意:不應該被頻繁調用,它消耗一定內存
     * @param hanzi
     * @return
     */
    public static String getPinYin(String hanzi){
        String pinyin = "";

        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();//控制轉換是否大小寫,是否帶音標
        format.setCaseType(HanyuPinyinCaseType.UPPERCASE);//大寫
        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);

        //由於不能直接對多個漢字轉換,只能對單個漢字轉換
        char[] arr = hanzi.toCharArray();
        for (int i = 0; i < arr.length; i++) {
            if(Character.isWhitespace(arr[i]))continue;//如果是空格,則不處理,進行下次遍歷

            //漢字是2個字節存儲,肯定大於127,所以大於127就可以當爲漢字轉換
            if(arr[i]>127){
                try {
                    //由於多音字的存在,單 dan shan
                    String[] pinyinArr = PinyinHelper.toHanyuPinyinStringArray(arr[i], format);

                    if(pinyinArr!=null){
                        pinyin += pinyinArr[0];
                    }else {
                        pinyin += arr[i];
                    }
                } catch (BadHanyuPinyinOutputFormatCombination e) {
                    e.printStackTrace();
                    //不是正確的漢字
                    pinyin += arr[i];
                }
            }else {
                //不是漢字,
                pinyin += arr[i];
            }
        }
        return pinyin;
    }
}

3)重點來了,主要實現的功能,自定義view(IndexView)

/**
 * Created by wk先森
 * 快速索引
 * 繪製快速索引的字母
 * 1.二十六個字母 放入集合中
 * 2.在onMeaus中計算每條的寬和高 itemHeight itemWidth wordHeight wordX wordY
 * <p>
 * 手指按下 文字變色
 * 重寫onTouchEvent方法  返回true
 * 在Down和move中 計算
 * int touchIndex = Y/itemHeight  強制繪製
 * <p>
 * 2.在onDraw方法對於該下包畫筆變色
 * <p>
 * 3。在Up的時候 touchIndex  = -1;
 * 強制繪製
 */

public class IndexView extends View {
    /**
     * 每條的寬和高
     */
    private int itemWidth;
    private int itemHeight;

    Paint paint;

    private String[] words = {"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  touchIndex = -1;


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

    private void initView(Context context) {
        paint = new Paint();
        paint.setColor(Color.BLACK);//設置顏色
        paint.setAntiAlias(true);
        paint.setTypeface(Typeface.DEFAULT_BOLD);
    }

    /**
     * 測量方法
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        itemWidth = getMeasuredWidth();
        itemHeight = getMeasuredHeight() / words.length;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < words.length; i++) {

            if(touchIndex == i){
               //設置灰色
                paint.setColor(Color.GRAY);
            }else{
                //設置白色
                paint.setColor(Color.BLACK);
            }

            String word = words[i];

            Rect rect = new Rect();
            //0,1 取一個字母
            paint.getTextBounds(word, 0, 1, rect);
            int wordWidth = rect.width();
            int wordHeight = rect.height();

            //計算每個字母在視圖上的座標
            float wordX = itemWidth / 2 - wordWidth / 2;
            float wordY = itemHeight / 2 + wordHeight / 2 + i * itemHeight;

            canvas.drawText(word, wordX, wordY, paint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent 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;//強制繪製 onDraw();
                    invalidate();
                    if(onIndexChangeListener != null && touchIndex<words.length){
                       onIndexChangeListener.OnIndexChange(words[touchIndex]);
                    }
                }
                break;
            case MotionEvent.ACTION_UP :
                touchIndex = -1;
                invalidate();
                break;
        }

        return true;
    }

    /**
     * 字母下標索引變化監聽器
     * */
    public interface OnIndexChangeListener{
        /**
         * 擋字幕下標位置發生變化時候回調
         * */
        void OnIndexChange(String word);
    }

    private OnIndexChangeListener onIndexChangeListener;

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

5)通過activity實現該功能(MainActivity)

該類需要關注的點:

Toast顯示的時間

public class MainActivity extends AppCompatActivity {

    private ListView lvMain;
    private TextView tvWord;
    private IndexView ivWords;

    private Handler handler = new Handler();
    /**
     * 聯繫人的集合
     */
    private ArrayList<Person> persons;
    private  IndexAdapter adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        lvMain = findViewById(R.id.lv_main);
        tvWord = findViewById(R.id.tv_word);
        ivWords = findViewById(R.id.iv_words);
        //設置監聽字母下標索引變化
        ivWords.setOnIndexChangeListener(new IndexView.OnIndexChangeListener() {
            /**
             * 回傳是字母
             * */
            @Override
            public void OnIndexChange(String word) {
                updateWord(word);
                updateListView(word);//A~Z
            }
        });
        initData();
        adapter = new IndexAdapter();
        lvMain.setAdapter(adapter);
        
    }

    private void updateListView(String word) {
        for(int i=0;i<persons.size();i++){
            String listWord = persons.get(i).getPinyin().substring(0,1);//YANGGUANGFU-->Y
            if (word.equals(listWord)) {
                //i是listView中的位置
                lvMain.setSelection(i);//定位到ListVeiw中的某個位置
                return;
            }
        }
    }


    private void updateWord(String word){
        //顯示
        tvWord.setVisibility(View.VISIBLE);
        tvWord.setText(word);
        handler.removeCallbacksAndMessages(null);
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //也是運行在主線程中
                tvWord.setVisibility(View.GONE);
            }
        },500);
    }

    /**
     * 初始化數據
     */
    private void initData() {

        persons = new ArrayList<>();
        persons.add(new Person("張曉飛"));
        persons.add(new Person("楊光福"));
        persons.add(new Person("胡繼羣"));
        persons.add(new Person("劉暢"));

        persons.add(new Person("鍾澤興"));
        persons.add(new Person("尹革新"));
        persons.add(new Person("安傳鑫"));
        persons.add(new Person("張騫壬"));

        persons.add(new Person("溫松"));
        persons.add(new Person("李鳳秋"));
        persons.add(new Person("劉甫"));
        persons.add(new Person("婁全超"));
        persons.add(new Person("張猛"));

        persons.add(new Person("王英傑"));
        persons.add(new Person("李振南"));
        persons.add(new Person("孫仁政"));
        persons.add(new Person("唐春雷"));
        persons.add(new Person("牛鵬偉"));
        persons.add(new Person("姜宇航"));

        persons.add(new Person("劉挺"));
        persons.add(new Person("張洪瑞"));
        persons.add(new Person("張建忠"));
        persons.add(new Person("侯亞帥"));
        persons.add(new Person("劉帥"));

        persons.add(new Person("喬競飛"));
        persons.add(new Person("徐雨健"));
        persons.add(new Person("吳亮"));
        persons.add(new Person("王兆霖"));

        persons.add(new Person("阿三"));
        persons.add(new Person("李博俊"));

        //排序
        Collections.sort(persons, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {

                return o1.getPinyin().compareTo(o2.getPinyin());
            }
        });
    }

    class  IndexAdapter extends BaseAdapter{

        @Override
        public int getCount() {
            return persons.size();
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            if(convertView == null){
                convertView = View.inflate(MainActivity.this,R.layout.item_main,null);
                viewHolder = new ViewHolder();
                viewHolder.tv_word =  convertView.findViewById(R.id.tv_word);
                viewHolder.tv_name =  convertView.findViewById(R.id.tv_name);
                convertView.setTag(viewHolder);
            }else{
                viewHolder = (ViewHolder) convertView.getTag();
            }

            String name = persons.get(position).getName();//阿福
            String word = persons.get(position).getPinyin().substring(0,1);//AFU->A
            viewHolder.tv_word.setText(word);
            viewHolder.tv_name.setText(name);
            if(position ==0){
                viewHolder.tv_word.setVisibility(View.VISIBLE);
            }else{
                //得到前一個位置對應的字母,如果當前的字母和上一個相同,隱藏;否則就顯示
                String preWord = persons.get(position-1).getPinyin().substring(0,1);//A~Z
                if(word.equals(preWord)){
                    viewHolder.tv_word.setVisibility(View.GONE);
                }else{
                    viewHolder.tv_word.setVisibility(View.VISIBLE);
                }
            }
            return convertView;
        }
    }
    static class ViewHolder{
        TextView tv_word;
        TextView tv_name;
    }

}

到這裏功能可以說已經介紹完了。把剩下的xml文件贈送給大家

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.zzsy.quickindex.MainActivity">
    <ListView
        android:id="@+id/lv_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
        <TextView
            android:id="@+id/tv_word"
            android:visibility="gone"
            android:layout_centerInParent="true"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:gravity="center"
            android:background="#44000000"
            android:textColor="#000000"
            android:text="A"
            android:textSize="30sp"
            android:textStyle="bold"
            />

    <com.zzsy.quickindex.IndexView
        android:id="@+id/iv_words"
        android:layout_width="35dp"
        android:layout_height="match_parent"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:background="#10000000" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_word"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#22000000"
        android:text="A"
        android:paddingLeft="5dp"
        android:textColor="#000000"
        android:textSize="25sp" />

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="阿福"
        android:padding="5dp"
        android:textColor="#dd000000"
        android:textSize="22sp" />


</LinearLayout>

上面的xml文件分別是main,item_main

五。jar包地址:

https://download.csdn.net/download/wk_beicai/11635244

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章