Android效果,SideBar 和RecyclerView聯(lián)動(dòng)的懸浮效果

轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處,謝謝!
類(lèi)似效果圖
  • 類(lèi)似效果圖 ↑
  • 類(lèi)似效果圖 ↑
  • 類(lèi)似效果圖 ↑
  • 重要的畫(huà)說(shuō)三遍
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ù)選中的首字母移動(dòng)到指定位置
                layoutManager.scrollToPositionWithOffset(adapter.getScrollPosition(s), 0);
            }
        });
    }


    private void getData() {
        ...
        data是網(wǎng)絡(luò)請(qǐng)求回來(lái)的數(shù)據(jù)
        ...
    
        // 處理數(shù)據(jù)
        // 根據(jù)字母分類(lèi)
        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); // 每個(gè)分類(lèi)的第一個(gè)顯示字母
            list.get(list.size() - 1).setShowLine(true); // 每個(gè)分類(lèi)的最后一個(gè)隱藏分割線
            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()) // 每個(gè)分類(lèi)的第一個(gè)有字母
                .setGone(R.id.line, !item.isShowLine()); // 每個(gè)分類(lèi)的最后一個(gè)沒(méi)有線
    }

    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不會(huì)滑動(dòng)
    }
}
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中每個(gè)字母的顯示區(qū)域的高度
    private float cellHeight;
    //畫(huà)字母的畫(huà)筆
    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) { //在這里測(cè)量SideBar的高度和寬度
            width = getMeasuredWidth();
            height = getMeasuredHeight();
            //SideBar的高度除以需要顯示的字母的個(gè)數(shù),就是每個(gè)字母顯示區(qū)域的高度
            cellHeight = height * 1.0f / CHARACTERS.length;
            //根據(jù)SideBar的寬度和每個(gè)字母顯示的高度,確定繪制字母的文字大小,這樣處理的好處是,對(duì)于不同分辨率的屏幕,文字大小是可變的
            int textSize = (int) ((width > cellHeight ? cellHeight : width) * (3.0f / 4));
            characterPaint.setTextSize(textSize);
        }
    }

    //畫(huà)出SideBar上的字母
    private void drawCharacters(Canvas canvas) {
        for (int i = 0; i < CHARACTERS.length; i++) {
            String s = CHARACTERS[i];
            //獲取畫(huà)字母的矩形區(qū)域
            characterPaint.getTextBounds(s, 0, s.length(), textRect);
            //根據(jù)上一步獲得的矩形區(qū)域,畫(huà)出字母
            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)聽(tīng)器,監(jiān)聽(tīng)手指在SideBar上按下和抬起的動(dòng)作
    public interface OnSelectListener {
        void onSelect(String s);

//        void onMoveUp(String s);
    }

    //設(shè)置監(jiān)聽(tīng)器
    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 編寫(xiě) BRVAH

BRVAH官方使用指南:http://www.itdecent.cn/p/b343fcff51b0/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 主要聲明三點(diǎn) 第一:關(guān)于本文的BaseRecyclerViewAdapterHelper用法,自然是轉(zhuǎn)載的官...
    _猜火車(chē)_閱讀 157,195評(píng)論 81 167
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,898評(píng)論 2 59
  • 一直很忙,沒(méi)時(shí)間寫(xiě)文章,也就是工作中遇到的東西記錄一下,若其他同行能從中獲益更好。 android開(kāi)發(fā),如果還不知...
    追夢(mèng)者king閱讀 472評(píng)論 1 3
  • RecyclerView其實(shí)都已經(jīng)用了一段時(shí)間了,但是說(shuō)實(shí)話,我是真的不太熟悉,而且看到自己項(xiàng)目用的這個(gè)BRVAH...
    jeffrey12138閱讀 922評(píng)論 0 5
  • 久違的晴天,家長(zhǎng)會(huì)。 家長(zhǎng)大會(huì)開(kāi)好到教室時(shí),離放學(xué)已經(jīng)沒(méi)多少時(shí)間了。班主任說(shuō)已經(jīng)安排了三個(gè)家長(zhǎng)分享經(jīng)驗(yàn)。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,788評(píng)論 16 22

友情鏈接更多精彩內(nèi)容