使用RecyclerView實現(xiàn)滾動控件

歡迎Follow我的GitHub, 關注我的簡書.

效果

本文的合集已經編著成書茂浮,高級Android開發(fā)強化實戰(zhàn)滔岳,歡迎各位讀友的建議和指導。在京東即可購買:https://item.jd.com/12385680.html

Android

滾動控件的實現(xiàn)方式有很多, 使用RecyclerView也比較簡單. 做了一個簡單的年齡滾動控件, 讓我們來看看RecyclerView的使用方式, 主要有以下幾點:
(1) 對齊控件中心位置.
(2) 計算滾動距離.
(3) 高亮中心視圖.
(4) 實時顯示中心數(shù)據(jù).
(5) 停止時自動對齊.
(6) 滾動時, 設置按鈕狀態(tài)開關.

1. 框架

主要關注RecyclerView部分邏輯.

    /**
     * 初始化年齡滑動條
     */
    private void initAgeList() {
        LinearLayoutManager mLayoutManager =
                new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
        mRvAgeList.setLayoutManager(mLayoutManager);
        mAgeAdapter = new PersonAgeAdapter(START_NUM, END_NUM);
        mRvAgeList.setAdapter(mAgeAdapter);
        mRvAgeList.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);

                mBDownStep.setEnabled(false);

                // 效果在暫停時顯示, 否則會導致重繪異常
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    mAgeAdapter.highlightItem(getMiddlePosition());
                    mRvAgeList.scrollToPosition(getScrollPosition());
                    mLastValue = getMiddlePosition();
                    UserInfoManager.setAge(getMiddlePosition() + START_NUM);

                    mBDownStep.setEnabled(true); // 滑動時不可用, 停止時才可以
                }
            }

            @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                // 值是實時增加
                mTvAgeValue.setText(String.valueOf(getMiddlePosition() + START_NUM));
            }
        });

        mAgeAdapter.highlightItem(getMiddlePosition());
    }

設置一個水平布局

LinearLayoutManager mLayoutManager =
                new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
        mRvAgeList.setLayoutManager(mLayoutManager);

添加Adapter, 設置起始和結束位置.

        mAgeAdapter = new PersonAgeAdapter(START_NUM, END_NUM);
        mRvAgeList.setAdapter(mAgeAdapter);

2. Adapter

Adapter定制一些功能, 如標示指向中心位置, 數(shù)據(jù)聯(lián)動顯示, 高亮Item等.

/**
 * 年齡的適配器
 * <p>
 * Created by wangchenlong on 15/11/12.
 */
public class PersonAgeAdapter extends RecyclerView.Adapter<PersonAgeAdapter.AgeItemViewHolder> {

    public static final int ITEM_NUM = 7; // 每行擁有的Item數(shù), 必須是奇數(shù)

    private int mFrom; // 起始
    private int mTo; // 終止
    private int mHighlight = -1; // 高亮

    public PersonAgeAdapter(int from, int to) {
        mFrom = from;
        mTo = to;
    }

    @Override public AgeItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View item = LayoutInflater.from(parent.getContext()).
                inflate(R.layout.view_age_item, parent, false);

        // 設置Item的寬度
        ViewGroup.LayoutParams lp = item.getLayoutParams();
        lp.width = getItemStdWidth();

