您好,登錄后才能下訂單哦!
先看Android仿微信通訊錄列表側邊欄效果圖
這是比較常見的效果了吧
列表根據首字符的拼音字母來排序,且可以通過側邊欄的字母索引來進行定位。
實現這樣一個效果并不難,只要自定義一個索引View,然后引入一個可以對漢字進行拼音解析的jar包——pinyin4j-2.5.0即可
首先,先來定義側邊欄控件View,只要直接畫出來即可。
字母選中項會變為紅色,且滑動時背景會變色,此時SideBar并不包含居中的提示文本
public class SideBar extends View { private Paint paint = new Paint(); private int choose = -1; private boolean showBackground; public static String[] letters = {"#", "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 OnChooseLetterChangedListener onChooseLetterChangedListener; public SideBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public SideBar(Context context, AttributeSet attrs) { super(context, attrs); } public SideBar(Context context) { super(context); } protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (showBackground) { canvas.drawColor(Color.parseColor("#D9D9D9")); } int height = getHeight(); int width = getWidth(); //平均每個字母占的高度 int singleHeight = height / letters.length; for (int i = 0; i < letters.length; i++) { paint.setColor(Color.BLACK); paint.setAntiAlias(true); paint.setTextSize(25); if (i == choose) { paint.setColor(Color.parseColor("#FF2828")); paint.setFakeBoldText(true); } float x = width / 2 - paint.measureText(letters[i]) / 2; float y = singleHeight * i + singleHeight; canvas.drawText(letters[i], x, y, paint); paint.reset(); } } @Override public boolean dispatchTouchEvent(MotionEvent event) { int action = event.getAction(); float y = event.getY(); int oldChoose = choose; int c = (int) (y / getHeight() * letters.length); switch (action) { case MotionEvent.ACTION_DOWN: showBackground = true; if (oldChoose != c && onChooseLetterChangedListener != null) { if (c > -1 && c < letters.length) { onChooseLetterChangedListener.onChooseLetter(letters[c]); choose = c; invalidate(); } } break; case MotionEvent.ACTION_MOVE: if (oldChoose != c && onChooseLetterChangedListener != null) { if (c > -1 && c < letters.length) { onChooseLetterChangedListener.onChooseLetter(letters[c]); choose = c; invalidate(); } } break; case MotionEvent.ACTION_UP: showBackground = false; choose = -1; if (onChooseLetterChangedListener != null) { onChooseLetterChangedListener.onNoChooseLetter(); } invalidate(); break; } return true; } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); } public void setOnTouchingLetterChangedListener(OnChooseLetterChangedListener onChooseLetterChangedListener) { this.onChooseLetterChangedListener = onChooseLetterChangedListener; } public interface OnChooseLetterChangedListener { void onChooseLetter(String s); void onNoChooseLetter(); } }
SideBar只是畫出了側邊欄索引條而已,不包含居中的提示文本,這個在另一個布局添加即可
public class HintSideBar extends RelativeLayout implements SideBar.OnChooseLetterChangedListener { private TextView tv_hint; private SideBar.OnChooseLetterChangedListener onChooseLetterChangedListener; public HintSideBar(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.view_hint_side_bar, this); initView(); } private void initView() { SideBar sideBar = (SideBar) findViewById(R.id.sideBar); tv_hint = (TextView) findViewById(R.id.tv_hint); sideBar.setOnTouchingLetterChangedListener(this); } @Override public void onChooseLetter(String s) { tv_hint.setText(s); tv_hint.setVisibility(VISIBLE); if (onChooseLetterChangedListener != null) { onChooseLetterChangedListener.onChooseLetter(s); } } @Override public void onNoChooseLetter() { tv_hint.setVisibility(INVISIBLE); if (onChooseLetterChangedListener != null) { onChooseLetterChangedListener.onNoChooseLetter(); } } public void setOnChooseLetterChangedListener(SideBar.OnChooseLetterChangedListener onChooseLetterChangedListener) { this.onChooseLetterChangedListener = onChooseLetterChangedListener; } }
HintSideBar通過回調接口來更新居中TextView的文本內容和可見性
使用到的布局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.czy.demo.SideBar android:id="@+id/sideBar" android:layout_width="30dp" android:layout_height="match_parent" android:layout_alignParentRight="true" /> <TextView android:id="@+id/tv_hint" android:layout_width="70dp" android:layout_height="70dp" android:layout_centerInParent="true" android:background="#4b0e0e0e" android:gravity="center" android:textColor="#ffffff" android:textSize="30sp" android:visibility="invisible" /> </RelativeLayout>
此時就完成了索引View的繪制,不過定位功能還需要再通過回調接口來完成。
引入jar包后,先來設定一個工具類,包含一個可以解析字符串的方法,返回值為首字符對應的拼音首字母或者為包含一個空格的char類型數據。
public class Utils { /** * 如果字符串的首字符為漢字,則返回該漢字的拼音大寫首字母 * 如果字符串的首字符為字母,也轉化為大寫字母返回 * 其他情況均返回' ' * * @param str 字符串 * @return 首字母 */ public static char getHeadChar(String str) { if (str != null && str.trim().length() != 0) { char[] strChar = str.toCharArray(); char headChar = strChar[0]; //如果是大寫字母則直接返回 if (Character.isUpperCase(headChar)) { return headChar; } else if (Character.isLowerCase(headChar)) { return Character.toUpperCase(headChar); } // 漢語拼音格式輸出類 HanyuPinyinOutputFormat hanYuPinOutputFormat = new HanyuPinyinOutputFormat(); hanYuPinOutputFormat.setCaseType(UPPERCASE); hanYuPinOutputFormat.setToneType(WITHOUT_TONE); if (String.valueOf(headChar).matches("[\\u4E00-\\u9FA5]+")) { try { String[] stringArray = PinyinHelper.toHanyuPinyinStringArray(headChar, hanYuPinOutputFormat); if (stringArray != null && stringArray[0] != null) { return stringArray[0].charAt(0); } } catch (BadHanyuPinyinOutputFormatCombination e) { return ' '; } } } return ' '; } }
然后再定義一個實體類,包含用戶名,電話,用戶名首字符的拼音首字母等三個屬性
需要實現Comparable 接口,用于排序
public class User implements Comparable { private String userName; private String phone; private char headLetter; public User(String userName, String phone) { this.userName = userName; this.phone = phone; headLetter = Utils.getHeadChar(userName); } public String getUserName() { return userName; } public String getPhone() { return phone; } public char getHeadLetter() { return headLetter; } @Override public boolean equals(Object object) { if (this == object) { return true; } if (object == null || getClass() != object.getClass()) { return false; } User that = (User) object; return getUserName().equals(that.getUserName()) && getPhone().equals(that.getPhone()); } @Override public int compareTo(Object object) { if (object instanceof User) { User that = (User) object; if (getHeadLetter() == ' ') { if (that.getHeadLetter() == ' ') { return 0; } return -1; } if (that.getHeadLetter() == ' ') { return 1; } else if (that.getHeadLetter() > getHeadLetter()) { return -1; } else if (that.getHeadLetter() == getHeadLetter()) { return 0; } return 1; } else { throw new ClassCastException(); } } }
主布局文件如下
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_userList" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.czy.demo.HintSideBar android:id="@+id/hintSideBar" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="right" /> </FrameLayout>
聯系人列表使用的是RecyclerView,還需要定義一個Adapter
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserHolder> { private List<User> userList; private LayoutInflater inflater; public UserAdapter(Context context) { inflater = LayoutInflater.from(context); userList = new ArrayList<>(); } @Override public UserHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = inflater.inflate(R.layout.item_user, parent, false); return new UserHolder(view); } @Override public void onBindViewHolder(UserHolder holder, int position) { holder.tv_userName.setText(userList.get(position).getUserName()); holder.tv_phone.setText(userList.get(position).getPhone()); } public void setData(List<User> userList) { this.userList.clear(); this.userList = userList; } public int getFirstPositionByChar(char sign) { if (sign == '#') { return 0; } for (int i = 0; i < userList.size(); i++) { if (userList.get(i).getHeadLetter() == sign) { return i; } } return -1; } @Override public int getItemCount() { return userList.size(); } class UserHolder extends RecyclerView.ViewHolder { public TextView tv_userName; public TextView tv_phone; public UserHolder(View itemView) { super(itemView); tv_userName = (TextView) itemView.findViewById(R.id.tv_userName); tv_phone = (TextView) itemView.findViewById(R.id.tv_phone); } } }
以下方法用于獲取聯系人列表中第一個首字符為sign的item的位置
public int getFirstPositionByChar(char sign)
主Activity代碼如下
public class MainActivity extends AppCompatActivity implements SideBar.OnChooseLetterChangedListener { private List<User> userList; private UserAdapter adapter; private RecyclerView rv_userList; private LinearLayoutManager manager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); supportRequestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); HintSideBar hintSideBar = (HintSideBar) findViewById(R.id.hintSideBar); rv_userList = (RecyclerView) findViewById(R.id.rv_userList); hintSideBar.setOnChooseLetterChangedListener(this); manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); rv_userList.setLayoutManager(manager); userList = new ArrayList<>(); adapter = new UserAdapter(this); initData(); adapter.setData(userList); rv_userList.setAdapter(adapter); } @Override public void onChooseLetter(String s) { int i = adapter.getFirstPositionByChar(s.charAt(0)); if (i == -1) { return; } manager.scrollToPositionWithOffset(i, 0); } @Override public void onNoChooseLetter() { } }
initData()用于向Adapter填充數據
public void initData() { User user1 = new User("陳", "12345678"); User user2 = new User("趙", "12345678"); ... userList.add(user1); userList.add(user2); ... Collections.sort(userList); adapter.notifyDataSetChanged(); }
這樣,整個效果就都完成了。
這里提供代碼下載:Android 仿微信通訊錄列表側邊欄
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。