横向滚动的RecycleView-------SimpleRecycleView,适用于android tv

      最近开始做android tv的应用,里面用到许多横向滚动的列表,查过资料后,我使用了SimpleRecycleView,代码如下:

package com.hisense.movienow.HorizontalView;

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Scroller;

/**
 * Created by wangchunmei.ex on 2018/11/13.
 */

public class SimpleRecycleView extends RecyclerView {
    private static final String TAG = SimpleRecycleView.class.getSimpleName();
    // 一个滚动对象
    private Scroller mScroller;
    private int mLastX = 0;

    public SimpleRecycleView(Context context) {
        super(context);
        init(context);
    }

    public SimpleRecycleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public SimpleRecycleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    // 一个初始化方法,传入了一个上下文对象,用来初始化滚动对象
    private void init(Context context){
        mScroller = new Scroller(context);
        initView();
        setItemAnimator(null);
    }

    /**
     * 初始化View
     * 为避免recycleview焦点混乱常用的一些设置
     */
    private void initView()
    {
        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
        setHasFixedSize(true);
        setWillNotDraw(true);
        setOverScrollMode(View.OVER_SCROLL_NEVER);
        setChildrenDrawingOrderEnabled(true);

        setClipChildren(false);
        setClipToPadding(false);

        setClickable(false);
        setFocusable(true);
        setFocusableInTouchMode(true);
        /**
         防止RecyclerView刷新时焦点不错乱bug的步骤如下:
         (1)adapter执行setHasStableIds(true)方法
         (2)重写getItemId()方法,让每个view都有各自的id
         (3)RecyclerView的动画必须去掉
         */
        setItemAnimator(null);
    }

    // 重写了计算滚动方法
    @Override
    public void computeScroll() {
        if(mScroller!=null && mScroller.computeScrollOffset()){
            scrollBy(mLastX - mScroller.getCurrX(), 0);
            mLastX = mScroller.getCurrX();
            postInvalidate();
        }
    }



    /**
     * 调用此方法滚动到目标位置,其中(fx, fy)表示最终要滚到的目标位置的坐标值
     * duration表示期间滚动的耗时。
     *
     * @param fx 目标位置的X向坐标值
     * @param fy 目标位置的Y向坐标值
     * @param duration 滚动到目标位置所消耗的时间毫秒值
     */
    @SuppressWarnings("unused")
    public void smoothScrollTo(int fx, int fy,int duration) {
        int dx = 0;
        int dy = 0;
        // 计算变化的位移量
        if(fx != 0) {
            dx = fx - mScroller.getFinalX();
        }
        if(fy!=0) {
            dy = fy - mScroller.getFinalY();
        }
        Log.i(TAG, "fx:" + fx + ", getFinalX:" + mScroller.getFinalX() + ", dx:" + dx);
        smoothScrollBy(dx, dy, duration);
    }

