Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 通訊錄導航欄

Android 通訊錄導航欄

編輯:關於Android編程

這樣的一個控件實現起來不難,需要對自定義view有一定的基礎,也要了解怎麼實現一個集合的排序。大體思路很簡單。

首先完成view的基本繪制以及相關的內部邏輯。 其次,就是要對聯系人數據進行排序,即姓名首字母按26個英文字母進行排序,說到排序不得不說的兩個接口Comparable和Comparator。利用這兩個接口中的一個,我們可以很方便的實現集合排序的功能。 接下來就可以使用ListView來展示聯系人數據了,關於ListView的使用相信大家都已經十分的熟練了,也就不多說了。 最後要實現點擊導航欄字母使ListView滾動到相應的位置,編寫一個相關回調接口即可。

原理還是比較簡單的,有了思路,做事情就會事半功倍。接下來,首先考慮自定義導航欄的實現,其實就是需要繪制一個#號和26個英文字母豎直排列的View。接下來看這樣的View該如何實現。

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

import cn.manchester.app.contactsnav.R;

/**
 * 右側導航欄字母列表
 *
 * @author Administrator
 */
public class SideBar extends View {

    /*
     * 默認的導航欄中字母的大小
     */
    private static final int DEFAULT_ALPHA_SIZE = 18;

    /*
     * 字母導航欄選擇時,在中部顯示所選擇字母的TextView
     */
    private TextView mDialog;

    /*
     * 字母導航欄選擇事件回調接口
     */
    private OnLetterSelectedListener mOnLetterSelectedListener;

    /*
     * 字母列表內容
     */
    private static final String[] ALPHA_LIST = {"#", "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 mChoiceIndex;

    /*
     * 畫筆
     */
    private Paint mPaint;

    /*
     * 默認字母顏色
     */
    private static final int DEFAULT_CHARACTER = Color.rgb(33, 66, 99);
    /*
     * 選中字母顏色
     */
    private static final int CHIOCED_CHARACTER = Color.parseColor("#FF0000");

    public SideBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setAntiAlias(true); // 抗鋸齒,字體圓滑
        mPaint.setColor(DEFAULT_CHARACTER); // 設置字體顏色
        mPaint.setTypeface(Typeface.DEFAULT_BOLD); // 設置字體為粗體
        mPaint.setTextSize(sp2px(DEFAULT_ALPHA_SIZE));
    }

    /**
     * 字母導航欄選中事件回調接口
     *
     * @author Administrator
     */
    public interface OnLetterSelectedListener {
        /**
         * 字母被選中事件回調方法
         *
         * @param letter 被選中的字母
         */
        void onLetterSelected(String letter);
    }

    /**
     * 設置字母導航欄字母被選中時的回調接口
     *
     * @param listener 接口
     */
    public void setOnLetterSelectedListener(OnLetterSelectedListener listener) {
        mOnLetterSelectedListener = listener;
    }

    public int sp2px(float spValue) {
        final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 獲取整個SideBar的寬和高
        int width = getWidth();
        int height = getHeight();

        // 平均每行所分配到的高度值
        int singleHeight = height / ALPHA_LIST.length;

        for (int i = 0; i < ALPHA_LIST.length; i++) {
            // 計算每個字母的x坐標
            float xPos = (width - mPaint.measureText(ALPHA_LIST[i])) / 2;
            // 計算每個字母的y坐標(頂部預留一個singleHeight的高度)
            float yPos = singleHeight * (i + 1);

            // 如果當前字母是點擊的字母,則改變畫筆顏色
            if (i == mChoiceIndex) {
                mPaint.setColor(CHIOCED_CHARACTER);
            }

            // 繪制文本
            canvas.drawText(ALPHA_LIST[i], xPos, yPos, mPaint);

            // 繪制完成後,將畫筆顏色修改為默認的顏色
            mPaint.setColor(DEFAULT_CHARACTER);
        }

    }

