zoukankan      html  css  js  c++  java
  • Android TV端的(RecyclerView)水平滚动焦点错乱问题

    package com.hhzt.iptv.ui.customview;

    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Rect;
    import android.os.Build;
    import android.support.v4.view.ViewCompat;
    import android.support.v7.widget.GridLayoutManager;
    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.MotionEvent;
    import android.view.View;

    import com.hhzt.iptv.R;
    import com.hhzt.iptv.lvb_w.interfaces.OnInterceptListener;
    import com.hhzt.iptv.lvb_w.utils.VirturlKeyPadCtr;

    /**
    * Created by Administrator on 2017/6/7.
    */

    public class TvRecyclerView extends RecyclerView {
    private static final String TAG = "TvRecyclerView";

    private int position;

    //焦点是否居中
    private boolean mSelectedItemCentered;

    private int mSelectedItemOffsetStart;

    private int mSelectedItemOffsetEnd;

    //分页的时候使用
    private int mLoadMoreBeforehandCount = 0;

    public TvRecyclerView(Context context) {
    this(context, null);
    }

    public TvRecyclerView(Context context, AttributeSet attrs) {
    this(context, attrs, -1);
    }

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

    private void init(Context context, AttributeSet attrs, int defStyle) {
    initView();
    initAttr(context, attrs);
    }

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

    private void initAttr(Context context, AttributeSet attrs) {
    if (attrs != null) {
    final TypedArray a = context.obtainStyledAttributes(attrs,
    R.styleable.TvRecyclerView);
    /**
    * 如果是towWayView的layoutManager
    */
    final String name = a.getString(R.styleable.TvRecyclerView_tv_layoutManager);


    mSelectedItemCentered = a.getBoolean(R.styleable.TvRecyclerView_tv_selectedItemCentered, false);

    mLoadMoreBeforehandCount = a.getInteger(R.styleable.TvRecyclerView_tv_loadMoreBeforehandCount, 0);

    mSelectedItemOffsetStart = a.getDimensionPixelSize(R.styleable.TvRecyclerView_tv_selectedItemOffsetStart, 0);

    mSelectedItemOffsetEnd = a.getDimensionPixelSize(R.styleable.TvRecyclerView_tv_selectedItemOffsetEnd, 0);

    a.recycle();
    }
    }


    private int getFreeWidth() {
    return getWidth() - getPaddingLeft() - getPaddingRight();
    }

    private int getFreeHeight() {
    return getHeight() - getPaddingTop() - getPaddingBottom();
    }


    @Override
    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
    super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
    }

    @Override
    public boolean hasFocus() {
    return super.hasFocus();
    }

    @Override
    public boolean isInTouchMode() {
    // 解决4.4版本抢焦点的问题
    if (Build.VERSION.SDK_INT == 19) {
    return !(hasFocus() && !super.isInTouchMode());
    } else {
    return super.isInTouchMode();
    }

    //return super.isInTouchMode();
    }

    @Override
    public void requestChildFocus(View child, View focused) {

    if (null != child) {
    if (mSelectedItemCentered) {
    mSelectedItemOffsetStart = !isVertical() ? (getFreeWidth() - child.getWidth()) : (getFreeHeight() - child.getHeight());
    mSelectedItemOffsetStart /= 2;
    mSelectedItemOffsetEnd = mSelectedItemOffsetStart;
    }
    }
    super.requestChildFocus(child, focused);
    }

    @Override
    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
    final int parentLeft = getPaddingLeft();
    final int parentRight = getWidth() - getPaddingRight();

    final int parentTop = getPaddingTop();
    final int parentBottom = getHeight() - getPaddingBottom();

    final int childLeft = child.getLeft() + rect.left;
    final int childTop = child.getTop() + rect.top;

    final int childRight = childLeft + rect.width();
    final int childBottom = childTop + rect.height();

    final int offScreenLeft = Math.min(0, childLeft - parentLeft - mSelectedItemOffsetStart);
    final int offScreenRight = Math.max(0, childRight - parentRight + mSelectedItemOffsetEnd);

    final int offScreenTop = Math.min(0, childTop - parentTop - mSelectedItemOffsetStart);
    final int offScreenBottom = Math.max(0, childBottom - parentBottom + mSelectedItemOffsetEnd);


    final boolean canScrollHorizontal = getLayoutManager().canScrollHorizontally();
    final boolean canScrollVertical = getLayoutManager().canScrollVertically();

    // Favor the "start" layout direction over the end when bringing one side or the other
    // of a large rect into view. If we decide to bring in end because start is already
    // visible, limit the scroll such that start won't go out of bounds.
    final int dx;
    if (canScrollHorizontal) {
    if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) {
    dx = offScreenRight != 0 ? offScreenRight
    : Math.max(offScreenLeft, childRight - parentRight);
    } else {
    dx = offScreenLeft != 0 ? offScreenLeft
    : Math.min(childLeft - parentLeft, offScreenRight);
    }
    } else {
    dx = 0;
    }

    // Favor bringing the top into view over the bottom. If top is already visible and
    // we should scroll to make bottom visible, make sure top does not go out of bounds.
    final int dy;
    if (canScrollVertical) {
    dy = offScreenTop != 0 ? offScreenTop : Math.min(childTop - parentTop, offScreenBottom);
    } else {
    dy = 0;
    }


    if (dx != 0 || dy != 0) {
    if (immediate) {
    scrollBy(dx, dy);
    } else {
    smoothScrollBy(dx, dy);
    }
    // 重绘是为了选中item置顶,具体请参考getChildDrawingOrder方法
    postInvalidate();
    return true;
    }


    return false;
    }


    @Override
    public int getBaseline() {
    return -1;
    }


    public int getSelectedItemOffsetStart() {
    return mSelectedItemOffsetStart;
    }

    public int getSelectedItemOffsetEnd() {
    return mSelectedItemOffsetEnd;
    }

    @Override
    public void setLayoutManager(LayoutManager layout) {
    super.setLayoutManager(layout);
    }

    /**
    * 判断是垂直,还是横向.
    */
    private boolean isVertical() {
    LayoutManager manager = getLayoutManager();
    if (manager != null) {
    LinearLayoutManager layout = (LinearLayoutManager) getLayoutManager();
    return layout.getOrientation() == LinearLayoutManager.VERTICAL;

    }
    return false;
    }

    /**
    * 设置选中的Item距离开始或结束的偏移量;
    * 与滚动方向有关;
    * 与setSelectedItemAtCentered()方法二选一
    *
    * @param offsetStart
    * @param offsetEnd 从结尾到你移动的位置.
    */
    public void setSelectedItemOffset(int offsetStart, int offsetEnd) {
    setSelectedItemAtCentered(false);
    mSelectedItemOffsetStart = offsetStart;
    mSelectedItemOffsetEnd = offsetEnd;
    }

    /**
    * 设置选中的Item居中;
    * 与setSelectedItemOffset()方法二选一
    *
    * @param isCentered
    */
    public void setSelectedItemAtCentered(boolean isCentered) {
    this.mSelectedItemCentered = isCentered;
    }


    @Override
    protected int getChildDrawingOrder(int childCount, int i) {
    View view = getFocusedChild();
    if (null != view) {

    position = getChildAdapterPosition(view) - getFirstVisiblePosition();
    if (position < 0) {
    return i;
    } else {
    if (i == childCount - 1) {//这是最后一个需要刷新的item
    if (position > i) {
    position = i;
    }
    return position;
    }
    if (i == position) {//这是原本要在最后一个刷新的item
    return childCount - 1;
    }
    }
    }
    return i;
    }

    public int getFirstVisiblePosition() {
    if (getChildCount() == 0)
    return 0;
    else
    return getChildAdapterPosition(getChildAt(0));
    }

    public int getLastVisiblePosition() {
    final int childCount = getChildCount();
    if (childCount == 0)
    return 0;
    else
    return getChildAdapterPosition(getChildAt(childCount - 1));
    }


    /***********
    * 按键加载更多 start
    **********/

    private OnLoadMoreListener mOnLoadMoreListener;

    public interface OnLoadMoreListener {
    void onLoadMore();
    }


    public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
    this.mOnLoadMoreListener = onLoadMoreListener;
    }

    private OnInterceptListener mInterceptLister;


    public void setOnInterceptListener(OnInterceptListener listener) {
    this.mInterceptLister = listener;
    }

    /**
    * 设置为0,这样可以防止View获取焦点的时候,ScrollView自动滚动到焦点View的位置
    */

    protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
    return 0;
    }

    @Override
    public void onScrollStateChanged(int state) {
    if (state == SCROLL_STATE_IDLE) {
    // 加载更多回调
    if (null != mOnLoadMoreListener) {
    if (getLastVisiblePosition() >= getAdapter().getItemCount() - (1 + mLoadMoreBeforehandCount)) {
    mOnLoadMoreListener.onLoadMore();
    }
    }
    }
    super.onScrollStateChanged(state);
    }


    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
    if (mInterceptLister != null && mInterceptLister.onIntercept(event)) {
    return true;
    }

    boolean result = super.dispatchKeyEvent(event);
    View focusView = this.getFocusedChild();
    if (focusView == null) {
    return result;
    } else {

    int dy = 0;
    int dx = 0;
    if (getChildCount() > 0) {
    View firstView = this.getChildAt(0);
    dy = firstView.getHeight();
    dx = firstView.getWidth();
    }
    if (event.getAction() == KeyEvent.ACTION_UP) {
    return true;
    } else {
    switch (event.getKeyCode()) {
    case KeyEvent.KEYCODE_DPAD_RIGHT:
    if(VirturlKeyPadCtr.isFastDoubleClick()){
    return true;
    }else {
    View rightView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
    Log.i(TAG, "rightView is null:" + (rightView == null));
    if (rightView != null) {
    rightView.requestFocus();
    return true;
    } else {
    this.smoothScrollBy(dx, 0);
    return true;
    }
    }
    case KeyEvent.KEYCODE_DPAD_LEFT:
    View leftView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
    Log.i(TAG, "leftView is null:" + (leftView == null));
    if (leftView != null) {
    leftView.requestFocus();
    return true;
    } else {
    this.smoothScrollBy(-dx, 0);
    return true;
    }
    case KeyEvent.KEYCODE_DPAD_DOWN:
    View downView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_DOWN);
    Log.i(TAG, " downView is null:" + (downView == null));
    if (downView != null) {
    downView.requestFocus();
    return true;
    } else {
    this.smoothScrollBy(0, dy);
    return true;
    }
    case KeyEvent.KEYCODE_DPAD_UP:
    View upView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_UP);
    Log.i(TAG, "upView is null:" + (upView == null));
    if (event.getAction() == KeyEvent.ACTION_UP) {
    return true;
    } else {
    if (upView != null) {
    upView.requestFocus();
    return true;
    } else {
    this.smoothScrollBy(0, -dy);
    return true;
    }

    }
    }

    }

    }
    return result;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
    return super.onInterceptTouchEvent(e);
    }


    /**
    * 设置默认选中.
    */

    public void setSelectedPosition(int pos) {
    this.smoothScrollToPosition(pos);
    }

    //防止Activity时,RecyclerView崩溃
    @Override
    protected void onDetachedFromWindow() {
    if (getLayoutManager() != null) {
    super.onDetachedFromWindow();
    }
    }


    /**
    * 是否是最右边的item,如果是竖向,表示右边,如果是横向表示下边
    *
    * @param childPosition
    * @return
    */
    public boolean isRightEdge(int childPosition) {
    LayoutManager layoutManager = getLayoutManager();

    if (layoutManager instanceof GridLayoutManager) {

    GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
    GridLayoutManager.SpanSizeLookup spanSizeLookUp = gridLayoutManager.getSpanSizeLookup();

    int totalSpanCount = gridLayoutManager.getSpanCount();
    int totalItemCount = gridLayoutManager.getItemCount();
    int childSpanCount = 0;

    for (int i = 0; i <= childPosition; i++) {
    childSpanCount += spanSizeLookUp.getSpanSize(i);
    }
    if (isVertical()) {
    if (childSpanCount % gridLayoutManager.getSpanCount() == 0) {
    return true;
    }
    } else {
    int lastColumnSize = totalItemCount % totalSpanCount;
    if (lastColumnSize == 0) {
    lastColumnSize = totalSpanCount;
    }
    if (childSpanCount > totalItemCount - lastColumnSize) {
    return true;
    }
    }

    } else if (layoutManager instanceof LinearLayoutManager) {
    if (isVertical()) {
    return true;
    }else{
    return childPosition == getLayoutManager().getItemCount() - 1;
    }
    }

    return false;
    }

    /**
    * 是否是最左边的item,如果是竖向,表示左方,如果是横向,表示上边
    *
    * @param childPosition
    * @return
    */
    public boolean isLeftEdge(int childPosition) {
    LayoutManager layoutManager = getLayoutManager();
    if (layoutManager instanceof GridLayoutManager) {
    GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
    GridLayoutManager.SpanSizeLookup spanSizeLookUp = gridLayoutManager.getSpanSizeLookup();

    int totalSpanCount = gridLayoutManager.getSpanCount();
    int childSpanCount = 0;
    for (int i = 0; i <= childPosition; i++) {
    childSpanCount += spanSizeLookUp.getSpanSize(i);
    }
    if (isVertical()) {
    if (childSpanCount % gridLayoutManager.getSpanCount() == 1) {
    return true;
    }
    } else {
    if (childSpanCount <= totalSpanCount) {
    return true;
    }
    }

    } else if (layoutManager instanceof LinearLayoutManager) {
    if (isVertical()) {
    return true;
    } else {
    return childPosition == 0;
    }

    }

    return false;
    }

    /**
    * 是否是最上边的item,以recyclerview的方向做参考
    *
    * @param childPosition
    * @return
    */
    public boolean isTopEdge(int childPosition) {
    LayoutManager layoutManager = getLayoutManager();
    if (layoutManager instanceof GridLayoutManager) {
    GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
    GridLayoutManager.SpanSizeLookup spanSizeLookUp = gridLayoutManager.getSpanSizeLookup();

    int totalSpanCount = gridLayoutManager.getSpanCount();

    int childSpanCount = 0;
    for (int i = 0; i <= childPosition; i++) {
    childSpanCount += spanSizeLookUp.getSpanSize(i);
    }

    if (isVertical()) {
    if (childSpanCount <= totalSpanCount) {
    return true;
    }
    } else {
    if (childSpanCount % totalSpanCount == 1) {
    return true;
    }
    }


    } else if (layoutManager instanceof LinearLayoutManager) {
    if (isVertical()) {
    return childPosition == 0;
    } else {
    return true;
    }

    }

    return false;
    }

    /**
    * 是否是最下边的item,以recyclerview的方向做参考
    *
    * @param childPosition
    * @return
    */
    public boolean isBottomEdge(int childPosition) {
    LayoutManager layoutManager = getLayoutManager();
    if (layoutManager instanceof GridLayoutManager) {
    GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
    GridLayoutManager.SpanSizeLookup spanSizeLookUp = gridLayoutManager.getSpanSizeLookup();
    int itemCount = gridLayoutManager.getItemCount();
    int childSpanCount = 0;
    int totalSpanCount = gridLayoutManager.getSpanCount();
    for (int i = 0; i <= childPosition; i++) {
    childSpanCount += spanSizeLookUp.getSpanSize(i);
    }
    if (isVertical()) {
    //最后一行item的个数
    int lastRowCount = itemCount % totalSpanCount;
    if (lastRowCount == 0) {
    lastRowCount = gridLayoutManager.getSpanCount();
    }
    if (childSpanCount > itemCount - lastRowCount) {
    return true;
    }
    } else {
    if (childSpanCount % totalSpanCount == 0) {
    return true;
    }
    }

    } else if (layoutManager instanceof LinearLayoutManager) {
    if (isVertical()) {
    return childPosition == getLayoutManager().getItemCount() - 1;
    } else {
    return true;
    }

    }
    return false;
    }

    }
    /*延时焦点的移动*/
    public static boolean isFastDoubleClick() {
    long time = System.currentTimeMillis();
    long timeD = time - lastClickTime;
    if ( 0 < timeD && timeD < 200) {
    return true;
    }
    lastClickTime = time;
    return false;
    }


  • 相关阅读:
    Oracle--pl/sql编程-分支语句(判断、循环)
    axios二次封装及API接口统一管理
    Vuejs之axios获取Http响应头
    对webpack和gulp的理解和区别
    nodejs+redis使用
    Linux安装Nginx、Redis、django
    js 原型里面写方法
    在layui中使用 jquery 触发select 的 change事件无效
    promise看这篇就够了
    vue 封装原型方法 加 promist .then
  • 原文地址:https://www.cnblogs.com/xiaoxiaing/p/6971322.html
Copyright © 2011-2022 走看看