        return new AgeItemViewHolder(item);
    }

    @Override public void onBindViewHolder(AgeItemViewHolder holder, int position) {
        holder.getTextView().setText(String.valueOf(mFrom + position));

        // 高亮顯示
        if (isSelected(position)) {
            holder.getTextView().setTextSize(30);
            holder.getTextView().setTextColor(ChunyuApp.getAppContext().getResources().getColor(R.color.black));
        } else {
            holder.getTextView().setTextSize(20);
            holder.getTextView().setTextColor(ChunyuApp.getAppContext().getResources().getColor(R.color.gray_line));
        }
    }

    // 高亮中心, 更新前后位置
    public void highlightItem(int position) {
        mHighlight = position;
        int offset = ITEM_NUM / 2;
        for (int i = position - offset; i <= position + offset; ++i)
            notifyItemChanged(i);
    }

    // 判斷是否是高亮
    public boolean isSelected(int position) {
        return mHighlight == position;
    }


    @Override public int getItemCount() {
        return mTo - mFrom + 1;
    }

    // 獲取標準寬度
    public static int getItemStdWidth() {
        DisplayMetrics displayMetrics = ChunyuApp.getAppContext().getResources().getDisplayMetrics();
        return displayMetrics.widthPixels / ITEM_NUM;
    }

    // ViewHolder
    public class AgeItemViewHolder extends RecyclerView.ViewHolder {

        private TextView mTextView;

        public AgeItemViewHolder(View itemView) {
            super(itemView);

            mTextView = (TextView) itemView.findViewById(R.id.item_age_value);
            mTextView.setTag(this);
        }

        public TextView getTextView() {
            return mTextView;
        }
    }
}

每個單元格寬度是屏幕寬度的奇數(shù)分之一, 填充屏幕, 則起始指向中心.

    @Override public AgeItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View item = LayoutInflater.from(parent.getContext()).
                inflate(R.layout.view_age_item, parent, false);

        // 設置Item的寬度
        ViewGroup.LayoutParams lp = item.getLayoutParams();
        lp.width = getItemStdWidth();

        return new AgeItemViewHolder(item);
    }

單數(shù)Item數(shù)量, 則中心的指示器必指向中心Item的中心.

根據(jù)選中狀態(tài), 更新Item樣式.

    @Override public void onBindViewHolder(AgeItemViewHolder holder, int position) {
        holder.getTextView().setText(String.valueOf(mFrom + position));

        // 高亮顯示
        if (isSelected(position)) {
            holder.getTextView().setTextSize(30);
            holder.getTextView().setTextColor(ChunyuApp.getAppContext().getResources().getColor(R.color.black));
        } else {
            holder.getTextView().setTextSize(20);
            holder.getTextView().setTextColor(ChunyuApp.getAppContext().getResources().getColor(R.color.gray_line));
        }
    }

設置高亮, 把中心位置高亮, 兩邊恢復, 通知Adapter重繪ViewHolder.

    // 高亮中心, 更新前后位置
    public void highlightItem(int position) {
        mHighlight = position;
        int offset = ITEM_NUM / 2;
        for (int i = position - offset; i <= position + offset; ++i)
            notifyItemChanged(i);
    }

    // 判斷是否是高亮
    public boolean isSelected(int position) {
        return mHighlight == position;
    }

notifyItemChanged()會重繪所選擇的頁面.

獲取寬度, 把一行顯示Item數(shù)量設置為單數(shù), 則中心指向一個Item.

    // 獲取標準寬度
    public static int getItemStdWidth() {
        DisplayMetrics displayMetrics = ChunyuApp.getAppContext().getResources().getDisplayMetrics();
        return displayMetrics.widthPixels / ITEM_NUM;
    }

注意RecyclerView不能移動半個單元, 每行數(shù)量是單數(shù)時, 則必會指向中心.

3. 滾動邏輯

在滾動時, 實時更新頁面顯示; 在停止時, 更新高亮和存儲數(shù)據(jù); 滾動結束時, 激活按鈕.

        mRvAgeList.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                mBDownStep.setEnabled(false);

                // 效果在暫停時顯示, 否則會導致重繪異常
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    mAgeAdapter.highlightItem(getMiddlePosition());
                    mRvAgeList.scrollToPosition(getScrollPosition());
                    mLastValue = getMiddlePosition();
                    UserInfoManager.setAge(getMiddlePosition() + START_NUM);

                    mBDownStep.setEnabled(true); // 滑動時不可用, 停止時才可以
                }
            }

            @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                // 值是實時增加
                mTvAgeValue.setText(String.valueOf(getMiddlePosition() + START_NUM));
            }
        });

        mAgeAdapter.highlightItem(getMiddlePosition());
    }

    /**
     * 獲取中間位置
     *
     * @return 當前值
     */
    private int getMiddlePosition() {
        return getScrollPosition() + (PersonAgeAdapter.ITEM_NUM / 2);
    }

    /**
     * 獲取滑動值, 滑動偏移 / 每個格子寬度
     *
     * @return 當前值
     */
    private int getScrollPosition() {
        return (int) ((double) mRvAgeList.computeHorizontalScrollOffset()
                / (double) PersonAgeAdapter.getItemStdWidth());
    }