    /**
     * 觸摸事件
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        // 記錄當前的觸摸動作
        int action = event.getAction();
        // 獲取當前的y坐標
        float yPos = event.getY();

        int oldChoice = mChoiceIndex;
        // 計算出當前的位置在哪個字母上面
        int c = (int) (yPos / getHeight() * ALPHA_LIST.length);

        switch (action) {
            case MotionEvent.ACTION_UP:
                setBackgroundColor(Color.TRANSPARENT);
                // 手指彈起,沒有選中任何字母
                mChoiceIndex = -1;
                invalidate();
                if (mDialog != null) {
                    // 將中部顯示字母的TextView隱藏
                    mDialog.setVisibility(View.INVISIBLE);
                }
                break;

            default:
                // 設置字母導航欄被觸摸時的背景
                setBackgroundResource(R.drawable.sidebar_background);
                // 如果當前的選擇不是之前的選擇,觸發字母選擇事件的回調接口方法,改變中部TextView中的字母
                if (oldChoice != c) {
                    if (c >= 0 && c < ALPHA_LIST.length) {
                        // 將選中的字母傳遞給注冊事件的監聽者
                        if (mOnLetterSelectedListener != null) {
                            mOnLetterSelectedListener.onLetterSelected(ALPHA_LIST[c]);
                        }

                        if (mDialog != null) {
                            mDialog.setVisibility(View.VISIBLE);
                            mDialog.setText(ALPHA_LIST[c]);
                        }
                    }
                    mChoiceIndex = c;
                    invalidate();
                }
                break;
        }
        return true;
    }

    /**
     * 綁定中部顯示字母的TextView控件
     *
     * @param tvDialog
     */
    public void setDialog(TextView tvDialog) {
        if (tvDialog == null) {
            throw new NullPointerException("tvDialog can not be null...");
        }
        this.mDialog = tvDialog;
    }

}

首先需要獲取屏幕的寬高,然後根據高度再決定每一行的高度並且計算每個字符的x,y坐標,就可以繪制出一個豎直排列的字母組合了。有了外觀接著就要實現view與用戶的交互,首先攔截用戶的觸摸事件,判斷事件類型,當用戶手指抬起的時候表示view要恢復原樣,其它情況下,表示用戶正在按下某個字母,再根據觸摸的位置從而可以計算出用戶具體按下的字母,然後實現自己的業務邏輯即可。

至於對集合排序的實現,則需要借助於Comparable和Comparator接口。這兩個接口都可以實現排序的功能,至於選擇哪個則看具體需求而定了。下面分別展示兩個接口具體的使用方法。

List personList = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 5; i++) {
    Person p = new Person();
    p.setName(String.valueOf(i));
    p.setAge(random.nextInt(5));
    personList.add(p);
}
// Collections.sort(personList);
Collections.sort(personList, new AgeComparator());
System.out.println(personList);

打印的結果信息

[Person{name='1', age=0}, Person{name='3', age=0}, Person{name='0', age=1}, Person{name='2', age=1}, Person{name='4', age=2}]

首先看讓Object實現Comparable接口的方式

public class Person implements Comparable {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public int compareTo(Person o) {
        if (age < o.getAge()) {
            return -1;
        } else if (age == o.getAge()) {
            return 0;
        }
        return 1;
    }
}

這裡要關注的點就是compareTo方法的返回值,如果當前對象的age小於要比較的對象並且返回-1,相等時返回0,大於時返回1,則表示排序的規則為升序。接下來看實現Comparator接口的方式。

public class AgeComparator implements Comparator {

    @Override
    public int compare(Person o1, Person o2) {
        if (o1.getAge() < o2.getAge()) {
            return -1;
        } else if (o1.getAge() == o2.getAge()) {
            return 0;
        }
        return 1;
    }
}

這樣就可以完成相關數據排序的功能了。僅僅是這樣還是不夠的,對於ListView的Adapter還需要一些特殊的處理,具體代碼如下。

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

public class SortAdapter extends BaseAdapter {

    private static final String TAG = SortAdapter.class.getSimpleName();

    private List mContactList;

    public SortAdapter() {
        mContactList = new ArrayList<>();
    }

    public void addData(List list) {
        mContactList.addAll(list);
        Collections.sort(mContactList, new PinYinComparator());
        notifyDataSetChanged();
    }

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

