转载请注明原创出处,谢谢!
- GitHub: @Ricco
- 类似效果图 ↑
- 类似效果图 ↑
- 类似效果图 ↑
- 重要的画说三遍
public class MyActivity extends BaseActivity implements SideBar.OnSelectListener {
@Bind(R.id.tv_suspension_bar)
TextView tvSuspensionBar; // 悬浮
@Bind(R.id.recycler)
RecyclerView recycler;
@Bind(R.id.side_bar)
SideBar sideBar;
private int mSuspensionHeight = 0; // 高度
private int mCurrentPosition = 0; // 当前悬浮
private LinearLayoutManager layoutManager;
private MyAdapter adapter = new MyAdapter();
@Override
public int getLayoutRes() {
return R.layout.activity_my;
}
@Override
public void afterLoadLayout(Bundle savedInstanceState) {
layoutManager = new LinearLayoutManager(context);
recycler.setLayoutManager(layoutManager);
recycler.setAdapter(adapter);
sideBar.setOnSelectListener(this);
adapter.setOnItemChildClickListener(this);
recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
mSuspensionHeight = tvSuspensionBar.getHeight();
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (mCurrentPosition > 0) {
if (adapter.getData().get(mCurrentPosition + 1).isShowFirm()) {
View view = layoutManager.findViewByPosition(mCurrentPosition + 1);
if (view != null) {
if (view.getTop() <= mSuspensionHeight) {
tvSuspensionBar.setY(-(mSuspensionHeight - view.getTop()));
} else {
tvSuspensionBar.setY(0);
}
}
}
}
if (mCurrentPosition != layoutManager.findFirstVisibleItemPosition()) {
mCurrentPosition = layoutManager.findFirstVisibleItemPosition();
tvSuspensionBar.setY(0);
tvSuspensionBar.setText(adapter.getData().get(mCurrentPosition).getFirstEng());
}
}
});
getData();
}
@Override
public void onSelect(String s) {
sideBar.setOnSelectListener(new SideBar.OnSelectListener() {
@Override
public void onSelect(String s) {
// 根据选中的首字母移动到指定位置
layoutManager.scrollToPositionWithOffset(adapter.getScrollPosition(s), 0);
}
});
}
private void getData() {
...
data是网络请求回来的数据
...
// 处理数据
// 根据字母分类
List<String> characterList = new ArrayList<>(); // 首字母
Map<String, List<Entity>> map = new LinkedHashMap<>();
for (Entity bean : data) {
if (map.containsKey(bean.getFirstEng())) {
map.get(bean.getFirstEng()).add(bean);
} else {
List<Entity> list = new ArrayList<>();
list.add(bean);
map.put(bean.getFirstEng(), list);
characterList.add(bean.getFirstEng());
}
}
// 合并
List<Entity> newList = new ArrayList<>();
for (List<Entity> list : map.values()) {
list.get(0).setShowFirm(true); // 每个分类的第一个显示字母
list.get(list.size() - 1).setShowLine(true); // 每个分类的最后一个隐藏分割线
newList.addAll(list);
}
// 设置activity的悬浮
if (newList.size() > 0) {
tvSuspensionBar.setText(newList.get(0).getFirstEng());
}
adapter.setCharacterList(characterList);
adapter.setNewData(newList);
}
}
public class MyAdapter extends BaseQuickAdapter<Entity, BaseViewHolder> {
public MyAdapter() {
super(R.layout.adapter_my);
}
@Override
protected void convert(BaseViewHolder helper, Entity item) {
helper.setText(R.id.tv_character, "" + item.getFirstEng())
.setText(R.id.tv_name, "" + item.getName())
.setGone(R.id.tv_character, item.isShowFirm()) // 每个分类的第一个有字母
.setGone(R.id.line, !item.isShowLine()); // 每个分类的最后一个没有线
}
private List<String> mCharacterList;
public void setCharacterList(List<String> characterList) {
mCharacterList = characterList;
}
public int getScrollPosition(String character) {
if (mData.size() != 0 && mCharacterList != null && mCharacterList.size() != 0) {
if (mCharacterList.contains(character)) {
for (int i = 0; i < mData.size(); i++) {
if (mData.get(i).getFirstEng().equals(character)) {
return i;
}
}
}
}
return -1; // -1不会滑动
}
}
public class SideBar extends View {
//SideBar上显示的字母
private static final String[] CHARACTERS = {"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"};
//SideBar的高度
private int width;
//SideBar的宽度
private int height;
//SideBar中每个字母的显示区域的高度
private float cellHeight;
//画字母的画笔
private Paint characterPaint;
//SideBar上字母绘制的矩形区域
private Rect textRect;
//手指触摸在SideBar上的横纵坐标
private float touchY;
private float touchX;
private OnSelectListener listener;
public SideBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public SideBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SideBar(Context context) {
super(context);
init(context);
}
//初始化操作
private void init(Context context) {
textRect = new Rect();
characterPaint = new Paint();
characterPaint.setColor(0xFF4980F2);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) { //在这里测量SideBar的高度和宽度
width = getMeasuredWidth();
height = getMeasuredHeight();
//SideBar的高度除以需要显示的字母的个数,就是每个字母显示区域的高度
cellHeight = height * 1.0f / CHARACTERS.length;
//根据SideBar的宽度和每个字母显示的高度,确定绘制字母的文字大小,这样处理的好处是,对于不同分辨率的屏幕,文字大小是可变的
int textSize = (int) ((width > cellHeight ? cellHeight : width) * (3.0f / 4));
characterPaint.setTextSize(textSize);
}
}
//画出SideBar上的字母
private void drawCharacters(Canvas canvas) {
for (int i = 0; i < CHARACTERS.length; i++) {
String s = CHARACTERS[i];
//获取画字母的矩形区域
characterPaint.getTextBounds(s, 0, s.length(), textRect);
//根据上一步获得的矩形区域,画出字母
canvas.drawText(s,
(width - textRect.width()) / 2f,
cellHeight * i + (cellHeight + textRect.height()) / 2f,
characterPaint);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawCharacters(canvas);
}
//根据手指触摸的坐标,获取当前选择的字母
private String getHint() {
int index = (int) (touchY / cellHeight);
if (index >= 0 && index < CHARACTERS.length) {
return CHARACTERS[index];
}
return null;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//获取手指触摸的坐标
touchX = event.getX();
touchY = event.getY();
if (listener != null && touchX > 0) {
listener.onSelect(getHint());
}
/*if (listener != null && touchX < 0) {
listener.onMoveUp(getHint());
}*/
return true;
case MotionEvent.ACTION_MOVE:
//获取手指触摸的坐标
touchX = event.getX();
touchY = event.getY();
if (listener != null && touchX > 0) {
listener.onSelect(getHint());
}
/*if (listener != null && touchX < 0) {
listener.onMoveUp(getHint());
}*/
return true;
case MotionEvent.ACTION_UP:
touchY = event.getY();
/*if (listener != null) {
listener.onMoveUp(getHint());
}*/
return true;
}
return super.onTouchEvent(event);
}
//监听器,监听手指在SideBar上按下和抬起的动作
public interface OnSelectListener {
void onSelect(String s);
// void onMoveUp(String s);
}
//设置监听器
public void setOnSelectListener(OnSelectListener listener) {
this.listener = listener;
}
}
public class Entity {
private String firstEng; // 首字母
private String name; // 内容
private boolean showFirm; // 是否显示上面内容
private boolean showLine; // 是否显示分割线
}
activity_my.xml ↓
<?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">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<SideBar
android:id="@+id/side_bar"
android:layout_width="30dp"
android:layout_height="400dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true" />
<TextView
android:id="@+id/tv_suspension_bar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#F7F9FD"
android:gravity="center_vertical"
android:paddingLeft="16dp"
android:text="A"
android:textColor="#333333"
android:textSize="16sp" />
</RelativeLayout>
adapter_my.xml ↓
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="vertical">
<TextView
android:id="@+id/tv_character"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#F7F9FD"
android:gravity="center_vertical"
android:paddingLeft="16dp"
android:textColor="#333333"
android:textSize="16dp" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="center"
android:layout_marginLeft="20dp"
android:textColor="#333333"
android:textSize="15dp" />
<View
android:id="@+id/line"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="15dp"
android:background="#F2F5F9" />
</LinearLayout>
MyAdapter 基于BRVAH 编写 BRVAH
BRVAH官方使用指南:https://www.jianshu.com/p/b343fcff51b0/