Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android自定義View——實現字母導航欄

Android自定義View——實現字母導航欄

編輯:關於Android編程

思路分析:

1、自定義View實現字母導航欄

2、ListView實現聯系人列表

3、字母導航欄滑動事件處理

4、字母導航欄與中間字母的聯動

5、字母導航欄與ListView的聯動


效果圖:

\

 

首先,我們先甩出主布局文件,方便後面代碼的說明

 




    

    

        

        

        
    

 

 

步驟一:分析自定義字母導航欄

 

思路分析:

1、我們在使用的時候把寬設置為20dp,高設置為填充父控件,所以這裡獲取的寬度為20dp

2、通過循環,畫出豎直的字母,每畫一次得重新設置一下顏色,因為我們需要一個選中的字母顏色和默認不一樣

 

 

 

public class NavView extends View {

    private Paint textPaint = new Paint();
    private String[] s = 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 choose = -1;
    //中間的文本
    private TextView tv;

    public NavView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NavView(Context context) {
        super(context);
    }

    public NavView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void initPaint() {
        textPaint.setTextSize(20);
        textPaint.setAntiAlias(true);
        textPaint.setColor(Color.BLACK);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //畫字母
        drawText(canvas);
    }


    /**
     * 畫字母
     *
     * @param canvas
     */
    private void drawText(Canvas canvas) {
        //獲取View的寬高
        int width = getWidth();
        int height = getHeight();
        //獲取每個字母的高度
        int singleHeight = height / s.length;
        //畫字母
        for (int i = 0; i < s.length; i++) {
            //畫筆默認顏色
            initPaint();
            //高亮字母顏色
            if (choose == i) {
                textPaint.setColor(Color.RED);
            }
            //計算每個字母的坐標
            float x = (width - textPaint.measureText(s[i])) / 2;
            float y = (i + 1) * singleHeight;
            canvas.drawText(s[i], x, y, textPaint);
            //重置顏色
            textPaint.reset();
        }
    }
}

 

步驟二:ListView實現聯系人列表

 

思路分析:

1、在主Activity中,定義一個數據數組,使用工具類獲取數組的第一個字母,使用Collections根據第一個字母進行排序,由於工具類有點長,就不貼出來了。

2、創建一個ListView子布局,創建一個Adapter進行填充。

 

主布局:

 

public class MainActivity extends AppCompatActivity {

    private TextView tv;
    private ListView lv;
    private NavView nv;

    private List list;
    private UserAdapter adapter;
    private String[] name = new String[]{
            "潘粵明", "戴軍", "薛之謙", "藍雨", "任泉", "張傑", "秦俊傑",
            "陳坤", "田亮", "夏雨", "保劍鋒", "陸毅", "喬振宇", "吉傑", "郭敬明", "巫迪文", "歡子", "井柏然",
            "左小祖咒", "段奕宏", "毛寧", "樊凡", "湯潮", "山野", "陳龍", "侯勇", "俞思遠", "馮紹峰", "崔健",
            "杜淳", "張翰", "彭坦", "柏栩栩", "蒲巴甲", "凌潇肅", "毛方圓", "武藝", "耿樂", "錢泳辰"};


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

        initView();
        initData();
    }

    private void initView() {
        tv = (TextView) findViewById(R.id.tv);
        lv = (ListView) findViewById(R.id.lv);
        nv = (NavView) findViewById(R.id.nv);
        nv.setTextView(tv);
    }

    private void initData() {
        //初始化數據
        list = new ArrayList<>();
        for (int i = 0; i < name.length; i++) {
            list.add(new User(name[i], CharacterUtils.getFirstSpell(name[i]).toUpperCase()));
        }
        //將拼音排序
        Collections.sort(list, new Comparator() {
            @Override
            public int compare(User lhs, User rhs) {
                return lhs.getFirstCharacter().compareTo(rhs.getFirstCharacter());
            }
        });
        //填充ListView
        adapter = new UserAdapter(this, list);
        lv.setAdapter(adapter);
    }

}

 

ListView子布局:



    

    

 

Adapter:

public class UserAdapter extends BaseAdapter {

    private List list;
    private User user;
    private LayoutInflater mInflater;
    private Context context;

