androidTV开发---RecyclerView焦点乱跳问题

1、KeyUtils

import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.util.Log;
import android.view.View;
import android.view.animation.ScaleAnimation;

import androidx.recyclerview.widget.RecyclerView;

import com.pjx.core.event.callback.CallbackManager;
import com.pjx.core.event.callback.IGlobalCallback;

import rxhttp.wrapper.utils.LogUtil;

public class KeyUtils {

    public static void keyListenerBackground(final View mView){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    mView.setSelected(true);
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.1f, 1f, 1.1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);
                }else {
                    mView.setSelected(false);
                    ScaleAnimation animationTwo = new ScaleAnimation(1.1f, 1f, 1.1f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);
                }
            }
        });
    }
    public static void keyListenerBackground(final View mView, final View v2){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    v2.setSelected(true);
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.1f, 1f, 1.1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);
                }else {
                    v2.setSelected(false);
                    ScaleAnimation animationTwo = new ScaleAnimation(1.1f, 1f, 1.1f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);
                }
            }
        });
    }
    public static void keyListenerBackground(final View mView, final int position, final String type){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    IGlobalCallback callback= CallbackManager.Companion.getInstance().getCallback(type);
                    if (callback != null) {
                        callback.executeCallback(position);
                    }
                    mView.setSelected(true);
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.1f, 1f, 1.1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);
                }else {
                    mView.setSelected(false);
                    ScaleAnimation animationTwo = new ScaleAnimation(1.1f, 1f, 1.1f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);
                }
            }
        });
    }
    /*****************************item获得与失去焦点的监听***************************/
    public static void keyListenerItem(final View mView){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    mView.setSelected(true);
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.02f, 1f, 1.02f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);

                    yesBorder(mView);
                }else {
                    mView.setSelected(false);
                    //mView.setBackgroundResource(R.drawable.shape_select_background_false);
                    ScaleAnimation animationTwo = new ScaleAnimation(1.02f, 1f, 1.02f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);

                    noBorder(mView);
                }
            }
        });
    }
    public static void keyListenerItem(final View mView,RecyclerView rv){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                LogUtil.log("选择"+b);
                LogUtil.log("view="+view);
                if (b) {
                    mView.setSelected(true);
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.02f, 1f, 1.02f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);
                    rv.smoothScrollToPosition(rv.getChildLayoutPosition(view));

                    yesBorder(mView);
                }else {
                    mView.setSelected(false);
                    //mView.setBackgroundResource(R.drawable.shape_select_background_false);
                    ScaleAnimation animationTwo = new ScaleAnimation(1.02f, 1f, 1.02f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);

                    noBorder(mView);
                }
            }
        });
    }
    public static void keyListenerItem(final View mView, final int position, final String type){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    IGlobalCallback callback=CallbackManager.Companion.getInstance().getCallback(type);
                    if (callback != null) {
                        callback.executeCallback(position);
                    }
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.06f, 1f, 1.06f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);

                    yesBorder(mView);
                }else {
                    ScaleAnimation animationTwo = new ScaleAnimation(1.06f, 1f, 1.06f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);

                    noBorder(mView);
                }
            }
        });
    }
    /********************************************************/
    public static void keyListenerText(final View mView){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.15f, 1f, 1.15f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);

                    yesBorder(mView);
                }else {
                    ScaleAnimation animationTwo = new ScaleAnimation(1.15f, 1f, 1.15f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);

                    noBorder(mView);
                }
            }
        });
    }
    private static void yesBorder(View mView){
        //创建Drawable对象
        GradientDrawable drawable=new GradientDrawable();
        drawable.setStroke(3, Color.parseColor("#A4D9DF"));
        drawable.setColor(Color.TRANSPARENT);
        drawable.setCornerRadius(6);
        mView.setBackground(drawable);
    }
    private static void noBorder(View mView){
        GradientDrawable drawable=new GradientDrawable();
        drawable.setCornerRadius(6);
        drawable.setColor(Color.TRANSPARENT);
        mView.setBackground(drawable);
    }
}

2、AnimationUtil

import android.animation.Animator;
import android.animation.Keyframe;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.widget.ImageView;

public class AnimationUtil {

    private AnimationUtil() {
    }

    private static final String TAG = AnimationUtil.class.getSimpleName();

