100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > android自定义sidebar Android仿微信通讯录列表侧边栏效果

android自定义sidebar Android仿微信通讯录列表侧边栏效果

时间:2023-03-31 07:01:23

相关推荐

android自定义sidebar Android仿微信通讯录列表侧边栏效果

先看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的文本内容和可见性

使用到的布局

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/sideBar"

android:layout_width="30dp"

android:layout_height="match_parent"

android:layout_alignParentRight="true" />

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" />

此时就完成了索引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();

}

}

}

主布局文件如下

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/rv_userList"

android:layout_width="match_parent"

android:layout_height="match_parent" />

android:id="@+id/hintSideBar"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_gravity="right" />

联系人列表使用的是RecyclerView,还需要定义一个Adapter

public class UserAdapter extends RecyclerView.Adapter {

private List 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 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 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();

}

这样,整个效果就都完成了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。