使用RecyclerView实现滚动控件

jopen 9年前

滚动控件的实现方式有很多, 使用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