Android 对Tv开发的总结

在新公司干了快一个月了,感觉对tv开发有了一定的经验。主要对recyclerview有了一定的认识。

1.调试盒子。

盒子的ip地址与电脑的ip地址保持一致

adb connect  ip地址  

adb uninstall 包名

2.焦点控制需要的一些方法。

requestFocus()  //获得焦点

setNextFocusXXX//就是让下一个按上下左右键聚焦的view

3.Recyclerview的用法

TV布局:1.如果是简单的页面(死界面),建议直接就用textview直接布局,焦点逻辑用setNextFocusXXX就可以了。像这种:


动态界面:较复杂的那种,就需要自定义recyclerview,像:


主要是一个recyclerview来布局这个页面,用到recyclerview的分类,gridlayout的“合并单元格”的知识点。这个已经总结过了。下面开始总结我开发中问道的头疼的问题。

.滑动焦点错乱了

.当页面遥控器控制不了滑动了怎么办

.如何判定分页以及分页加载刷新后焦点逻辑

.删除后我的焦点该如何控制

.导航栏下来如何保存导航的状态以及第一个聚焦的始终是第一个item。

滑动焦点错乱了

.当遥控一直按住的时候,突然不知道焦点飞哪去了。

1.看看position是否在乱跳,我的主要是position乱跳导致焦点乱跳,然后我把布局都用LinearLayout限定宽高解决的

2.当遥控器按到底部的时候,如果底部还有数据但并没有显示在屏幕上,可能也会导致焦点乱跳。所以尽量让底部的界面露出来一些像上图一样。当然也可代码判断当焦点失去的时候,让页面先滑上来一些

3.什么去除recyclerview的动画以及写找不到焦点的处理方法

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

onSearchFocusfailed方法百度很多,我没用到过

当页面遥控器控制不了滑动了怎么办

当遥控器向下按时没问题的,但向上按的时候滑不上去了比如下图:


此时屏幕上部应该还有数据,但并不显示在屏幕上,此时如果焦点在第一个item上,遥控器向上按就没反应,超出屏幕的部分应该是获取不到焦点的

解决方案有两种:

1.同事的方案:

package com.familybox.ui.family.widget;

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;

/**
 * 日期:2018/4/4
 * <p>
 * 作者:xudiwei
 * <p>
 * 描述:用于TV垂直方向的RecyclerView.
 */
public class ParentRecyclerView extends RecyclerView {
    private static final String TAG = "ParentSchoolRecyclerVie";

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

    public ParentRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ParentRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        setClipChildren(false);
        setClipToPadding(false);
        setWillNotDraw(false);
        setChildrenDrawingOrderEnabled(true);
    }


    @Override
    public void smoothScrollBy(int dx, int dy) {
        if (dy > 0) {
            super.smoothScrollBy(dx, dy + 350);
        } else if (dy < 0) {
            super.smoothScrollBy(dx, dy - 350);
        } else {
            super.smoothScrollBy(dx, dy);
        }
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        Log.d(TAG, "dispatchKeyEvent:--> "+event.isLongPress());
        return super.dispatchKeyEvent(event);
    }



    private int focusViewIndex;
    private int focusId;

    /**
     * 自定义重绘排序,好让获取焦点的子view最后绘制,这样子view放大时不会被遮住.
     *
     * @param childCount
     * @param i
     * @return
     */
    @Override
    protected int getChildDrawingOrder(int childCount, int i) {
        focusViewIndex = indexOfChild(getFocusedChild());
        Log.d(TAG, "getChildDrawingOrder:--> Index: " + focusViewIndex);
        if (focusViewIndex == -1) {
            return i;
        }
        if (focusViewIndex == i) {
            focusId = i;
            return childCount - 1;
        } else if (i == childCount - 1) {
            return focusId;
        } else {
            return i;
        }

    }




}

重写了smoothScrollby的方法,让每次recyclerview默认滑动的时候多滑一段距离。就是始终保持顶部会露出一点(有缺陷的,不适用于这个界面,适用于界面比较固定,不会有二级标题的那种

焦点搜索失败同样也处理了

package com.familybox.ui.family.widget;

import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * 日期:2018/4/8
 * <p>
 * 作者:XXX
 * <p>
 * 描述:处理找不到焦点时的处理方式的GridLayoutManager
 */
public class FocusGridLayoutManager extends GridLayoutManager {
    private static final String TAG = "FocusGridLayoutManager";

    public FocusGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public FocusGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
    }

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

   /* @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            super.onLayoutChildren(recycler, state);
        } catch (IndexOutOfBoundsException e) {
            Log.e("probe", "meet a IOOBE in RecyclerView");
        }
    }*/


    @Override
    public View onFocusSearchFailed(View focused, int focusDirection, RecyclerView.Recycler recycler, RecyclerView.State state) {
//        ViewParent parent = focused.getParent();
//        if (parent instanceof RecyclerView) {
//            RecyclerView recyclerView = (RecyclerView) parent;
//            if (focusDirection == View.FOCUS_DOWN) {
//                recyclerView.smoothScrollBy(0, 1);
//            } else if (focusDirection == View.FOCUS_UP) {
//                recyclerView.smoothScrollBy(0, -1);
//            }
//        }
        return focused;
//        return super.onFocusSearchFailed(focused, focusDirection, recycler, state);
    }
}