判斷滾動的距離單位, 偏移總距離/單個Item寬度.

    private int getScrollPosition() {
        return (int) ((double) mRvAgeList.computeHorizontalScrollOffset()
                / (double) PersonAgeAdapter.getItemStdWidth());
    }

computeHorizontalScrollOffset()獲取RecyclerView的偏移總位移.

中間位置, 還要加上半行Item數(shù)量

    private int getMiddlePosition() {
        return getScrollPosition() + (PersonAgeAdapter.ITEM_NUM / 2);
    }

判斷滾動是否停止.

if (newState == RecyclerView.SCROLL_STATE_IDLE)

在滾動停止時, 自動對齊.

mRvAgeList.scrollToPosition(getScrollPosition());

調用需要高亮顯示的Item.

mAgeAdapter.highlightItem(getMiddlePosition());

注意在滾動停止時, 更新高亮比較好, 否則重繪速度較慢, 會影響滾動效果.

滑動效果

推薦, 介紹RecyclerView的文章, 特別詳細.

OK, 我們可以根據(jù)這些定制各式各樣的滾動條了!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末慈俯,一起剝皮案震驚了整個濱河市旋圆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌用狱,老刑警劉巖运怖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異齿拂,居然都是意外死亡驳规,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門署海,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吗购,“玉大人,你說我怎么就攤上這事砸狞∧砻悖” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵刀森,是天一觀的道長踱启。 經常有香客問我,道長研底,這世上最難降的妖魔是什么埠偿? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮榜晦,結果婚禮上冠蒋,老公的妹妹穿的比我還像新娘。我一直安慰自己乾胶,他們只是感情好抖剿,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著识窿,像睡著了一般斩郎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喻频,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天缩宜,我揣著相機與錄音,去河邊找鬼甥温。 笑死脓恕,一個胖子當著我的面吹牛膜宋,可吹牛的內容都是我干的。 我是一名探鬼主播炼幔,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼秋茫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了乃秀?” 一聲冷哼從身側響起肛著,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎跺讯,沒想到半個月后枢贿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡刀脏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年局荚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愈污。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡耀态,死狀恐怖,靈堂內的尸體忽然破棺而出暂雹,到底是詐尸還是另有隱情首装,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布杭跪,位于F島的核電站仙逻,受9級特大地震影響,放射性物質發(fā)生泄漏涧尿。R本人自食惡果不足惜系奉,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望姑廉。 院中可真熱鬧缺亮,春花似錦、人聲如沸庄蹋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽限书。三九已至,卻和暖如春章咧,著一層夾襖步出監(jiān)牢的瞬間倦西,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工赁严, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扰柠,地道東北人粉铐。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像卤档,于是被迫代替她去往敵國和親蝙泼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,756評論 25 707
  • 簡介: 提供一個讓有限的窗口變成一個大數(shù)據(jù)集的靈活視圖劝枣。 術語表: Adapter:RecyclerView的子類...
    酷泡泡閱讀 5,148評論 0 16
  • 簡介 RecyclerView在24.2.0版本中新增了SnapHelper這個輔助類汤踏,用于輔助RecyclerV...
    辰之貓閱讀 154,249評論 65 617
  • 聽著她放的 薛之謙的 別, 別坦白舔腾, 別勇敢 剛剛好溪胶,在她不在的時候我剛聽過 她出去玩了,回來的...
    MMa_f259閱讀 166評論 0 0
  • 趙明喜歡開著燈睡覺稳诚。 他的房子很大哗脖,其實原先看起來沒這么大,只是粉刷的時候涂了藍色扳还,設計師說:這是一種后退色才避。果然...
    連卿閱讀 342評論 0 5