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

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

編輯:關於Android編程

很多的Android入門程序猿來說對於Android自定義View,可能都是比較恐懼的,但是這又是高手進階的必經之路,所有准備在自定義View上面花一些功夫,多寫一些文章。

思路分析:

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

2、ListView實現聯系人列表

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

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

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

效果圖:

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

<!--?xml version="1.0" encoding="utf-8"?-->
<linearlayout android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> 
 <edittext android:background="@drawable/search_border" android:drawableleft="@android:drawable/ic_menu_search" android:layout_height="wrap_content" android:layout_width="match_parent" android:padding="8dp"> 
 <relativelayout android:layout_height="match_parent" android:layout_width="match_parent">
  <listview android:divider="@null" android:id="@+id/lv" android:layout_height="match_parent" android:layout_width="match_parent">
  <textview android:background="#888888" android:gravity="center" android:id="@+id/tv" android:layout_centerinparent="true" android:layout_height="50dp" android:layout_width="50dp" android:textcolor="#000000" android:textsize="18dp" android:visibility="gone">
  <com.handsome.tulin.view.navview android:id="@+id/nv" android:layout_alignparentright="true" android:layout_height="match_parent" android:layout_margin="16dp" android:layout_width="20dp">
 </com.handsome.tulin.view.navview></textview></listview></relativelayout>
</edittext></linearlayout>

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

思路分析:

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<user> 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<user>() {
   @Override
   public int compare(User lhs, User rhs) {
    return lhs.getFirstCharacter().compareTo(rhs.getFirstCharacter());
   }
  });
  //填充ListView
  adapter = new UserAdapter(this, list);
  lv.setAdapter(adapter);
 }
}</user></user>

ListView子布局:

<!--?xml version="1.0" encoding="utf-8"?-->
<linearlayout android:background="#ffffff" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
  <textview android:background="#DBDBDA" android:id="@+id/tv_firstCharacter" android:layout_height="wrap_content" android:layout_width="match_parent" android:padding="8dp" android:text="A" android:textcolor="#000000" android:textsize="14dp"> 
 <textview android:background="#ffffff" android:id="@+id/tv_name" android:layout_height="wrap_content" android:layout_width="match_parent" android:padding="8dp" android:text="張棟梁" android:textcolor="#2196F3" android:textsize="14dp"> 
</textview></textview></linearlayout>

Adapter:

public class UserAdapter extends BaseAdapter {
 private List<user> list;
 private User user;
 private LayoutInflater mInflater;
 private Context context;
 public UserAdapter(Context context, List<user> 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;
 }
}</user></user>

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

思路分析:

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