    public static void zoomByFactor(View view, float factor, int duration) {

        ScaleAnimation animation = AnimationBuilder.getInstance()
                .getScaleAnimation(1.0f, factor, 1.0f, factor,
                        Animation.RELATIVE_TO_SELF, 0.5f,
                        Animation.RELATIVE_TO_SELF, 0.5f, duration);

        if (animation != null) {
            view.startAnimation(animation);
        }
    }

    public static void scaleToOriginalDimension(View view, float factor, int duration) {
        ScaleAnimation animation = AnimationBuilder.getInstance()
                .getScaleAnimation(factor, 1.0f, factor, 1.0f,
                        Animation.RELATIVE_TO_SELF, 0.5f,
                        Animation.RELATIVE_TO_SELF, 0.5f, duration);

        if (animation != null) {
            view.startAnimation(animation);
        }
    }

    //键盘移动的位移动画,动画类型为属性动画
    public static void slideView(View view, final float p1, final float p2, int duration, final View showView, final View hideView) {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, "translationX", p1, p2);
        objectAnimator.setDuration(duration);
        objectAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
//                try {
//
//                } catch (Exception e) {
//                    Log.e(TAG, "wqs:slideView:onAnimationStart:Exception:" + e.toString());
//                    e.printStackTrace();
//                }
            }

            @Override
            public void onAnimationEnd(Animator animator) {
//                if (showView != null) {
//                    showView.setVisibility(View.VISIBLE);
//                }
//                if (hideView != null) {
//                    hideView.setVisibility(View.INVISIBLE);
//                }
            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
        objectAnimator.start();
    }

    public static void doBoundaryAnimation(View view, int keyCode, int ratio) {
        int leftShakeDegrees = 6 * ratio;
        int rightShakeDegrees = 2 * ratio;
        if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
            leftShakeDegrees = 2 * ratio;
            rightShakeDegrees = 6 * ratio;
        }
        PropertyValuesHolder rotateValuesHolder = PropertyValuesHolder.ofKeyframe(View.TRANSLATION_X,
                Keyframe.ofFloat(0f, 0f),
                Keyframe.ofFloat(0.2f, -leftShakeDegrees),
                Keyframe.ofFloat(0.4f, rightShakeDegrees),
                Keyframe.ofFloat(0.6f, -leftShakeDegrees),
                Keyframe.ofFloat(0.8f, rightShakeDegrees),
                Keyframe.ofFloat(1.0f, 0f)
        );

        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(view, rotateValuesHolder);
        objectAnimator.setDuration(500);
        objectAnimator.start();
    }

    public static void startLoadingAni(ImageView loadingImg) {
        if (loadingImg == null) {
            Log.e(TAG, "wqs:startLoadingAni:loadingImg==null");
            return;
        }
        loadingImg.clearAnimation();
        RotateAnimation rotateAnimation = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        rotateAnimation.setFillAfter(true);
        rotateAnimation.setRepeatMode(Animation.RESTART);
        rotateAnimation.setRepeatCount(Animation.INFINITE);
        rotateAnimation.setDuration(1000);
        rotateAnimation.setInterpolator(new LinearInterpolator());
        loadingImg.startAnimation(rotateAnimation);
        showView(loadingImg);
    }

    public static void stopLoadingAni(ImageView loadingImg) {
        if (loadingImg == null) {
            Log.e(TAG, "wqs:stopLoadingAni:loadingImg==null");
            return;
        }
        loadingImg.clearAnimation();
        goneView(loadingImg);
    }

    public static void showView(View view) {
        if (view != null) {
            view.setVisibility(View.VISIBLE);
        }
    }

    public static void hideView(View view) {
        if (view != null) {
            view.setVisibility(View.INVISIBLE);
        }
    }

    public static void goneView(View view) {
        if (view != null) {
            view.setVisibility(View.GONE);
        }
    }

    public static void setFouseView(View view) {
        if (view != null) {
            view.requestFocus();
        }
    }
}

3、AnimationBuilder

import android.view.animation.ScaleAnimation;

public final class AnimationBuilder {

    private static AnimationBuilder mInstance;

    private AnimationBuilder() {
    }

    public static synchronized AnimationBuilder getInstance() {
        if (mInstance == null) {
            mInstance = new AnimationBuilder();
        }
        return mInstance;
    }

    public ScaleAnimation getScaleAnimation(float fromX, float toX,
                                            float fromY, float toY, int pivotXType, float pivotXValue,
                                            int pivotYType, float pivotYValue, int duration) {

        ScaleAnimation sa = new ScaleAnimation(fromX, toX, fromY, toY,
                pivotXType, pivotXValue,
                pivotYType, pivotYValue);
        sa.setDuration(duration);
        sa.setFillAfter(true);
        return sa;
    }
}

4、IRecyclerView

import android.view.View;

import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;


/**
 * @author weihaichao
 * @date 2019-10-10 16:00
 */
public interface IRecyclerView {

    int ALIGN_MODE_START = 2;
    int ALIGN_MODE_CENTER = 1;
    int ALIGN_MODE_FIRST = 3;

    int DIRECTION_HORIZONTAL = LinearLayoutManager.HORIZONTAL;
    int DIRECTION_VERTICAL = LinearLayoutManager.VERTICAL;

    /**
     * 获取默认焦点View
     * @return View
     */
    @Nullable
    View getDefaultFocusView();

    /**
     * 请求默认焦点
     * @return Boolean 是否请求成功
     */
    boolean requestDefaultFocus();

    /**
     * 设置对齐方式
     * @param mode 对齐方式
     * @param direction
     */
    void setLayoutManager(int mode, int direction);
}

5、CenterLayoutManager (居中移动)

import android.content.Context;
import android.util.DisplayMetrics;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;


public class CenterLayoutManager extends LinearLayoutManager {

    public CenterLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        RecyclerView.SmoothScroller centerSmoothScroller = new CenterSmoothScroller(recyclerView.getContext());
        centerSmoothScroller.setTargetPosition(position);
        startSmoothScroll(centerSmoothScroller);
    }

    @Override
    protected int getExtraLayoutSpace(RecyclerView.State state) {
        return 100;
    }

    @Nullable
    @Override
    public View onInterceptFocusSearch(@NonNull View focused, int direction) {
        int pos = getPosition(focused);
        if (pos != findLastCompletelyVisibleItemPosition()) {
            return null;
        }
        return super.onInterceptFocusSearch(focused, direction);
    }

    private static class CenterSmoothScroller extends LinearSmoothScroller {

        CenterSmoothScroller(Context context) {
            super(context);
        }

        @Override
        public int calculateDxToMakeVisible(View view, int snapPreference) {
            final RecyclerView.LayoutManager layoutManager = getLayoutManager();
            if (layoutManager == null || !layoutManager.canScrollHorizontally()) {
                return 0;
            }
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
                    view.getLayoutParams();
            final int left = view.getLeft() - params.leftMargin;
            final int right = view.getRight() + params.rightMargin;
            final int start = layoutManager.getPaddingLeft();
            final int end = layoutManager.getWidth() - layoutManager.getPaddingRight();
            return (start + (end - start) / 2) - (left + (right - left) / 2);
        }

        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
            return 60f / displayMetrics.densityDpi;
        }
    }
}

6、BothSidesLayoutManager(两边移动)

import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;


public class BothSidesLayoutManager extends LinearLayoutManager {

    public BothSidesLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                       int position) {
        RecyclerView.SmoothScroller smoothScroller =
                new StartSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private static class StartSmoothScroller extends LinearSmoothScroller {

        StartSmoothScroller(Context context) {
            super(context);
        }

        @Override
        public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd,
                                    int snapPreference) {
            Log.d("LayoutManager","vs="+viewStart+" vd="+viewEnd+" bs="+boxStart+" bd="+boxEnd);
            return boxStart - viewStart;
        }

        @Override
        protected int getHorizontalSnapPreference() {
            return SNAP_TO_START;
        }

        @Override
        protected int getVerticalSnapPreference() {
            return SNAP_TO_START;
        }

        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
            return 80f / displayMetrics.densityDpi;
        }
    }
}

7、FirstLayoutManager(首个移动)

mport android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;


public class FirstLayoutManager extends LinearLayoutManager {

    public FirstLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                       int position) {
        RecyclerView.SmoothScroller smoothScroller =
                new FirstSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private static class FirstSmoothScroller extends LinearSmoothScroller {

        FirstSmoothScroller(Context context) {
            super(context);
        }

        @Override
        public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd,
                                    int snapPreference) {
            Log.d("LayoutManager","vs="+viewStart+" vd="+viewEnd+" bs="+boxStart+" bd="+boxEnd);
            return boxStart - viewStart + (viewEnd - viewStart);
        }

        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
            return 150f / displayMetrics.densityDpi;
        }
    }
}

下面的方式有缺陷

1、CenterFocusLinearLayoutManager 类是解决LinearLayoutManager模式下的item居中问题同时也控制了焦点乱跳问题,有多个重载构造函数,其中isLeft表示是否控制左键,其他参数就不用我说了吧。如果false表示不自己控制左键,那么将默认使用系统的规范。
isLeft默认true
isRight默认true
isUp默认false
isDown默认false

public class CenterFocusLinearLayoutManager extends LinearLayoutManager {

    private int orientation = VERTICAL;

    public CenterFocusLinearLayoutManager(Context context) {
        super(context);
    }

    public CenterFocusLinearLayoutManager(Context context, boolean isLeft, boolean isRight) {
        super(context);
        this.isLeft = isLeft;
        this.isRight = isRight;
    }

    public CenterFocusLinearLayoutManager(Context context, boolean isLeft, boolean isRight, boolean isDown) {
        super(context);
        this.isLeft = isLeft;
        this.isRight = isRight;
        this.isDown = isDown;
    }

    public CenterFocusLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
        this.orientation = orientation;
    }

    public CenterFocusLinearLayoutManager(Context context, int orientation, boolean reverseLayout, boolean isLeft, boolean isRight) {
        super(context, orientation, reverseLayout);
        this.isLeft = isLeft;
        this.isRight = isRight;
        this.orientation = orientation;
    }

    public CenterFocusLinearLayoutManager(Context context, int orientation, boolean reverseLayout, boolean isLeft, boolean isRight, boolean isDown) {
        super(context, orientation, reverseLayout);
        this.isLeft = isLeft;
        this.isRight = isRight;
        this.isDown = isDown;
        this.orientation = orientation;
    }

    public CenterFocusLinearLayoutManager(Context context, int orientation, boolean reverseLayout, boolean isLeft, boolean isRight, boolean isUp, boolean isDown) {
        super(context, orientation, reverseLayout);
        this.isLeft = isLeft;
        this.isRight = isRight;
        this.isUp = isUp;
        this.isDown = isDown;
        this.orientation = orientation;
    }


    private boolean isLeft = true;
    private boolean isRight = true;
    private boolean isUp = false;
    private boolean isDown = false;

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    public static class CenterSmoothScroller extends LinearSmoothScroller {

        CenterSmoothScroller(Context context) {
            super(context);
        }

        @Override
        public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
            return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
        }
    }

    private long mLastKeyDownTime = 0;

    @Override
    public View onInterceptFocusSearch(View focused, int direction) {

        long current = System.currentTimeMillis();
        if (current - mLastKeyDownTime < 500) {//延时获得焦点
            return focused;
        } else {
            mLastKeyDownTime = current;
            if (getFocusedChild() != null) {
                int pos = getPosition(getFocusedChild());//这里要用这个方法
//            int pos = getPosition(focused);//这里要用这个方法
                int count = getItemCount();
                int lastVisiblePosition = findLastVisibleItemPosition();
                switch (direction) {
                    case View.FOCUS_LEFT:
                        if (isLeft) {
                            if (orientation == VERTICAL) {
                                return focused;
                            } else {
                                if (pos == 0) {
                                    return focused;
                                }
                            }
                        }
                        break;
                    case View.FOCUS_RIGHT:
                        if (isRight) {
                            if (orientation == VERTICAL) {
                                return focused;
                            } else {
                                if (pos == count - 1) {
                                    return focused;
                                }
                            }
                        }
                        break;
                    case View.FOCUS_UP:
                        if (isUp) {
                            if (orientation == VERTICAL) {
                                if (pos == 0) {
                                    return focused;
                                }
                            } else {
                                return focused;
                            }
                        }
                        break;
                    case View.FOCUS_DOWN:
                        if (isDown) {
                            if (orientation == VERTICAL) {
                                if (pos == count - 1) {
                                    return focused;
                                }
                            } else {
                                return focused;
                            }
                        }
                        break;
                }
                if (pos > getItemCount() - 1) {
                    return focused;
                } else if (pos > lastVisiblePosition) {
                    scrollToPosition(pos);
                }
            }
            return super.onInterceptFocusSearch(focused, direction);
        }
    }
}

如果想要居中的话需要写一个回调接口,在adapter中传position给activity,并在回调中加入下面代码即可

mRecyclerView.smoothScrollToPosition(position);

2、CenterFocusGridLayoutManager 类是解决GridLayoutManager模式下的item居中问题同时也控制了焦点乱跳问题,如果不居中,本人也想不到其他方法控制焦点乱跳,网上的方法都试过,没效果。
isLeft默认true
isRight默认true
isUp默认false
isDown默认false
isAlone默认false

public class CenterFocusGridLayoutManager extends GridLayoutManager {

    private int spanCount;
    private int orientation= VERTICAL;

    public CenterFocusGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
        this.spanCount = spanCount;
    }

    public CenterFocusGridLayoutManager(Context context, int spanCount, boolean isAlone) {
        super(context, spanCount);
        this.spanCount = spanCount;
        this.isAlone = isAlone;
    }

    public CenterFocusGridLayoutManager(Context context, int spanCount, boolean isLeft, boolean isRight) {
        super(context, spanCount);
        this.spanCount = spanCount;
        this.isLeft = isLeft;
        this.isRight = isRight;
    }

    public CenterFocusGridLayoutManager(Context context, int spanCount, boolean isLeft, boolean isRight, boolean isDown) {
        super(context, spanCount);
        this.spanCount = spanCount;
        this.isLeft = isLeft;
        this.isRight = isRight;
        this.isDown = isDown;
    }

    public CenterFocusGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout, boolean isLeft, boolean isRight) {
        super(context, spanCount, orientation, reverseLayout);
        this.spanCount = spanCount;
        this.isLeft = isLeft;
        this.isRight = isRight;
        this.orientation = orientation;
    }

    public CenterFocusGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout,boolean isAlone, boolean isLeft, boolean isRight,boolean isUp, boolean isDown) {
        super(context, spanCount, orientation, reverseLayout);
        this.spanCount = spanCount;
        this.orientation = orientation;
        this.isAlone = isAlone;
        this.isLeft = isLeft;
        this.isRight = isRight;
        this.isUp = isUp;
        this.isDown = isDown;
    }

    private boolean isRight = true;
    private boolean isLeft = true;
    private boolean isUp=false;
    private boolean isDown = false;
    private boolean isAlone=false;


    //用来控制自定义每行/每列不同个数的焦点,默认false。因为平常的就不适用了
    private int[] rightPosArray;
    private int[] leftPosArray;

    public void setRightPosArray(int[] rightPosArray) {
        this.rightPosArray = rightPosArray;
    }
    public void setLeftPosArray(int[] leftPosArray) {
        this.leftPosArray = leftPosArray;
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    public static class CenterSmoothScroller extends LinearSmoothScroller {

        CenterSmoothScroller(Context context) {
            super(context);
        }

        @Override
        public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
            return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
        }
    }
    private long mLastKeyDownTime = 0;
    @Override
    public View onInterceptFocusSearch(View focused, int direction) {
        long current = System.currentTimeMillis();
        if (current - mLastKeyDownTime < 500) {
            return focused;
        } else {
            mLastKeyDownTime = current;
            if (getFocusedChild() != null) {
                int pos = getPosition(getFocusedChild());
                int lastVisibleItemPos = findLastVisibleItemPosition();
                int count = getItemCount();
                pos += 1;
                if (direction == View.FOCUS_RIGHT) {
                    if (isAlone) {
                        for (int position : rightPosArray) {
                            if (pos == position) {
                                return focused;
                            }
                        }
                    } else if (isRight) {
                        if (orientation == VERTICAL) {
                            if (pos % spanCount == 0) {//|| pos==getChildCount()
                                return focused;
                            }
                        } else {
                            if (pos > count - spanCount) {
                                return focused;
                            }
                        }
                    }
                } else if (direction == View.FOCUS_LEFT) {
                    if (isAlone) {
                        for (int position : leftPosArray) {
                            if (pos == position) {
                                return focused;
                            }
                        }
                    } else if (isLeft) {
                        if (orientation == VERTICAL) {
                            if (pos % spanCount == 1) {
                                return focused;
                            }
                        } else {
                            if (pos <= spanCount) {
                                return focused;
                            }
                        }
                    }
                } else if (direction == View.FOCUS_UP) {
                    if (isUp) {
                        if (orientation == VERTICAL) {
                            if (pos <= spanCount) {
                                return focused;
                            }
                        } else {
                            if (pos % spanCount == 1) {
                                return focused;
                            }
                        }
                    }
                } else if (direction == View.FOCUS_DOWN) {
                    if (isDown) {
                        if (orientation == VERTICAL) {
                            if (pos > count - spanCount) {
                                return focused;
                            }
                        } else {
                            if (pos % spanCount == 0) {
                                return focused;
                            }
                        }
                    }
                }
                if (pos > getItemCount()) {
                    return focused;
                } else if (pos > lastVisibleItemPos+1) {
                    scrollToPosition(pos);
                }
            }
            return super.onInterceptFocusSearch(focused, direction);
        }
    }
}

3、给大家一个焦点获得与失去监听的工具类(获得焦点的view会变大且可以获得边框并且可以通过回调得到选中的position

public class KeyUtils {

    public static void keyListenerBackground(final View mView){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    mView.setSelected(true);
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.1f, 1f, 1.1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);
                }else {
                    mView.setSelected(false);
                    ScaleAnimation animationTwo = new ScaleAnimation(1.1f, 1f, 1.1f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);
                }
            }
        });
    }
    public static void keyListenerBackground(final View mView, final View v2){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    v2.setSelected(true);
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.1f, 1f, 1.1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);
                }else {
                    v2.setSelected(false);
                    ScaleAnimation animationTwo = new ScaleAnimation(1.1f, 1f, 1.1f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);
                }
            }
        });
    }
    public static void keyListenerBackground(final View mView, final int position, final String type){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    IGlobalCallback callback= CallbackManager.getInstance().getCallback(type);
                    if (callback != null) {
                        callback.executeCallback(position);
                    }
                    mView.setSelected(true);
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.1f, 1f, 1.1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);
                }else {
                    mView.setSelected(false);
                    ScaleAnimation animationTwo = new ScaleAnimation(1.1f, 1f, 1.1f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);
                }
            }
        });
    }
    /*****************************item获得与失去焦点的监听***************************/
    public static void keyListenerItem(final View mView){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    mView.setSelected(true);
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.02f, 1f, 1.02f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);

                    yesBorder(mView);
                }else {
                    mView.setSelected(false);
                    //mView.setBackgroundResource(R.drawable.shape_select_background_false);
                    ScaleAnimation animationTwo = new ScaleAnimation(1.02f, 1f, 1.02f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);

                    noBorder(mView);
                }
            }
        });
    }
    public static void keyListenerItem(final View mView, final int position, final String type){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    IGlobalCallback callback=CallbackManager.getInstance().getCallback(type);
                    if (callback != null) {
                        callback.executeCallback(position);
                    }
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.06f, 1f, 1.06f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);

                    yesBorder(mView);
                }else {
                    ScaleAnimation animationTwo = new ScaleAnimation(1.06f, 1f, 1.06f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);

                    noBorder(mView);
                }
            }
        });
    }
    /********************************************************/
    public static void keyListenerText(final View mView){
        mView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b) {
                    ScaleAnimation animationTwoCopy = new ScaleAnimation(1f, 1.15f, 1f, 1.15f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwoCopy.setDuration(300);
                    animationTwoCopy.setFillAfter(true);
                    mView.startAnimation(animationTwoCopy);

                    yesBorder(mView);
                }else {
                    ScaleAnimation animationTwo = new ScaleAnimation(1.15f, 1f, 1.15f, 1f, mView.getWidth() / 2, mView.getHeight() / 2);
                    animationTwo.setDuration(300);
                    animationTwo.setFillAfter(true);
                    mView.startAnimation(animationTwo);

                    noBorder(mView);
                }
            }
        });
    }
    private static void yesBorder(View mView){
        //创建Drawable对象
        GradientDrawable drawable=new GradientDrawable();
        drawable.setStroke(3, Color.parseColor("#A4D9DF"));
        drawable.setColor(Color.TRANSPARENT);
        drawable.setCornerRadius(6);
        mView.setBackground(drawable);
    }
    private static void noBorder(View mView){
        GradientDrawable drawable=new GradientDrawable();
        drawable.setCornerRadius(6);
        drawable.setColor(Color.TRANSPARENT);
        mView.setBackground(drawable);
    }
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值