安卓实现列表左右滑动和点击滑动的效果

项目场景:

需要实现一个横向滑动的效果,外加一个点击箭头滑动的效果,滑动以后默认还是选中第一个。


使用技术

首先需要一个工具类,直接导入就行,可以直接使用:

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

 

最后效果图展示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值