项目场景:
需要实现一个横向滑动的效果,外加一个点击箭头滑动的效果,滑动以后默认还是选中第一个。
使用技术
首先需要一个工具类,直接导入就行,可以直接使用:
public class PagingScrollHelper { RecyclerView mRecyclerView = null; private MyOnScrollListener mOnScrollListener = new MyOnScrollListener(); private MyOnFlingListener mOnFlingListener = new MyOnFlingListener(); private int offsetY = 0; private int offsetX = 0; int startY = 0; int startX = 0; enum ORIENTATION { HORIZONTAL, VERTICAL, NULL } private ORIENTATION mOrientation = ORIENTATION.HORIZONTAL; public void setUpRecycleView(RecyclerView recycleView) { if (recycleView == null) { throw new IllegalArgumentException("recycleView must be not null"); } mRecyclerView = recycleView; //处理滑动 recycleView.setOnFlingListener(mOnFlingListener); //设置滚动监听,记录滚动的状态,和总的偏移量 recycleView.addOnScrollListener(mOnScrollListener); //记录滚动开始的位置 recycleView.setOnTouchListener(mOnTouchListener); //获取滚动的方向 updateLayoutManger(); } public void removeScrollListener(){ mRecyclerView.removeOnScrollListener(mOnScrollListener); } public void updateLayoutManger() { RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager(); if (layoutManager != null) { if (layoutManager.canScrollVertically()) { mOrientation = ORIENTATION.VERTICAL; } else if (layoutManager.canScrollHorizontally()) { mOrientation = ORIENTATION.HORIZONTAL; } else { mOrientation = ORIENTATION.NULL; } if (mAnimator != null) { mAnimator.cancel(); } startX = 0; startY = 0; offsetX = 0; offsetY = 0; } } /** * 获取总共的页数 */ public int getPageCount() { if (mRecyclerView != null) { if (mOrientation == ORIENTATION.NULL) { return 0; } if (mOrientation == ORIENTATION.VERTICAL && mRecyclerView.computeVerticalScrollExtent() != 0) { return mRecyclerView.computeVerticalScrollRange() / mRecyclerView.computeVerticalScrollExtent(); } else if (mRecyclerView.computeHorizontalScrollExtent() != 0) { Log.i("zzz", "rang=" + mRecyclerView.computeHorizontalScrollRange() + " extent=" + mRecyclerView.computeHorizontalScrollExtent()); return mRecyclerView.computeHorizontalScrollRange() / mRecyclerView.computeHorizontalScrollExtent(); } } return 0; } ValueAnimator mAnimator = null; public void scrollToPosition(int position) { if (mAnimator == null) { mOnFlingListener.onFling(0, 0); } if (mAnimator != null) { int startPoint = mOrientation == ORIENTATION.VERTICAL ? offsetY : offsetX, endPoint = 0; if (mOrientation == ORIENTATION.VERTICAL) { endPoint = mRecyclerView.getHeight() * position; } else { endPoint = mRecyclerView.getWidth() * position; } if (startPoint != endPoint) { mAnimator.setIntValues(startPoint, endPoint); mAnimator.start(); } } } public class MyOnFlingListener extends RecyclerView.OnFlingListener { @Override public boolean onFling(int velocityX, int velocityY) { if (mOrientation == ORIENTATION.NULL) { return false; } //获取开始滚动时所在页面的index int p = getStartPageIndex(); //记录滚动开始和结束的位置 int endPoint = 0; int startPoint = 0; //如果是垂直方向 if (mOrientation == ORIENTATION.VERTICAL) { startPoint = offsetY; if (velocityY < 0) { p--; } else if (velocityY > 0) { p++; } //更具不同的速度判断需要滚动的方向 //注意,此处有一个技巧,就是当速度为0的时候就滚动会开始的页面,即实现页面复位 endPoint = p * mRecyclerView.getHeight(); } else { startPoint = offsetX; if (velocityX < 0) { p--; } else if (velocityX > 0) { p++; } endPoint = p * mRecyclerView.getWidth(); } if (endPoint < 0) { endPoint = 0; } //使用动画处理滚动 if (mAnimator == null) { mAnimator = new ValueAnimator().ofInt(startPoint, endPoint); mAnimator.setDuration(300); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int nowPoint = (int) animation.getAnimatedValue(); if (mOrientation == ORIENTATION.VERTICAL) { int dy = nowPoint - offsetY; //这里通过RecyclerView的scrollBy方法实现滚动。 mRecyclerView.scrollBy(0, dy); } else { int dx = nowPoint - offsetX; mRecyclerView.scrollBy(dx, 0); } } }); mAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { //回调监听 if (null != mOnPageChangeListener) { mOnPageChangeListener.onPageChange(getPageIndex()); } //修复双击item bug mRecyclerView.stopScroll(); startY = offsetY; startX = offsetX; } }); } else { mAnimator.cancel(); mAnimator.setIntValues(startPoint, endPoint); } mAnimator.start(); return true; } } public class MyOnScrollListener extends RecyclerView.OnScrollListener { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { //newState==0表示滚动停止,此时需要处理回滚 if (newState == 0 && mOrientation != ORIENTATION.NULL) { boolean move; int vX = 0, vY = 0; if (mOrientation == ORIENTATION.VERTICAL) { int absY = Math.abs(offsetY - startY); //如果滑动的距离超过屏幕的一半表示需要滑动到下一页 move = absY > recyclerView.getHeight() / 2; vY = 0; if (move) { vY = offsetY - startY < 0 ? -1000 : 1000; } } else { int absX = Math.abs(offsetX - startX); move = absX > recyclerView.getWidth() / 2; if (move) { vX = offsetX - startX < 0 ? -1000 : 1000; } } mOnFlingListener.onFling(vX, vY); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { //滚动结束记录滚动的偏移量 offsetY += dy; offsetX += dx; } } private MyOnTouchListener mOnTouchListener = new MyOnTouchListener(); private boolean firstTouch = true; public class MyOnTouchListener implements View.OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { //手指按下的时候记录开始滚动的坐标 if (firstTouch) { //第一次touch可能是ACTION_MOVE或ACTION_DOWN,所以使用这种方式判断 firstTouch = false; startY = offsetY; startX = offsetX; } if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { firstTouch = true; } return false; } } private int getPageIndex() { int p = 0; if (mRecyclerView.getHeight() == 0 || mRecyclerView.getWidth() == 0) { return p; } if (mOrientation == ORIENTATION.VERTICAL) { p = offsetY / mRecyclerView.getHeight(); } else { p = offsetX / mRecyclerView.getWidth(); } return p; } private int getStartPageIndex() { int p = 0; if (mRecyclerView.getHeight() == 0 || mRecyclerView.getWidth() == 0) { //没有宽高无法处理 return p; } if (mOrientation == ORIENTATION.VERTICAL) { p = startY / mRecyclerView.getHeight(); } else { p = startX / mRecyclerView.getWidth(); } return p; } onPageChangeListener mOnPageChangeListener; public void setOnPageChangeListener(onPageChangeListener listener) { mOnPageChangeListener = listener; } public interface onPageChangeListener { void onPageChange(int index); } }
这个工具类主要是实现,滑动的时候,当前的下标。
页面开发和调用:
在initview初始化的是需要先调用一下这些adapter和工具类:
//使用通用RecyclerView组件 //初始化横向管理器 PagingScrollHelper scrollHelper = new PagingScrollHelper(); HorizontalPageLayoutManager horizontalPageLayoutManager = new HorizontalPageLayoutManager(1, 1);//这里两个参数是行列,这里实现的是一行三列 scrollHelper.setUpRecycleView(recyclerView);//将横向布局管理器和recycler view绑定到一起 scrollHelper.setOnPageChangeListener(new PagingScrollHelper.onPageChangeListener() { @SuppressLint("NotifyDataSetChanged") @Override public void onPageChange(int index) { Log.i("onPageChange", "安全环保-index " + index); if (maps != null) { Log.i("onPageChange", "安全环保-maps " + maps.size()); if (maps.size() > 1) { //实现箭头点击滑动 if (index == 0) { EventBus.getDefault().post(new SafetyEvent(false, true)); } else if (index > 0 && index < maps.size() - 1) { EventBus.getDefault().post(new SafetyEvent(true, true)); } else if (index == maps.size() - 1) { EventBus.getDefault().post(new SafetyEvent(true, false)); } else if (maps.size() == 1) { EventBus.getDefault().post(new SafetyEvent(false, false)); } //滑动以后默认选中第一个 for (int i = 0; i < maps.size(); i++) { List<SafetyBean.ListBean> defaultList = maps.get(i).getList(); for (int j = 0; j < defaultList.size(); j++) { defaultList.get(j).setCheck(false); } } maps.get(index).getList().get(0).setCheck(true); safetyAdapter.notifyDataSetChanged(); for (int i = 0; i < maps.size(); i++) { List<SafetyBean.ListBean> defaultList = maps.get(i).getList(); for (int j = 0; j < defaultList.size(); j++) { if (defaultList.get(j).isCheck()) { int ordinal = defaultList.get(j).getKey(); if (baseordinal == ordinal) { } else { newSafetyFragmentPresenter.DataShow(new SafetyListRequest(ordinal), ordinal); } } } } } } } }); //设置滑动监听 recyclerView.setLayoutManager(horizontalPageLayoutManager);//设置为横向 recyclerView.setHorizontalScrollBarEnabled(true); if (safetyAdapter == null){ safetyAdapter = new SafetyAdapter(getContext()); recyclerView.setAdapter(safetyAdapter); } if (safetyListAdapter == null){ safetyListAdapter = new SafetyListAdapter(mcontext); RecyclerViewHelper.initRecyclerViewV(getActivity(), recy_list, new SlideInBottomAnimationAdapter(safetyListAdapter)); } newSafetyFragmentPresenter.getDataShow(new OperationBaseRequest());//接口调用
然后就是一级标题拿到数据以后赋值:
private List<SafetyBean> maps; private int indexPos = 0;@SuppressLint("NotifyDataSetChanged") public void ItemTab(List<SafetyBean.ListBean> data) { newSafetyFragmentPresenter.DataShow(new SafetyListRequest(data.get(0).getKey()), data.get(0).getKey()); Log.i("", "ItemTab: " + data.size()); //默认选中第一个item data.get(0).setCheck(true); //一级列表数据 maps = new ArrayList<>(); maps = PageUtil.SafetyformatPageData(data);//手动分页 if (maps.size() > 1) { Log.i("mapsmapsmapsmaps", " " + maps.size()); iv_img.setVisibility(View.VISIBLE); iv_img_left.setVisibility(View.GONE); } iv_img.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (maps.size() > 0) { indexPos = indexPos + 1; scrollHelper.scrollToPosition(indexPos);//默认滑动到第一页 } } }); iv_img_left.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (maps.size() > 0) { indexPos = indexPos - 1; scrollHelper.scrollToPosition(indexPos);//默认滑动到第一页 if (indexPos == 0) { iv_img.setVisibility(View.VISIBLE); iv_img_left.setVisibility(View.GONE); } } } }); safetyAdapter.updateItems(maps); safetyAdapter.notifyDataSetChanged(); }
一级标题的适配器 因为是需要滑动的,所以他是列表套着列表的:
public class SafetyAdapter extends BaseQuickAdapter<SafetyBean> { private Context context; public SafetyAdapter(Context context) { super(context); this.context = context; } public SafetyAdapter(Context context, List<SafetyBean> data) { super(context, data); } @Override protected int attachLayoutRes() { return R.layout.item_new_title; } @Override protected void convert(BaseViewHolder holder,SafetyBean item) { RecyclerView recyclerView = holder.getView(R.id.recyclerView); LinearLayoutPagerManager layoutManager = new LinearLayoutPagerManager(context, LinearLayoutManager.HORIZONTAL, false, 4); layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); recyclerView.setLayoutManager(layoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); NewSafetyTitleAdapter adapter = new NewSafetyTitleAdapter(context,item.getList(), getData()); recyclerView.setAdapter(adapter); adapter.setOnItemClickListener(new OnItemsClickListener() { @SuppressLint("NotifyDataSetChanged") @Override public void onItemClickListener(View view) { notifyDataSetChanged(); } }); } private OnItemsClickListener onItemsClickListener; private void setOnItemClickListener(OnItemsClickListener onItemsClickListeners){ onItemsClickListener = onItemsClickListeners; } }
一级标题的内部列表 (赋值的):
public class NewSafetyTitleAdapter extends BaseQuickAdapter<SafetyBean.ListBean> { private List<SafetyBean> data; public NewSafetyTitleAdapter(Context context, List<SafetyBean.ListBean> defaultList, List<SafetyBean> data) { super(context, defaultList); this.data = data; } @Override protected int attachLayoutRes() { return R.layout.new_safety_title; } @Override protected void convert(BaseViewHolder holder, SafetyBean.ListBean item) { TextView tvTitle = holder.getView(R.id.tv_title); ImageView iv_indicator = holder.getView(R.id.iv_indicator); tvTitle.setText(item.getTitle()); if (item.isCheck()) { iv_indicator.setVisibility(View.VISIBLE); tvTitle.getPaint().setFakeBoldText(true); tvTitle.setTextColor(ContextCompat.getColor(mContext, R.color.c_171A1D)); } else { iv_indicator.setVisibility(View.INVISIBLE); tvTitle.getPaint().setFakeBoldText(false); tvTitle.setTextColor(ContextCompat.getColor(mContext, R.color.c_737577)); } //通过EventBus将数据和点击事件发送给首页 holder.getConvertView().setOnClickListener(new View.OnClickListener() { @SuppressLint("NotifyDataSetChanged") @Override public void onClick(View view) { onItemsClickListener.onItemClickListener(view); if (item.getTitle() != null) { EventBus.getDefault().post(new NewSafetyListEvent(true, item.getKey())); } else { EventBus.getDefault().post(new NewSafetyListEvent(false, item.getKey())); } for (int i = 0; i < data.size(); i++) { for (SafetyBean.ListBean bean : data.get(i).getList()) { bean.setCheck(false); } } item.setCheck(true); notifyDataSetChanged(); } }); } private OnItemsClickListener onItemsClickListener; public void setOnItemClickListener(OnItemsClickListener onItemsClickListeners) { onItemsClickListener = onItemsClickListeners; } }
二级列表的数据对应的赋值:
private int baseordinal = -1;//用来获取一级列表下对应的二级数据
public void getFunctionType(List<SafetyListBean.ResultBean> result, int id) { if (result == null || result.size() == 0) { iv_empty.setVisibility(View.VISIBLE); } else { iv_empty.setVisibility(View.GONE); } baseordinal = id; safetyListAdapter = new SafetyListAdapter(mcontext, result, id); // recy_list.addItemDecoration(new RoundedRectItemDecoration(5, 5)); recy_list.setAdapter(safetyListAdapter); }
二级列表的适配器(赋值,做了点击事件):
public class SafetyListAdapter extends BaseQuickAdapter<SafetyListBean.ResultBean> { private Context mcontext; private int id; private List<SafetyListBean.ResultBean> data; public SafetyListAdapter(Context context) { super(context); this.mcontext = context; } public SafetyListAdapter(Context context, List<SafetyListBean.ResultBean> data, int id) { super(context, data); this.data = data; this.mcontext = context; this.id = id; } @Override protected int attachLayoutRes() { return R.layout.item_safety_list; } @Override protected void convert(BaseViewHolder holder, SafetyListBean.ResultBean item) { int bindingAdapterPosition = holder.getBindingAdapterPosition(); TextView tv_title = holder.getView(R.id.tv_title); TextView tv_cont = holder.getView(R.id.tv_cont); View views = holder.getView(R.id.view); View viewss = holder.getView(R.id.viewss); ConstraintLayout rl_show = holder.getView(R.id.rl_show); tv_title.setText(item.getCategoryName()); RecyclerView rv_list = holder.getView(R.id.rv_list); ArrayList<SafetyListBean.ResultBean.ListBean> listBeans = new ArrayList<>(); for (int i = 0; i < item.getList().size(); i++) { SafetyListBean.ResultBean.ListBean listBean = new SafetyListBean.ResultBean.ListBean(); String title = item.getList().get(i).getTitle(); String createTime = item.getList().get(i).getCreateTime(); String url = item.getList().get(i).getUrl(); String categoryName = item.getList().get(i).getCategoryName(); String fileUrl = item.getList().get(i).getFileUrl(); listBean.setFileUrl(fileUrl); listBean.setUrl(url); listBean.setCategoryName(categoryName); listBean.setTitle(title); listBean.setCreateTime(createTime); listBeans.add(listBean); } SafetyListsAdapter safetyListsAdapter = new SafetyListsAdapter(mcontext, listBeans); RecyclerViewHelper.initRecyclerViewV(mcontext, rv_list, new SlideInBottomAnimationAdapter(safetyListsAdapter)); safetyListsAdapter.cleanItems(); safetyListsAdapter.updateItems(listBeans); if (item.getTotal() > 3) { views.setVisibility(View.VISIBLE); rl_show.setVisibility(View.VISIBLE); viewss.setVisibility(View.GONE); tv_cont.setText("查看更多"); } else { rl_show.setVisibility(View.GONE); views.setVisibility(View.GONE); viewss.setVisibility(View.VISIBLE); } tv_cont.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { EventBus.getDefault().post(new NewSafetyListsEvent(item.getList().get(bindingAdapterPosition).getCategoryName(), item.getList().get(bindingAdapterPosition).getCategoryId())); } }); } }
箭头的显示和隐藏使用的是eventbus:
//显示/隐藏箭头 @Subscribe(threadMode = ThreadMode.MAIN) public void onMainThreadEvent(SafetyEvent event) { if (event.isStart()) { iv_img_left.setVisibility(View.VISIBLE); } else { iv_img_left.setVisibility(View.GONE); } if (event.isEnd()) { iv_img.setVisibility(View.VISIBLE); } else { iv_img.setVisibility(View.GONE); } }
点击或者滑动到下一页的刷新方法:
@Subscribe(threadMode = ThreadMode.MAIN) public void onMainThreadEvent(SafeRefreshEvent event) { if (isCreate){ scrollHelper.removeScrollListener(); initViews(); recyclerView.scrollToPosition(0);//周历滑动到第几页 scrollHelper.scrollToPosition(0);//默认滑动到第一页 } } //点击按钮刷新列表 @Subscribe(threadMode = ThreadMode.MAIN) public void onMainThreadEvent(NewSafetyListEvent event) { if (event.isHasData()) { recyclerView.setVisibility(View.VISIBLE); recy_list.setVisibility(View.VISIBLE); newSafetyFragmentPresenter.DataShow(new SafetyListRequest(event.getPos()), event.getPos()); } else { recy_list.setVisibility(View.GONE); recyclerView.setVisibility(View.GONE); } }
最后效果图展示: