RecycleView嵌套,addItemDecoration之后,多次滑动,item间隔拉大

    项目中要实现一个竖向的RecycleView嵌套一个横向的RecycleView,展示多个类别的影片推荐。上下拉动列表的过程中,每一项的Item会出现间隔拉大的问题。解决方法如下:

      从网上查到的资料来看,是因为添加了分隔线的原因,分隔线多次重复加载,造成间隔越来越大。项目中外层的RecycleView没有添加分隔线,只在内层添加了,所以只帖内层代码:

     外层RecycleView的适配器:

package com.hisense.movienow.HorizontalView;

import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.hisense.movienow.DetailActivity;
import com.hisense.movienow.MovieCategory;
import com.hisense.movienow.R;
import com.hisense.movienow.bean.VideoCateContent;

import java.util.List;

/**
 * Created by wang on 2018/11/13.
 */

public class ListRecyclerAdapter extends RecyclerView.Adapter {
    private static final String TAG = ListRecyclerAdapter.class.getSimpleName();
    private LayoutInflater mInflater;
    private List mListOfApps;
    List videoCateContents;
//    private int currentPosition = 0;
    private Context context;
    int proPosition = -1;
    SpaceItemDecoration divider;

    public ListRecyclerAdapter(Context context, List mListOfApps){
        mInflater = LayoutInflater.from(context);
        this.context = context;
        this.mListOfApps = mListOfApps;
        divider = new SpaceItemDecoration(18,18);
    }

    public void setMList(List movieCategories){
        this.mListOfApps = movieCategories;
    }

    @SuppressWarnings("unused")
    public void setData(List mListOfApps){
        this.mListOfApps = mListOfApps;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.list_view_layout, parent, false);
        ViewHolder vh = new ViewHolder(view);
        vh.mImageView = (SimpleRecycleView) view.findViewById(R.id.movie_gridview);
        vh.mTextView = (TextView) view.findViewById(R.id.category_title);
        vh.mImageView.addItemDecoration(divider);
        return vh;
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
//        currentPosition = position;
        MovieCategory videoCateContent = mListOfApps.get(position);
        holder.mTextView.setText(videoCateContent.getCategory_title());
        videoCateContents = videoCateContent.getMovieItemList();
        SimpleRecyclerAdapter simpleRecyclerAdapter = new SimpleRecyclerAdapter(context,videoCateContents);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
        linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

        holder.mImageView.setLayoutManager(linearLayoutManager);
//   网上找到的方法,但不管用,会报数组越界
//        divider = (SpaceItemDecoration) holder.mImageView.getItemDecorationAt(0);
//        if(divider == null){
//            holder.mImageView.addItemDecoration(new SpaceItemDecoration(18,18));
//        }

//        simpleRecyclerAdapter.setPosition(0);
        holder.mImageView.setAdapter(simpleRecyclerAdapter);
        simpleRecyclerAdapter.setOnItemClickListener(new SimpleRecyclerAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Intent intent = new Intent(context,DetailActivity.class);
                intent.putExtra("media_id",videoCateContents.get(position).getId());
                context.startActivity(intent);
            }
        });

        simpleRecyclerAdapter.setOnItemSelectListener(new SimpleRecyclerAdapter.OnItemSelectListener() {
            @Override
            public void onItemSelect(View view, int subposition) {
                Log.e(TAG,"SimpleRecycleView======="+","+position+",proPosition="+proPosition);
                if(position != proPosition){
                    holder.mImageView.getChildAt(0).requestFocus();
                }
                proPosition = position;
            }
        });

//        // 设置itemView可以获得焦点,外层的RecycleView不能有焦点
        holder.itemView.setFocusable(false);
        holder.itemView.setTag(position);

     }


    @Override
    public int getItemCount() {
        return mListOfApps.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder{
        SimpleRecycleView mImageView;
        TextView mTextView;

        ViewHolder(View itemView) {
            super(itemView);
        }
    }

}

    外层RecycleView的布局文件:




    

    

    


    自定义的横向的RecycleView:

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 wang 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();
        if(this.getChildAt(position) != null){
            dx = this.getChildAt(position).getWidth();
        }
        Log.e(TAG,"dispatchKeyEvent ======dx="+dx+",postion="+position);
        View focusView = this.getFocusedChild();
        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);
                        }
                        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);
                        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);
    }



    }

  SimpleRecycleView的适配器:

package com.hisense.movienow.HorizontalView;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.hisense.movienow.R;
import com.hisense.movienow.bean.VideoCateContent;
import com.hisense.movienow.widget.BoundsImageView;

import java.util.List;

/**
 * Created by wang on 2018/11/13.
 */

