使用RecyclerView實現滾動控件

jopen 8年前發布 | 18K 次閱讀 Android開發 移動開發

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

效果

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定制一些功能, 如標示指向中心位置, 數據聯動顯示, 高亮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數, 必須是奇數

    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;
        }
    }
}

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

    @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);
    }

單數Item數量, 則中心的指示器必指向中心Item的中心.

根據選中狀態, 更新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數量設置為單數, 則中心指向一個Item.

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

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

3. 滾動邏輯

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

        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數量

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

判斷滾動是否停止.

if (newState == RecyclerView.SCROLL_STATE_IDLE)

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

mRvAgeList.scrollToPosition(getScrollPosition());

調用需要高亮顯示的Item.

mAgeAdapter.highlightItem(getMiddlePosition());

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

動畫效果

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

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

來自: http://blog.csdn.net//caroline_wendy/article/details/50107599

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!