我的方案就是重写了recyclerview分发事件的方法,当滑动的时候如果焦点得不到了(指的是超出屏幕外,就让它滑出来)缺点也很明显,先滑出来再按一下才聚焦

 @Override
    public boolean dispatchKeyEvent(KeyEvent event) {


        Log.i("VVCCC::", event.getKeyCode() + "");


        if (mInterceptLister != null && mInterceptLister.onIntercept(event)) {
            return true;
        }


        if (KeyEvent.KEYCODE_BACK == event.getKeyCode()) {
            Log.i("RRRRFF::", "isreturn");
            return false;
        }

        boolean result = super.dispatchKeyEvent(event);
        View focusView = this.getFocusedChild();
        currentfocus = getChildPosition(focusView);//把当前的聚焦对象的位置记录下来


        if (focusView == null) {
            Log.d(TAG, "dispatchKeyEvent:--> focusView == null" + currentfocus);
            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) {
                Log.i("FFDDD225::", "");

                return true;
            } else {
                switch (event.getKeyCode()) {
                    case KeyEvent.KEYCODE_DPAD_RIGHT:
                        View rightView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
                        View leftViews = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
                        if (rightView == null && leftViews == null) {
                            if (getAdapter() != null) {
                                ((PersonalityGrowthAdapter) getAdapter()).func(1);
                            }
                        }
                        Log.i(TAG, "rightView is null:" + (rightView == null));
                        Log.i(TAG, "leftViews is null:" + (leftViews == null));

                        if (rightView != null) {
                            rightView.requestFocus();
                            return true;
                        } else {
                            this.smoothScrollBy(dx, 0);
                            return true;
                        }
                    case KeyEvent.KEYCODE_SETTINGS:
                        if (mOnLoadMoreListener != null) {
                            mOnLoadMoreListener.ParentsSubscribe();
                        }
                        return true;

                    case KeyEvent.KEYCODE_DPAD_LEFT:
                        View leftView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
                        View rightViews = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);

                        if (rightViews == null && leftView == null) {
                            if (getAdapter() != null) {
                                ((PersonalityGrowthAdapter) getAdapter()).func(-1);
                            }
                        }
                        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 lView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
                        View rView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
                        View downView;
                        if (lView == null && rView == null) {
                            //焦点左边的view与焦点右边的view都是空的话,说明此事焦点在头部,
                            // 说明此时是从导航栏按下来,要让第一个item获得焦点
                            downView = getChildAt(2);//第一个item就是第二个position的位置
                        } else {
                            downView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_DOWN);
                        }


                        Log.i(TAG, " downView is null:" + (downView == null));
                        if (downView != null) {
                            downView.requestFocus();

                        } else {
                            this.smoothScrollBy(0, dy);
                        }
                        if (mOnLoadMoreListener != null) {
                            if (downView == null) {
                                if (isSlideToBottom(this)) {
                                    mOnLoadMoreListener.onLoadMore(currentfocus);
                                }
                            }
                        }
                        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) {
                            Log.i("FFDDD::", "66666");
                            return true;
                        } else {
                            if (upView != null) {
                                Log.i("FFDDD::", "DDDD");
                                View topView = FocusFinder.getInstance().findNextFocus(this, upView, View.FOCUS_UP);
                                if (topView != null) {
                                    this.smoothScrollBy(0, -dy);
                                } else {
                                    //this.smoothMoveToPosition(0);
                                    //  this.smoothScrollBy(0, -this.getChildAt(0).getHeight()*2);
                                }

                                upView.requestFocus();
                                return true;
                            } else {
                                Log.i("FFDDD::", "顶部view为空");
                                LayoutManager layoutManager = getLayoutManager();
                                int childLayoutPosition = getChildLayoutPosition(focusView);
                                if (layoutManager instanceof GridLayoutManager) {
                                    int spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
                                    if (childLayoutPosition <= spanCount) {
                                        return result;
                                    }
                                } else if (layoutManager instanceof LinearLayoutManager) {
                                    if (childLayoutPosition == 0) {
                                        return result;
                                    }
                                }
                                this.smoothScrollBy(0, -dy);
                                return true;
                            }
                        }
                }
            }
        }
        return result;
    }
如何判定分页以及分页加载刷新后焦点逻辑


判定分页,也就是判定滑动到底部了,通常按道理来说可以和手机recyeclerview一样在这里处理

@Override
    public void onScrollStateChanged(int state) {
        if (state == SCROLL_STATE_IDLE) {
            // 加载更多回调,个人认为要保存一下上次焦点的位置
            /*if (null != mOnLoadMoreListener) {
                if (getLastVisiblePosition() ==getAdapter().getItemCount() - (1 + mLoadMoreBeforehandCount)) {
                    mOnLoadMoreListener.onLoadMore(currentfocus);
                    isBottom=true;
                }else{
                    isBottom=false;
                }
            }*/
        }
        super.onScrollStateChanged(state);
        if (mShouldScroll) {
            mShouldScroll = false;
            smoothMoveToPosition(mToPosition);
        }
    }

但我用了之后发现了一个问题:就是我遥控器控制的焦点还没有到底部的时候,就触发了。我的方案是在遥控器下按的时候,判断是否完全到了屏幕底部,在触发加载更多的事件

                  case KeyEvent.KEYCODE_DPAD_DOWN:
                        //让第一个默认选中
                        View lView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
                        View rView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
                        View downView;
                        if (lView == null && rView == null) {
                            //焦点左边的view与焦点右边的view都是空的话,说明此事焦点在头部,
                            // 说明此时是从导航栏按下来,要让第一个item获得焦点
                            downView = getChildAt(2);//第一个item就是第二个position的位置
                        } else {
                            downView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_DOWN);
                        }


                        Log.i(TAG, " 
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值