    /**
     * 调用此方法设置滚动的相对偏移
     */
    public void smoothScrollBy(int dx, int dy, int duration) {
        if(duration > 0) {
            mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy, duration);
        } else {
            // 设置mScroller的滚动偏移量
            mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
        }
        // 重绘整个view,重绘过程会调用到computeScroll()方法。
        // 这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
        invalidate();
    }

    /**
     * 此方法用来检查自动调节
     *
     * @param position 要检查的位置
     */
    @SuppressWarnings("unused")
    public void checkAutoAdjust(int position){
        int childCount = getChildCount();
        // 获取可视范围内的选项的头尾位置
        int firstVisibleItemPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
        int lastVisibleItemPosition = ((LinearLayoutManager) getLayoutManager()).findLastVisibleItemPosition();
        Log.d(TAG, "childCount:" + childCount + ", position:" + position + ", firstVisibleItemPosition:" + firstVisibleItemPosition
                + "  lastVisibleItemPosition:" + lastVisibleItemPosition);
        if(position == (firstVisibleItemPosition + 1) || position == firstVisibleItemPosition){
            // 当前位置需要向右平移
            leftScrollBy(position, firstVisibleItemPosition);
        } else if (position == (lastVisibleItemPosition - 1) || position == lastVisibleItemPosition){
            // 当前位置需要向左平移
            rightScrollBy(position, lastVisibleItemPosition);
        }
    }

    private void leftScrollBy(int position, int firstVisibleItemPosition){
        View leftChild = getChildAt(0);
        if(leftChild != null){
            int startLeft = leftChild.getLeft();
            int endLeft = (position == firstVisibleItemPosition ? leftChild.getWidth() : 0);
            Log.d(TAG, "startLeft:" + startLeft + " endLeft" + endLeft);
            autoAdjustScroll(startLeft, endLeft);
        }
    }

    private void rightScrollBy(int position, int lastVisibleItemPosition){
        int childCount = getChildCount();
        View rightChild = getChildAt(childCount - 1);
        if(rightChild != null){
            int startRight = rightChild.getRight() - getWidth();
            int endRight = (position == lastVisibleItemPosition ? (-1 * rightChild.getWidth()) : 0);
            Log.d(TAG,"startRight:" + startRight + " endRight:" + endRight);
            autoAdjustScroll(startRight, endRight);
        }
    }

    /**
     *
     * @param start 滑动起始位置
     * @param end 滑动结束位置
     */
    private void autoAdjustScroll(int start, int end){
        mLastX = start;
        mScroller.startScroll(start, 0, end - start, 0);
        postInvalidate();
    }


    /**
     * 将指定item平滑移动到整个view的中间位置
     * @param position 指定的item的位置
     */
    public void smoothScrollMaster(int position) {
        // 这个方法是为了设置Scroller的滚动的,需要根据业务需求,编写算法。
    }

    int position = 0;
    public boolean dispatchKeyEvent(KeyEvent event) {
        int dx = this.getChildAt(0).getWidth();

        View focusView = this.getFocusedChild();
        position = (int)focusView.getTag();
        if(this.getChildAt(position) != null){
            dx = this.getChildAt(position).getWidth();
        }
        Log.e(TAG,"dispatchKeyEvent ======dx="+dx+",postion="+position);
        switch (event.getKeyCode()){
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                if(event.getAction() == KeyEvent.ACTION_UP){
                    return true;
                }else{
                    View rightView = FocusFinder.getInstance().findNextFocus(this,focusView,FOCUS_RIGHT);
                    if(rightView != null){
                        Log.e(TAG,"rightView != null");
                        rightView.requestFocusFromTouch();//获取焦点
                        position += 1;
                        if(position >= this.getChildCount()-1){
//                            checkAutoAdjust(position);
                            this.smoothScrollBy(dx,0);
                        }
                        return true;
                    }else{
                        //将滑动的动作放在不为空中做,只有在最后一项按右键才走这步,
                        // 如果什么都不做,直接return false的话,焦点到最后一项后直接滑到下一个page
                        this.smoothScrollBy(dx,0);
//                        checkAutoAdjust(position);
                        return true;
                    }
                }

            case KeyEvent.KEYCODE_DPAD_LEFT:
                View leftView = FocusFinder.getInstance().findNextFocus(this,focusView,FOCUS_LEFT);
                if(event.getAction() == KeyEvent.ACTION_UP){
                    return true;
                }else{
                    if(leftView != null){
//                        checkAutoAdjust(position);
                        this.smoothScrollBy(-dx,0);
                        Log.e(TAG,"leftView != null");
                        leftView.requestFocusFromTouch();
                        position -= 1;
                        return true;
                    }else{
                        Log.e(TAG,"leftView = null");
                        this.smoothScrollBy(-dx,0);
                        return true;
                    }
                }

        }
        return super.dispatchKeyEvent(event);
    }

    private void setViewPostion(View mNextFocused){
        if(mNextFocused != null){
            position = getChildAdapterPosition(mNextFocused);
        }else{
            position = getChildAdapterPosition(getFocusedChild());
        }
    }



    }

使用过程中遇到的问题总结:

1. 当前可见的item显示正常,当按右键向右移动时,可见项以外的项,从第一项开始显示;

     具体示例:一个横向列表有10条数据,一屏只能显示5个,按右键移动,从第6个item开始,又开始显示第一个item的内容,并且position的值也从0开始了;

    解决方法:在给这个控件设置适配器的时候,将

simpleRecyclerAdapter.setHasStableIds(true);的true改为false
final SimpleRecyclerAdapter simpleRecyclerAdapter = new SimpleRecyclerAdapter(context,videoCateContents);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
        linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

        holder.mImageView.setLayoutManager(linearLayoutManager);
        simpleRecyclerAdapter.setHasStableIds(false);
        holder.mImageView.setAdapter(simpleRecyclerAdapter);
        holder.mImageView.setItemAnimator(null);

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值