public class SimpleRecyclerAdapter extends RecyclerView.Adapter {
    private static final String TAG = SimpleRecyclerAdapter.class.getSimpleName();
    private LayoutInflater mInflater;
    private List mListOfApps;
    private int currentPosition = 0;
    private Context context;

    public SimpleRecyclerAdapter(Context context, List mListOfApps){
        mInflater = LayoutInflater.from(context);
        this.context = context;
        this.mListOfApps = mListOfApps;
    }


    public void setPosition(int position) {
        currentPosition = position;
    }

    @SuppressWarnings("unused")
    public void setData(List mListOfApps){
        this.mListOfApps = mListOfApps;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.movie_item_layout, parent, false);
        ViewHolder vh = new ViewHolder(view);
        vh.mImageView = (BoundsImageView) view.findViewById(R.id.movie_image);
        vh.mTextView = (TextView) view.findViewById(R.id.movie_title);
        return vh;
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        VideoCateContent videoCateContent = mListOfApps.get(position);
        Glide.with(context).load(videoCateContent.getPoster()).into(holder.mImageView);
        holder.mTextView.setText(videoCateContent.getTitle());
        ViewGroup.LayoutParams params = holder.mImageView.getLayoutParams();
        ViewGroup.LayoutParams txtParams = holder.mTextView.getLayoutParams();
        if(videoCateContent.getPoster_type() == 0){//竖海报
            params.width = 166;
            txtParams.width = 166;
        }else{
            params.width = 444;
            txtParams.width = 444;
        }
        holder.mImageView.setLayoutParams(params);
        holder.mTextView.setLayoutParams(txtParams);

        // 设置itemView可以获得焦点
        holder.itemView.setFocusable(true);
        holder.itemView.setTag(position);
        holder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    currentPosition = (int) holder.itemView.getTag();
                    ViewCompat.animate(holder.itemView).scaleX(1.10f).scaleY(1.10f).start();
                    Log.e(TAG,"SimpleRecycleView======="+currentPosition+","+position);

                    mOnItemSelectListener.onItemSelect(holder.itemView, currentPosition);
                    holder.mImageView.setBorderColor(context.getResources().getColor(R.color.white));
                } else {
                    ViewCompat.animate(holder.itemView).scaleX(1.0f).scaleY(1.0f).start();
                    holder.mImageView.setBorderColor(context.getResources().getColor(R.color.trans));
                }
            }
        });

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mOnItemClickListener.onItemClick(v, currentPosition);
            }
        });

        holder.itemView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
//                mOnItemKeyListener.OnItemKey(v, keyCode, event, currentPosition);
                return false;
            }
        });
    }

    @Override
    public int getItemCount() {
        return mListOfApps.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder{
        LinearLayout ll_movieItem;
        BoundsImageView mImageView;
        TextView mTextView;

        ViewHolder(View itemView) {
            super(itemView);
        }
    }

    private OnItemSelectListener mOnItemSelectListener;
    private OnItemClickListener mOnItemClickListener;
    private OnItemLongClickListener mOnItemLongClickListener;
    private OnItemKeyListener mOnItemKeyListener;

    public interface OnItemSelectListener {
        void onItemSelect(View view, int position);
    }

    public interface OnItemClickListener {
        void onItemClick(View view, int position);
    }

    public interface OnItemLongClickListener {
        void onItemLongClick(View view, int position);
    }

    public interface OnItemKeyListener {
        void OnItemKey(View view, int keyCode, KeyEvent event, int position);
    }

    public void setOnItemSelectListener(OnItemSelectListener listener){
        mOnItemSelectListener = listener;
    }

    public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) {
        this.mOnItemClickListener = mOnItemClickListener;
    }

    public void setOnItemLongClickListener(OnItemLongClickListener mOnItemLongClickListener) {
        this.mOnItemLongClickListener = mOnItemLongClickListener;
    }

    public void setOnItemKeyListener(OnItemKeyListener mOnItemKeyListener) {
        this.mOnItemKeyListener = mOnItemKeyListener;
    }
}

  最内层的布局文件:




    


    


    这是一个android tv的项目所以增加了一些按键处理,注意事项:

1. 外层RecycleView不能获得焦点,只在布局文件中加入android:focusable="false"的话不起作用,得在代码中加入recycleView.setFocusable(false);

2. 要解决间隔拉大的问题,要在外层适配器初始化的时候就将ItemDecoration定义好,在onCreateViewHolder中添加到recycleView上。即holder.mImageView.addItemDecoration(divider);在onBindViewHolder中只添加适配器即可。

 

你可能感兴趣的:(自定义控件,平板电视,android,tv,recycleview,recycleview嵌套)