    @Override
    public Object getItem(int position) {
        return mContactList.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        Contact conatct = mContactList.get(position);

        if (convertView == null) {
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.listview_contact, parent, false);
            holder = new ViewHolder(convertView);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        // 獲取當前聯系人的首字母
        char headerChar = getHeaderCharByPosition(position);
        // 獲取當前聯系人首字母第一次出現的位置
        int index = getFirstPositionByHeaderChar(headerChar);


        // 如果當前聯系人首字母第一次出現的位置等於當前的位置,則表示該聯系人是該首字母下出現的第一個聯系人
        if (position == index) {
            holder.mCharCategoryText.setVisibility(View.VISIBLE);
            holder.mCharCategoryText.setText(conatct.getHeaderChar());
            holder.mDivider.setVisibility(View.GONE);
        } else {
            // 默認設置字母欄不顯示
            holder.mCharCategoryText.setVisibility(View.GONE);
            holder.mDivider.setVisibility(View.VISIBLE);
        }
        holder.mNameText.setText(conatct.getName());

        return convertView;
    }

    /**
     * 通過聯系人的位置獲取該聯系人的名稱的首字母
     *
     * @param position
     * @return 首字母
     */
    private char getHeaderCharByPosition(int position) {
        return mContactList.get(position).getHeaderChar().toUpperCase().charAt(0);
    }

    /**
     * 通過首字母獲取顯示該首字母的第一個聯系人的位置:比如C,陳奕迅
     *
     * @param c
     * @return 位置,如果返回-1表示未查找到該字母
     */
    public int getFirstPositionByHeaderChar(char c) {
        for (int i = 0; i < getCount(); i++) {
            String headerChar = mContactList.get(i).getHeaderChar();
            char firstChar = headerChar.toUpperCase(Locale.CHINA).charAt(0);
            if (firstChar == c) {
                return i;
            }
        }
        return -1;
    }

    private class ViewHolder {
        TextView mCharCategoryText;
        TextView mNameText;
        View mDivider;

        ViewHolder(View view) {
            mCharCategoryText = (TextView) view.findViewById(R.id.tv_header_char);
            mNameText = (TextView) view.findViewById(R.id.tv_name);
            mDivider = view.findViewById(R.id.divider);
        }
    }

}

ListView的item布局文件如下




    

    

    

基本的代碼已經完成,最後再MainActivity中創建一些測試的數據。

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import cn.manchester.app.contactsnav.view.SideBar;

public class MainActivity extends AppCompatActivity implements SideBar.OnLetterSelectedListener {

    private static final String TAG = MainActivity.class.getSimpleName();

    ListView mListContact;
    SideBar mSideBar;
    TextView mTextChar;

    String[] chinese = new String[]{"1234", "2345", "432143", "532", "6", "431", "98", "78", "89", "趙發", "錢去", "孫我", "李想", "一額", "發啊", "去的",
            "額啊", "范圍", "是的", "干活", "干活", "干活", "干活", "干活", "干活", "干活",
            "如圖", "後台", "熱突然", "未熱", "無法", "那就"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mListContact = (ListView) findViewById(R.id.lv_contacts_list);
        mSideBar = (SideBar) findViewById(R.id.side_bar);
        mTextChar = (TextView) findViewById(R.id.tv_dialog);

        mListContact.setAdapter(new SortAdapter());
        mSideBar.setOnLetterSelectedListener(this);

        Random random = new Random();
        List contactList = new ArrayList<>();
        for (int i = 0; i < chinese.length; i++) {
            Contact contact = new Contact();
            contact.setName(chinese[i]);
            contactList.add(contact);
        }
        ((SortAdapter) mListContact.getAdapter()).addData(contactList);

        mSideBar.setDialog(mTextChar);
    }

    @Override
    public void onLetterSelected(String letter) {
        mTextChar.setText(letter);
        SortAdapter adapter = (SortAdapter) mListContact.getAdapter();
        int firstPosition = -1;
        char c = letter.charAt(0);
        do {
            firstPosition = adapter.getFirstPositionByHeaderChar(c);
            if (firstPosition == -1) {
                c++;
            }
        } while (firstPosition == -1);
        mListContact.setSelection(firstPosition);
        Log.e(TAG, String.valueOf(c) + " pos=" + firstPosition);
    }
}

MainActivity的布局文件



    <framelayout android:layout_height="match_parent" android:layout_width="match_parent">

        

        

        
        


    </framelayout>

最後看下實現的效果圖

這裡寫圖片描述

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved