轉(zhuǎn)載請注明原創(chuàng)出處,謝謝!
- 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; // 當(dāng)前懸浮
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) {
// 根據(jù)選中的首字母移動到指定位置
layoutManager.scrollToPositionWithOffset(adapter.getScrollPosition(s), 0);
}
});
}
private void getData() {
...
data是網(wǎng)絡(luò)請求回來的數(shù)據(jù)
...
// 處理數(shù)據(jù)
// 根據(jù)字母分類
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);
}
// 設(shè)置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中每個字母的顯示區(qū)域的高度
private float cellHeight;
//畫字母的畫筆
private Paint characterPaint;
//SideBar上字母繪制的矩形區(qū)域
private Rect textRect;
//手指觸摸在SideBar上的橫縱坐標(biāo)
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的高度除以需要顯示的字母的個數(shù),就是每個字母顯示區(qū)域的高度
cellHeight = height * 1.0f / CHARACTERS.length;
//根據(jù)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];
//獲取畫字母的矩形區(qū)域
characterPaint.getTextBounds(s, 0, s.length(), textRect);
//根據(jù)上一步獲得的矩形區(qū)域唤反,畫出字母
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);
}
//根據(jù)手指觸摸的坐標(biāo)鸭津,獲取當(dāng)前選擇的字母
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:
//獲取手指觸摸的坐標(biāo)
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:
//獲取手指觸摸的坐標(biāo)
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);
}
//監(jiān)聽器逆趋,監(jiān)聽手指在SideBar上按下和抬起的動作
public interface OnSelectListener {
void onSelect(String s);
// void onMoveUp(String s);
}
//設(shè)置監(jiān)聽器
public void setOnSelectListener(OnSelectListener listener) {
this.listener = listener;
}
}
public class Entity {
private String firstEng; // 首字母
private String name; // 內(nèi)容
private boolean showFirm; // 是否顯示上面內(nèi)容
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官方使用指南:http://www.reibang.com/p/b343fcff51b0/