    public UserAdapter(Context context, List list) {
        this.list = list;
        mInflater = LayoutInflater.from(context);
        this.context = context;
    }

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

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.adapter_user, null);
        }
        ViewHolder holder = getViewHolder(convertView);
        user = list.get(position);
        if (position == 0) {
            //第一個數據要顯示字母和姓名
            holder.tv_firstCharacter.setVisibility(View.VISIBLE);
            holder.tv_firstCharacter.setText(user.getFirstCharacter());
            holder.tv_name.setText(user.getUsername());
        } else {
            //其他數據判斷是否為同個字母,這裡使用Ascii碼比較大小
            if (CharacterUtils.getCnAscii(list.get(position - 1).getFirstCharacter().charAt(0)) <
                    CharacterUtils.getCnAscii(user.getFirstCharacter().charAt(0))) {
                //後面字母的值大於前面字母的值,需要顯示字母
                holder.tv_firstCharacter.setVisibility(View.VISIBLE);
                holder.tv_firstCharacter.setText(user.getFirstCharacter());
                holder.tv_name.setText(user.getUsername());
            } else {
                //後面字母的值等於前面字母的值,不顯示字母
                holder.tv_firstCharacter.setVisibility(View.GONE);
                holder.tv_name.setText(user.getUsername());
            }
        }
        return convertView;
    }

    /**
     * 獲得控件管理對象
     *
     * @param view
     * @return
     */
    private ViewHolder getViewHolder(View view) {
        ViewHolder holder = (ViewHolder) view.getTag();
        if (holder == null) {
            holder = new ViewHolder(view);
            view.setTag(holder);
        }
        return holder;
    }

    /**
     * 控件管理類
     */
    private class ViewHolder {

        private TextView tv_firstCharacter, tv_name;

        ViewHolder(View view) {
            tv_firstCharacter = (TextView) view.findViewById(R.id.tv_firstCharacter);
            tv_name = (TextView) view.findViewById(R.id.tv_name);
        }
    }

    /**
     * 通過字符查找位置
     *
     * @param s
     * @return
     */
    public int getSelectPosition(String s) {
        for (int i = 0; i < getCount(); i++) {
            String firChar = list.get(i).getFirstCharacter();
            if (firChar.equals(s)) {
                return i;
            }
        }
        return -1;
    }
}

 

步驟三:字母導航欄滑動事件處理、字母導航欄與中間字母的聯動

 

思路分析:

1、在自定義View中重寫dispatchTouchEvent處理滑動事件,最後返回true。

2、在主Activity傳進來一個TextView,在我們滑動的時候設置Text,松開的時候消失Text。設置Text的時候需要計算Text的位置,並且滑過多的話會出現數組越界的問題,所以我們在裡面處理數組越界問題。

3最後,提供一個接口,記錄我們滑到的字母,為了後面可以和ListView聯動。

 

 

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        //計算選中字母
        int index = (int) (event.getY() / getHeight() * s.length);
        //防止腳標越界
        if (index >= s.length) {
            index = s.length - 1;
        } else if (index < 0) {
            index = 0;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                setBackgroundColor(Color.GRAY);
                //選中字母高亮
                choose = index;
                //出現中間文字
                tv.setVisibility(VISIBLE);
                tv.setText(s[choose]);
                //調用ListView連動接口
                if (listener != null) {
                    listener.touchCharacterListener(s[choose]);
                }
                //重繪
                invalidate();
                break;
            default:
                setBackgroundColor(Color.TRANSPARENT);
                //取消選中字母高亮
                choose = -1;
                //隱藏中間文字
                tv.setVisibility(GONE);
                //重繪
                invalidate();
                break;
        }
        return true;
    }

    public onTouchCharacterListener listener;

    public interface onTouchCharacterListener {
        void touchCharacterListener(String s);
    }

    public void setListener(onTouchCharacterListener listener) {
        this.listener = listener;
    }

    /**
     * 傳進來一個TextView
     *
     * @param tv
     */
    public void setTextView(TextView tv) {
        this.tv = tv;
    }

步驟四:字母導航欄和ListView的聯動

 

思路分析:

1、我們已經通過接口傳遞過去了一個選擇的字母,和在adapter寫好了根據字母查詢position的方法,這個時候只要主Activity對自定義View設置監聽,判斷即可。

        //ListView連動接口
        nv.setListener(new NavView.onTouchCharacterListener() {
            @Override
            public void touchCharacterListener(String s) {
                int position = adapter.getSelectPosition(s);
                if (position != -1) {
                    lv.setSelection(position);
                }
            }
        });

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