积累一些关于ANDROID TV开发的经验

吐槽 

       年前辞了职,年后去香港了玩了几天,然后就开始找工作了,找工作就要面试。笔试有些题看的我一脸蒙蔽,java是基于哪种语言,超类是什么,说实话不会,pass了,回家突击背书。面试也有恶心的,问了职业发展规划,我说我要转python方向(心想我他妈坑定要坐你的位置啊),又问为什么转python,我说貌似比较火,薪资高,也被pass了,我真是日了狗,真是上层人士不懂底层码农的艰辛。最后还是找到了,新公司开发部的管理也更加的规范,老大是阿里的,感觉有很多东西要学,激情也就瞬间上来了(git真的从没真实用过),公司有自己的oa系统。

       回归正题吧,新公司业务主要是电视端。之前也做过,但也是浅尝辄止。主要是recyclerview与焦点的控制

    

技术点:

1.上面导航栏遥控器切换,下面要刷新数据

2.内容的分类,分页(加载更多),上下滑动的时候导航栏跟着动

3.焦点选中效果

4.导航栏下按的时候,跳到内容的第一个item,同理第一排内容的item上按的时候跳到对应的导航栏

解决方案:

1.用一个recyclerview来做这个页面

a.item的分类:头部+标题+内容

b.合并item

  RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
            gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    if (position==0||getItemViewType(position)==2){
                        return 5;
                    } else {
                        return 1;
                    }
                }
            });
        }

这个方法的意思呢,当item是头部或者标题的时候,我将我的5列合成一列,是内容的时候不合并。假设我有一个页面,第一行有2个item,第二行有三个item,第三行有4个item,那么我的girdlayoutmanager设置的spancount应该是2,3,4的最小公倍数12。两列的时候return6,三列的时候return4,四列的时候return3。知道这个加上recyclerview的分类那么剩下的事情就是数据扁平化了。

2。焦点控制。

     首先要确定的一点是,遥控器的确定事件就是onclick。其余的就是不要在activity里实现onkeydown方法,这样代码很乱,最好自定义recyclerview。如果不自定义的话,那么从导航栏按下,下个聚焦的不是内容的第一个,而是中间,此时要在recyclerview的dispatchKeyEvent方法处理key_down事件

  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();
                            return true;
                        } else {
                            this.smoothScrollBy(0, dy);
                            return true;
                        }

从第一排往上按回到指定的导航button的处理

思路:焦点上去的时候不要让导航的button去获取焦点,而是传给导航的linearlayout。也就是三个button不用focus监听,之间用setSelect(true)来切换图片文字。我知道在左右的keydown事件从处理。当然得先知道此时是导航的linearlayout获取焦点。

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

                        //左右两边都是空说明此事焦点在头部的linearlayout
                        if (rightViews==null&&leftView==null){
                            if (getAdapter()!=null){
                                ( (PersonalityGrowthAdapter)getAdapter()).func(-1);
                            }
                        }
        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);
                            }
                        }
 //用于导航栏左右逻辑
    public void func(int dir) {
            //当前是否在导航栏上,正数向右,负数向左
            Toast.makeText(context,"当前在导航栏上", Toast.LENGTH_SHORT).show();
            if (dir > 0) {
                if (currentNavIndex >= 0 && currentNavIndex < 2) {
                    currentNavIndex++;
                }
                for (int i = 0; i < ll_tbar.getChildCount(); i++) {
                    ll_tbar.getChildAt(i).setSelected(false);
                    TextPaint tp = ((TextView) ll_tbar.getChildAt(i)).getPaint();
                    tp.setFakeBoldText(false);
                }
                ll_tbar.getChildAt(currentNavIndex).setSelected(true);

                TextPaint tp = ((TextView) ll_tbar.getChildAt(currentNavIndex)).getPaint();
                tp.setFakeBoldText(true);
            }

            if (dir < 0) {
                if (currentNavIndex > 0 && currentNavIndex <= 2) {
                    currentNavIndex--;
                }
                for (int i = 0; i < ll_tbar.getChildCount(); i++) {
                    ll_tbar.getChildAt(i).setSelected(false);
                    TextPaint tp = ((TextView) ll_tbar.getChildAt(i)).getPaint();
                    tp.setFakeBoldText(false);
                }
                ll_tbar.getChildAt(currentNavIndex).setSelected(true);
                TextPaint tp = ((TextView) ll_tbar.getChildAt(currentNavIndex)).getPaint();
                tp.setFakeBoldText(true);
            }


            if (remoteControlListener!=null){
                remoteControlListener.changeNav(currentNavIndex);
            }

    }

3.选中布局的切换,这主要是才用组件的显示与隐藏

  holder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                    @Override
                    public void onFocusChange(View view, boolean isfous) {
                        if (isfous){
                            mCurrentfocus=position;
                            Drawable drawable=context.getResources().getDrawable(R.drawable.border_color);
                            ((PGView_ContentHolder)holder).cv_01.setForeground(drawable);

                            if (!testData01.isFunc()){
                                ((PGView_ContentHolder)holder).tv_content_s.setVisibility(View.VISIBLE);
                                ((PGView_ContentHolder)holder).tv_content.setVisibility(View.GONE);
                            }

                            enlargeAnim(view);
                        }else{
                            ((PGView_ContentHolder)holder).cv_01.setForeground(null);
                            if (!testData01.isFunc()){
                                ((PGView_ContentHolder)holder).tv_content_s.setVisibility(View.GONE);
                                ((PGView_ContentHolder)holder).tv_content.setVisibility(View.VISIBLE);
                            }
                            reduceAnim(view);
                        }

                    }
                });

4.焦点错乱。一个是遥控器上按与下按由于recyclerview复用导致的焦点错乱。

        /**
         防止RecyclerView刷新时焦点不错乱bug的步骤如下:
         (1)adapter执行setHasStableIds(true)方法
         (2)重写getItemId()方法,让每个view都有各自的id
         (3)RecyclerView的动画必须去掉
         */
        setItemAnimator(null);
 @Override
    public boolean isInTouchMode() {
        // 解决4.4版本抢焦点的问题
        if (Build.VERSION.SDK_INT == 19) {
            return !(hasFocus() && !super.isInTouchMode());
        } else {
            return super.isInTouchMode();
        }

        //return super.isInTouchMode();
    }


还有一个是加载更多时候导致的错乱

    //加载新一页的数据

    public void loadmore(List<TestData01> list,int currentfocus) {
        int start=dataList.size()-1;
        int length=list.size();
        dataList.addAll(list);
        mCurrentfocus=currentfocus;
        notifyItemRangeChanged(start,length);//处理焦点的错乱
    }

5.下按与上按的时候要伴随着recyclerview的滑动

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

        boolean result = super.dispatchKeyEvent(event);
        View focusView = this.getFocusedChild();

        currentfocus=getChildPosition(focusView);//把当前的聚焦对象的位置记录下来


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

                //Log.i(TAG, "遥控器向上按");
               // View UPView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_UP);

              //

                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_DPAD_LEFT:
                        View leftView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
                        View rightViews = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);

                        //左右两边都是空说明此事焦点在头部的linearlayout
                        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();
                            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) {
                                this.smoothScrollBy(0, -dy);
                                upView.requestFocus();

                               /* View upViews = FocusFinder.getInstance().findNextFocus(this, upView, View.FOCUS_UP);
                                View rViews = FocusFinder.getInstance().findNextFocus(this, upView, View.FOCUS_RIGHT);
                                View lViews = FocusFinder.getInstance().findNextFocus(this, upView, View.FOCUS_LEFT);

                                if (upViews==null&&rViews==null&&lViews==null){
                                    Log.i("FFDDD::","导航");
                                    upView.requestFocus();
                                   smoothScrollToPosition(0);
                                }else{
                                    Log.i("FFDDD::","不是导航");
                                    Log.i(TAG, "upView is null:" + (upView == null)+dy);
                                    this.smoothScrollBy(0, -dy);
                                    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;
    }

还有就是细节问题了,间距之类的,这个就不谈了

附件:

自定义recyclerview

package com.familybox.ui.family.widget.thrid_party;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
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 android.widget.LinearLayout;

import com.familybox.R;
import com.familybox.ui.demo.PersonalityGrowthAdapter;


/**
 * Created by hcy.
 */
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;



    //记录当前的focus的位置

    private int  currentfocus=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(int currentfocus);
    }


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

    protected 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(currentfocus);
                }
            }
        }
        super.onScrollStateChanged(state);
    }

    /**
     * 子类如果要重写dispatchKeyEvent的话请调用个
     * @param event
     * @return
     */
    protected boolean customDispatchKeyEvent(KeyEvent event){
        return super.dispatchKeyEvent(event);
    }

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

        boolean result = super.dispatchKeyEvent(event);
        View focusView = this.getFocusedChild();

        currentfocus=getChildPosition(focusView);//把当前的聚焦对象的位置记录下来


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

                //Log.i(TAG, "遥控器向上按");
               // View UPView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_UP);

              //

                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_DPAD_LEFT:
                        View leftView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
                        View rightViews = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);

                        //左右两边都是空说明此事焦点在头部的linearlayout
                        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();
                            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) {
                                this.smoothScrollBy(0, -dy);
                                upView.requestFocus();

                               /* View upViews = FocusFinder.getInstance().findNextFocus(this, upView, View.FOCUS_UP);
                                View rViews = FocusFinder.getInstance().findNextFocus(this, upView, View.FOCUS_RIGHT);
                                View lViews = FocusFinder.getInstance().findNextFocus(this, upView, View.FOCUS_LEFT);

                                if (upViews==null&&rViews==null&&lViews==null){
                                    Log.i("FFDDD::","导航");
                                    upView.requestFocus();
                                   smoothScrollToPosition(0);
                                }else{
                                    Log.i("FFDDD::","不是导航");
                                    Log.i(TAG, "upView is null:" + (upView == null)+dy);
                                    this.smoothScrollBy(0, -dy);
                                    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;
    }

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


}

activity

package com.familybox.ui.baby.personality_growth;

import android.support.v7.widget.GridLayoutManager;
import android.widget.Toast;

import com.familybox.R;
import com.familybox.ui.BaseActivity;
import com.familybox.ui.demo.PGRecyclerViewItemDecoration;
import com.familybox.ui.demo.PersonalityGrowthAdapter;
import com.familybox.ui.demo.TestData01;
import com.familybox.ui.family.widget.thrid_party.TvRecyclerView;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;

/**
 * Created by hcy on 2018/4/11.
 * 个性成长
 * func:家长订阅的取消订阅的功能
 *
 */
public class PersonalityGrowthActivity extends BaseActivity{
    @BindView(R.id.rv_main)
    TvRecyclerView rv_main;
    GridLayoutManager gridLayoutManager;
    private static final String TAG="PGActivity:::";
    private PersonalityGrowthAdapter adapter;
    @Override
    protected int setLayoutId() {
        return R.layout.activty_personality_growth;
    }

    @Override
    protected void init() {
        initUI();
        initData();
    }

    @Override
    protected void requestInitFocus() {

    }


    private void initData() {
        //数据模拟


        List<TestData01> list=new ArrayList<TestData01>();

        list.add(new TestData01(3,false,"","",false,"",""));//头部
        list.add(new TestData01(0,false,"《我上幼儿园啦》主题节目推荐","",false,"",""));


        list.add(new TestData01(1,false,"运营标题X","",false,"《我上幼儿园啦》主题节目推荐",""));
        list.add(new TestData01(1,false,"运营标题2","",false,"《我上幼儿园啦》主题节目推荐",""));
        list.add(new TestData01(1,false,"运营标题3","",false,"《我上幼儿园啦》主题节目推荐",""));
        list.add(new TestData01(1,false,"运营标题5","",false,"《我上幼儿园啦》主题节目推荐",""));
        list.add(new TestData01(1,false,"运营标题5","",false,"《我上幼儿园啦》主题节目推荐",""));
        list.add(new TestData01(1,false,"运营标题1","",false,"《我上幼儿园啦》主题节目推荐",""));
        list.add(new TestData01(1,false,"运营标题2","",false,"《我上幼儿园啦》主题节目推荐",""));
        list.add(new TestData01(1,false,"运营标题3","",false,"《我上幼儿园啦》主题节目推荐",""));
        list.add(new TestData01(1,false,"运营标题5","",false,"《我上幼儿园啦》主题节目推荐",""));
        list.add(new TestData01(1,false,"运营标题5","",false,"《我上幼儿园啦》主题节目推荐",""));
        list.add(new TestData01(1,false,"运营标题5","",true,"","《我上幼儿园啦》主题节目推荐"));




       /* list.add(new TestData01(0,false,"《奇妙的我》主题节目推荐","",false));
        list.add(new TestData01(1,false,"运营标题1","",false));
        list.add(new TestData01(1,false,"运营标题2","",false));
        list.add(new TestData01(1,false,"运营标题3","",false));
        list.add(new TestData01(1,false,"运营标题5","",false));
        list.add(new TestData01(1,false,"运营标题5","",false));
        list.add(new TestData01(1,false,"运营标题1","",false));
        list.add(new TestData01(1,false,"运营标题2","",false));
        list.add(new TestData01(1,false,"运营标题3","",false));
        list.add(new TestData01(1,false,"运营标题5","",false));
        list.add(new TestData01(1,false,"运营标题5","",false));



        list.add(new TestData01(0,false,"家长订阅关于《小气》的节目推荐","",false));
        list.add(new TestData01(1,false,"运营标题1","",false));
        list.add(new TestData01(1,false,"运营标题2","",false));
        list.add(new TestData01(1,false,"运营标题3","",false));
        list.add(new TestData01(1,false,"运营标题5","",false));
        list.add(new TestData01(1,false,"运营标题5","",false));
        list.add(new TestData01(1,false,"运营标题1","",false));
        list.add(new TestData01(1,false,"运营标题2","",false));
        list.add(new TestData01(1,false,"运营标题3","",false));
        list.add(new TestData01(1,false,"运营标题5","",false));
        list.add(new TestData01(1,false,"运营标题5","",false));*/




        if (adapter!=null){
            adapter.setData(list);
        }

        adapter.setRemoteControlListener(new PersonalityGrowthAdapter.onRemoteControlListener() {
            @Override
            public void makeSure(int navPosition, int currentItemFocusPostion) {
                Toast.makeText(PersonalityGrowthActivity.this,"当前遥控器选择了"+navPosition+"?"+currentItemFocusPostion,Toast.LENGTH_SHORT).show();
            }

            @Override
            public void changeNav(int navPosition) {
                Toast.makeText(PersonalityGrowthActivity.this,"当行切换了"+navPosition,Toast.LENGTH_SHORT).show();

            }

            @Override
            public void cancelSubscribe(String title) {

                if (adapter!=null){
                    adapter.cancelSubscribe(title);
                }
                Toast.makeText(PersonalityGrowthActivity.this,"取消订阅按钮启动",Toast.LENGTH_SHORT).show();
            }
        });

        rv_main.setOnLoadMoreListener(new TvRecyclerView.OnLoadMoreListener() {
            @Override
            public void onLoadMore(int currentfocus) {
                Toast.makeText(PersonalityGrowthActivity.this,"加载更多"+currentfocus,Toast.LENGTH_SHORT).show();
                List<TestData01> list=new ArrayList<TestData01>();
                list.add(new TestData01(0,false,"新一页数据","",false,"",""));
                list.add(new TestData01(1,false,"运营标题1","",false,"新一页数据",""));
                list.add(new TestData01(1,false,"运营标题2","",false,"新一页数据",""));
                list.add(new TestData01(1,false,"运营标题3","",false,"新一页数据",""));
                list.add(new TestData01(1,false,"运营标题5","",false,"新一页数据",""));
                list.add(new TestData01(1,false,"运营标题5","",false,"新一页数据",""));
                list.add(new TestData01(1,false,"运营标题1","",false,"新一页数据",""));
                list.add(new TestData01(1,false,"运营标题2","",false,"新一页数据",""));
                list.add(new TestData01(1,false,"运营标题3","",false,"新一页数据",""));
                list.add(new TestData01(1,false,"运营标题5","",false,"新一页数据",""));
                list.add(new TestData01(1,false,"运营标题5","",false,"新一页数据",""));
                if (adapter!=null){
                    adapter.loadmore(list,currentfocus);
                }
            }
        });

    }

    private void initUI() {
        adapter=new PersonalityGrowthAdapter(PersonalityGrowthActivity.this);
        gridLayoutManager = new GridLayoutManager(PersonalityGrowthActivity.this, 5);
        rv_main.setLayoutManager(gridLayoutManager);
        rv_main.setHasFixedSize(true);
        //设置item间距
        rv_main.addItemDecoration(new PGRecyclerViewItemDecoration(PersonalityGrowthActivity.this));
        rv_main.setAdapter(adapter);
        adapter.onAttachedToRecyclerView(rv_main);

       /* adapter.setListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean isfous) {
                if (isfous){
                    if (adapter!=null){
                        if (view.getTag()!=null){
                            view.findViewById(R.id.rl_select).setVisibility(View.VISIBLE);
                            view.findViewById(R.id.rl_unselect).setVisibility(View.GONE);
                        }
                    }
                    enlargeAnim(view);
                }else{
                    reduceAnim(view);
                    if (adapter!=null){
                        if (view.getTag()!=null){
                            view.findViewById(R.id.rl_unselect).setVisibility(View.VISIBLE);
                            view.findViewById(R.id.rl_select).setVisibility(View.GONE);
                        }
                    }
                }
            }
        });*/

    }



}

适配器

package com.familybox.ui.demo;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.CardView;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextPaint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.familybox.R;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by hcy on 2018/4/11.
 * func:个性成长详情页的适配器
 */

public class PersonalityGrowthAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    //定义数据
    private List<TestData01> dataList;
    //上下文
    private Context context;

    private LayoutInflater inflater;
    //recyclerview的分类
    //头部
    private static final int TYPE_HEAD = 1;
    //标题
    private static final int TYPE_TITLE = 2;
    //内容
    private static final int TYPE_CONTENT = 3;


    private View.OnFocusChangeListener listener;//焦点变化监听

    public void setListener(View.OnFocusChangeListener listener) {
        this.listener = listener;
    }




    public void cancelSubscribe(String title) {

        List<TestData01>  readyToDeleteList=new ArrayList<TestData01>();

        for (int i = 0; i < dataList.size(); i++) {
            if (dataList.get(i).getDelete_title().equals(title)||dataList.get(i).getConnect_title().equals(title)||dataList.get(i).getText().equals(title)){
                readyToDeleteList.add(dataList.get(i));
            }
        }
        dataList.removeAll(readyToDeleteList);
        notifyDataSetChanged();

    }


    public  interface   onRemoteControlListener{
        //遥控器确定操作
        void makeSure(int navPosition,int currentItemFocusPostion);
        //切换导航
        void changeNav(int navPosition);
        //取消订阅
        void cancelSubscribe(String title);
    }


    private onRemoteControlListener  remoteControlListener;

    public void setRemoteControlListener(onRemoteControlListener remoteControlListener) {
        this.remoteControlListener = remoteControlListener;
    }

    @Override
    public int getItemViewType(int position) {
        TestData01 testData01 = dataList.get(position);
        if (testData01.getType() == 0) {
            return TYPE_TITLE;
        } else if (testData01.getType() == 1) {
            return TYPE_CONTENT;
        } else if (testData01.getType() == 3) {
            return TYPE_HEAD;
        }





        return super.getItemViewType(position);
    }

    private LinearLayout ll_tbar;
    private int currentNavIndex = 0;

    //用于导航栏左右逻辑
    public void func(int dir) {
            //当前是否在导航栏上,正数向右,负数向左
            Toast.makeText(context,"当前在导航栏上", Toast.LENGTH_SHORT).show();
            if (dir > 0) {
                if (currentNavIndex >= 0 && currentNavIndex < 2) {
                    currentNavIndex++;
                }
                for (int i = 0; i < ll_tbar.getChildCount(); i++) {
                    ll_tbar.getChildAt(i).setSelected(false);
                    TextPaint tp = ((TextView) ll_tbar.getChildAt(i)).getPaint();
                    tp.setFakeBoldText(false);
                }
                ll_tbar.getChildAt(currentNavIndex).setSelected(true);

                TextPaint tp = ((TextView) ll_tbar.getChildAt(currentNavIndex)).getPaint();
                tp.setFakeBoldText(true);
            }

            if (dir < 0) {
                if (currentNavIndex > 0 && currentNavIndex <= 2) {
                    currentNavIndex--;
                }
                for (int i = 0; i < ll_tbar.getChildCount(); i++) {
                    ll_tbar.getChildAt(i).setSelected(false);
                    TextPaint tp = ((TextView) ll_tbar.getChildAt(i)).getPaint();
                    tp.setFakeBoldText(false);
                }
                ll_tbar.getChildAt(currentNavIndex).setSelected(true);
                TextPaint tp = ((TextView) ll_tbar.getChildAt(currentNavIndex)).getPaint();
                tp.setFakeBoldText(true);
            }


            if (remoteControlListener!=null){
                remoteControlListener.changeNav(currentNavIndex);
            }

    }


    public PersonalityGrowthAdapter(Context context) {
        dataList = new ArrayList<TestData01>();
        this.context = context;
        inflater = LayoutInflater.from(context);

    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_HEAD) {
            return new PGView_HeadHolder(inflater.inflate(R.layout.item_personality_growth_adapter_head, parent, false));
        } else if (viewType == TYPE_TITLE) {
            return new PGView_TitleHolder(inflater.inflate(R.layout.item_personality_growth_adapter_title, parent, false));
        } else if (viewType == TYPE_CONTENT) {
            return new PGView_ContentHolder(inflater.inflate(R.layout.item_personality_growth_adapter_content, parent, false));
        }
        return null;
    }



    @Override
    public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
           /*
        * else if (position>12&&position<23){

                        //((GridLayoutManager)recyclerView.getLayoutManager()).setSpanCount(4);
                        return 4;
                    }*/
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
            gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    if (position==0||getItemViewType(position)==2){
                        return 5;
                    } else {
                        return 1;
                    }
                }
            });
        }
    }


    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
        final TestData01 testData01 = dataList.get(position);

      /*  if (position == mCurrentfocus) {
            holder.itemView.requestFocus();
        }*/

        switch (getItemViewType(position)){
            case TYPE_HEAD:

                ((PGView_HeadHolder)holder).ll_tool.setTag(position);
                break;
            case TYPE_TITLE:
                ((PGView_TitleHolder)holder).itemView.setFocusable(false);
                ((PGView_TitleHolder)holder).tv_title.setText(testData01.getText()+"?"+position);
                break;
            case TYPE_CONTENT:
                holder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                    @Override
                    public void onFocusChange(View view, boolean isfous) {
                        if (isfous){
                            mCurrentfocus=position;
                            Drawable drawable=context.getResources().getDrawable(R.drawable.border_color);
                            ((PGView_ContentHolder)holder).cv_01.setForeground(drawable);

                            if (!testData01.isFunc()){
                                ((PGView_ContentHolder)holder).tv_content_s.setVisibility(View.VISIBLE);
                                ((PGView_ContentHolder)holder).tv_content.setVisibility(View.GONE);
                            }

                            enlargeAnim(view);
                        }else{
                            ((PGView_ContentHolder)holder).cv_01.setForeground(null);
                            if (!testData01.isFunc()){
                                ((PGView_ContentHolder)holder).tv_content_s.setVisibility(View.GONE);
                                ((PGView_ContentHolder)holder).tv_content.setVisibility(View.VISIBLE);
                            }
                            reduceAnim(view);
                        }

                    }
                });










                if (testData01.isFunc()){
                    //取消订阅按钮
                    //((PGView_ContentHolder)holder).cv_01.setForeground(drawable);
                    ((PGView_ContentHolder)holder).tv_content_s.setVisibility(View.GONE);
                    ((PGView_ContentHolder)holder).tv_content.setVisibility(View.GONE);

                    if (remoteControlListener!=null){
                        holder.itemView.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                //遥控器取消订阅处理
                                remoteControlListener.cancelSubscribe(testData01.getDelete_title());
                            }
                        });
                    }


                }else{
                    ((PGView_ContentHolder)holder).tv_content_s.setVisibility(View.GONE);
                    ((PGView_ContentHolder)holder).tv_content.setVisibility(View.VISIBLE);
                    if (remoteControlListener!=null){
                        holder.itemView.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                //遥控器确定按钮
                                remoteControlListener.makeSure(currentNavIndex,mCurrentfocus);
                            }
                        });
                    }
                }







                if (((PGView_ContentHolder)holder).itemView.getTag()==null){
                    ((PGView_ContentHolder)holder).itemView.setTag(position);
                }

                ((PGView_ContentHolder)holder).tv_content_s.setText(testData01.getText()+"/"+position);
                ((PGView_ContentHolder)holder).tv_content.setText(testData01.getText()+"/"+position);
                break;
        }
    }

    private int mCurrentfocus=0;

    @Override
    public int getItemCount() {
        return dataList.size();  //数据源(头部布局是死的)
    }
    public void setData(List<TestData01> list) {
        dataList.addAll(list);
        notifyDataSetChanged();
    }

    //加载新一页的数据

    public void loadmore(List<TestData01> list,int currentfocus) {
        int start=dataList.size()-1;
        int length=list.size();
        dataList.addAll(list);
        mCurrentfocus=currentfocus;
        notifyItemRangeChanged(start,length);//处理焦点的错乱
    }

    public class PGView_HeadHolder extends RecyclerView.ViewHolder{
        private LinearLayout ll_tool;
        public PGView_HeadHolder( View itemView) {
            super(itemView);
            ll_tool=itemView.findViewById(R.id.ll_tool);
            ll_tool.setTag(-1);
            ll_tool.getChildAt(0).setSelected(true);
            //字体加粗
            TextPaint tp = ((TextView) ll_tool.getChildAt(0)).getPaint();
            tp.setFakeBoldText(true);
            currentNavIndex=0;
            ll_tbar=ll_tool;
        }
    }
    public class PGView_TitleHolder extends RecyclerView.ViewHolder{
        private TextView tv_title;
        public PGView_TitleHolder( View itemView) {
            super(itemView);
            itemView.setFocusable(false);
            tv_title=(TextView)itemView.findViewById(R.id.tv_title);
        }
    }
    public class PGView_ContentHolder extends RecyclerView.ViewHolder{
        private TextView tv_content;
        //private RelativeLayout rl_unselect;
       // private RelativeLayout rl_select;
        private TextView tv_content_s;
        private CardView cv_01;
        public PGView_ContentHolder(View itemView) {
            super(itemView);
            itemView.setFocusable(true);
            tv_content=(TextView) itemView.findViewById(R.id.tv_content);
           // rl_unselect=itemView.findViewById(R.id.rl_unselect);
           // rl_select=itemView.findViewById(R.id.rl_select);
            tv_content_s= itemView.findViewById(R.id.tv_content_s);
            cv_01=itemView.findViewById(R.id.cv_01);
        }
    }


    //获取焦点view实现放大效果
    private void enlargeAnim(View view) {
        if (Build.VERSION.SDK_INT >= 21) {//android 5.0以上
            ViewCompat.animate(view).scaleX(1.13f).scaleY(1.13f).translationZ(1.13f).start();
        } else {
            Animation a = android.view.animation.AnimationUtils.loadAnimation(view.getContext(), R.anim.uikit_enlarge);
            a.setFillAfter(true);
            view.clearAnimation();
            view.setAnimation(a);
            view.bringToFront();
            a.start();
        }
    }

    //失去焦点view实现缩小效果
    private void reduceAnim(View view) {
        if (Build.VERSION.SDK_INT >= 21) {//android 5.0以上
            ViewCompat.animate(view).scaleX(1.0f).scaleY(1.0f).translationZ(1.0f).start();
        } else {
            Animation a = android.view.animation.AnimationUtils.loadAnimation(view.getContext(), R.anim.uikit_reduce);
            a.setFillAfter(true);
            view.clearAnimation();
            view.startAnimation(a);
            a.start();
        }
    }



    /* holder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                        @Override
                        public void onFocusChange(View view, boolean isfous) {
                            if (isfous){
                                mCurrentfocus=position;
                                Drawable drawable=context.getResources().getDrawable(R.drawable.border_color);
                                ((PGView_ContentHolder)holder).cv_01.setForeground(drawable);
                                ((PGView_ContentHolder)holder).tv_content_s.setVisibility(View.VISIBLE);
                                ((PGView_ContentHolder)holder).tv_content.setVisibility(View.GONE);
                                enlargeAnim(view);
                            }else{
                                ((PGView_ContentHolder)holder).cv_01.setForeground(null);
                                ((PGView_ContentHolder)holder).tv_content_s.setVisibility(View.GONE);
                                ((PGView_ContentHolder)holder).tv_content.setVisibility(View.VISIBLE);
                                reduceAnim(view);

                            }

                        }
                    });*/
}

间距

package com.familybox.ui.demo;

import android.content.Context;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.familybox.utils.PxtransformUtil;

/**
 * Created by hcy on 2018/4/12.
 */

public class PGRecyclerViewItemDecoration extends RecyclerView.ItemDecoration{


    /**
     * Retrieve any offsets for the given item. Each field of <code>outRect</code> specifies
     * the number of pixels that the item view should be inset by, similar to padding or margin.
     * The default implementation sets the bounds of outRect to 0 and returns.
     * <p>
     * <p>
     * If this ItemDecoration does not affect the positioning of item views, it should set
     * all four fields of <code>outRect</code> (left, top, right, bottom) to zero
     * before returning.
     * <p>
     * <p>
     * If you need to access Adapter for additional data, you can call
     * {@link RecyclerView#getChildAdapterPosition(View)} to get the adapter position of the
     * View.
     *
     * @param outRect Rect to receive the output.
     * @param view    The child view to decorate
     * @param parent  RecyclerView this ItemDecoration is decorating
     * @param state   The current state of RecyclerView.
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);

     if ( parent.getChildAdapterPosition(view)==1&&parent.getAdapter().getItemViewType(parent.getChildAdapterPosition(view))==2){
         //标题 第一行
         outRect.top=PxtransformUtil.dip2px(context,32);
     }else if( parent.getChildAdapterPosition(view)!=1&&parent.getAdapter().getItemViewType(parent.getChildAdapterPosition(view))==2){
         //标题 第二行
         outRect.top=PxtransformUtil.dip2px(context,21);

     }else if (parent.getAdapter().getItemViewType(parent.getChildAdapterPosition(view))==3){
         //内容
         outRect.top=PxtransformUtil.dip2px(context,13);
         outRect.right = PxtransformUtil.dip2px(context,23);
         outRect.bottom= PxtransformUtil.dip2px(context,11);
     }






    }

    private Context context;

    public PGRecyclerViewItemDecoration(Context context) {
        this.context=context;
    }

}

主页+头部+标题+内容布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:clipChildren="false"
    android:orientation="vertical"
    android:focusable="false"
    android:background="#75C11B"
    tools:context="com.familybox.ui.family.parents_class.ParentsSchoolActivity">


        <com.familybox.ui.family.widget.thrid_party.TvRecyclerView
            android:id="@+id/rv_main"
            android:focusable="false"
            android:clipToPadding="false"
            android:paddingLeft="60dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
        </com.familybox.ui.family.widget.thrid_party.TvRecyclerView>




        <LinearLayout
            android:orientation="vertical"
            android:layout_centerHorizontal="true"
            android:layout_below="@+id/rv_main"
            android:layout_marginTop="160dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:text="未收到课程相关推荐"
                    android:textSize="40sp"
                    android:textColor="#3A6806 "/>
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="24.67dp"
                    android:layout_gravity="center_horizontal"
                    android:text="可能是老师尚未制定本月教学内容"
                    android:textSize="32sp"
                    android:textColor="#3A6806 "/>

        </LinearLayout>


</RelativeLayout>
















<!--
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="42px"
android:layout_marginTop="48px"
android:focusable="true"
    android:background="#f00"
android:gravity="center">


    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_main"
        android:visibility="gone"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>
</FrameLayout>-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="213.33dp"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:layout_marginLeft="-60dp"
    android:layout_marginRight="-60dp"
    android:paddingLeft="60dp"
    android:focusable="true"
    android:id="@+id/rl_tool"
    android:background="@mipmap/gexing_top"
    tools:context="com.familybox.ui.family.parents_class.ParentsSchoolActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="个性成长"
            android:textStyle="bold"
            android:focusable="false"
            android:id="@+id/tv_tag"
            android:layout_marginTop="40dp"
            android:gravity="center"
            android:textColor="#ffffff"
            android:textSize="@dimen/font_size_48"/>

        <LinearLayout
            android:orientation="horizontal"
            android:layout_marginTop="101.33dp"
            android:gravity="center"
            android:focusable="false"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="24sp"
                    android:layout_marginTop="3dp"
                    android:textColor="#CDE7B0"
                    android:gravity="center"
                    android:text="@string/gexingchengzhang_tag_01"/>
                <ImageView
                    android:layout_width="33.33dp"
                    android:layout_height="33.33dp"
                    android:scaleType="fitCenter"
                    android:src="@mipmap/gxcz_ico"
                    android:layout_gravity="center_vertical"/>
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="24sp"
                    android:gravity="center"
                    android:layout_marginTop="3dp"
                    android:textColor="#CDE7B0"
                    android:text="@string/gexingchengzhang_tag_02"/>

        </LinearLayout>


        <LinearLayout
            android:id="@+id/ll_tool"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="144dp"
            android:focusable="true"
            android:orientation="horizontal">
                <TextView
                    android:id="@+id/tv_01"
                    android:layout_width="213.33dp"
                    android:layout_height="66.67dp"
                    android:text="课程相关"
                    android:nextFocusRight="@+id/tv_02"
                    android:focusable="true"
                    android:background="@drawable/sel_focus_gp2"
                    android:gravity="center"
                    android:textColor="#ffffff"
                    android:textSize="@dimen/font_size_24"/>

                <TextView
                    android:id="@+id/tv_02"
                    android:focusable="true"
                    android:layout_width="213.33dp"
                    android:layout_height="66.67dp"
                    android:text="测评推荐"
                    android:nextFocusRight="@+id/tv_03"
                    android:background="@drawable/sel_focus_gp2"
                    android:gravity="center"
                    android:textColor="#ffffff"
                    android:textSize="@dimen/font_size_24"/>

                <TextView
                    android:focusable="true"
                    android:id="@+id/tv_03"
                    android:layout_width="213.33dp"
                    android:layout_height="66.67dp"
                    android:text="家长订阅"
                    android:background="@drawable/sel_focus_gp2"
                    android:gravity="center"
                    android:textColor="#ffffff"
                    android:textSize="@dimen/font_size_24"/>

        </LinearLayout>




</RelativeLayout>

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clipToPadding="false"
    android:background="#75C11B"
    android:layout_marginLeft="-60dp"
    android:layout_marginRight="-60dp"
    android:paddingLeft="60dp"
    android:clipChildren="false"
    android:orientation="vertical"
    android:focusable="false"
    tools:context="com.familybox.ui.family.parents_class.ParentsSchoolActivity">
    <TextView
        android:id="@+id/tv_title"
        android:layout_marginTop="32dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textStyle="bold"
        android:textSize="26.67sp"
        android:clickable="false"
        android:focusableInTouchMode="false"
        android:focusable="false"
        android:textColor="@android:color/white"
        />
</RelativeLayout>

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_gravity="center"
    android:id="@+id/fl_content"
    android:clipChildren="false"
    android:clipToPadding="false"
    android:focusable="true"
    android:gravity="center">

      <RelativeLayout
          android:id="@+id/rl_select"
          android:layout_centerHorizontal="true"
          android:layout_width="150dp"
          android:layout_height="120dp">
          <android.support.v7.widget.CardView
              android:id="@+id/cv_01"
              app:cardPreventCornerOverlap="false"
              android:padding="3dp"
              android:clipChildren="false"
              android:layout_width="142dp"
              android:layout_height="90dp"
              app:cardCornerRadius="3dp">

              <RelativeLayout
                  android:id="@+id/rl_img"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:background="#333"
                  android:clipChildren="false"></RelativeLayout>

          </android.support.v7.widget.CardView>

          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_below="@+id/cv_01"
              android:layout_marginTop="5.33dp"
              android:layout_centerHorizontal="true"
              android:textSize="18.67sp"
              android:gravity="center_vertical"
              android:textColor="#ffffff"
              android:id="@+id/tv_content"
              android:text="dddddddddddd"/>

         <!-- <TextView
              android:layout_width="120.33dp"
              android:layout_height="22.67dp"
              android:background="#ffffff"
              android:layout_marginTop="80.6dp"
              android:layout_centerHorizontal="true"
              android:textSize="18.67sp"
              android:textColor="#000000"
              android:visibility="gone"
              android:id="@+id/tv_content_s"
              android:text="dd"/>-->
      </RelativeLayout>



<!--    <RelativeLayout
        android:id="@+id/rl_select"
        android:layout_width="150dp"
        android:layout_height="120dp"
        android:clipChildren="false"
        android:visibility="gone">

            <RelativeLayout
                android:padding="3dp"
                android:id="@+id/rl_img2"
                android:background="@drawable/border_color"
                android:layout_width="150dp"
                android:layout_height="100dp">
                <ImageView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:background="#333"/>
            </RelativeLayout>





    </RelativeLayout>-->

    <TextView
        android:layout_width="120.33dp"
        android:layout_height="22.67dp"
        android:background="#ffffff"
        android:visibility="gone"
        android:layout_marginTop="78.6dp"
        android:layout_centerHorizontal="true"
        android:textSize="18.67sp"
        android:textColor="#000000"
        android:id="@+id/tv_content_s"
        android:text="dd"/>













</RelativeLayout>

模拟的数据:

package com.familybox.ui.demo;

import java.util.List;

/**
 * Created by coderShan on 2018/4/13.
 */

public class TestData01 {
    private int type;//判断是标题0还是内容1还是头部3
    private  boolean isSelect;//判断当前是否被选中
    private String text;//显示的文本
    private String pic;//图片的连接
    private boolean func;//用于标记取消订阅按钮
    private String connect_title;//如果是内容,存放内容的标题
    private String delete_title;//如果是取消订阅的按钮,存放的要取消订阅的标题


    public TestData01(int type, boolean isSelect, String text, String pic, boolean func, String connect_title, String delete_title) {
        this.type = type;
        this.isSelect = isSelect;
        this.text = text;
        this.pic = pic;
        this.func = func;
        this.connect_title = connect_title;
        this.delete_title = delete_title;
    }


    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public boolean isSelect() {
        return isSelect;
    }

    public void setSelect(boolean select) {
        isSelect = select;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    public boolean isFunc() {
        return func;
    }

    public void setFunc(boolean func) {
        this.func = func;
    }

    public String getConnect_title() {
        return connect_title;
    }

    public void setConnect_title(String connect_title) {
        this.connect_title = connect_title;
    }

    public String getDelete_title() {
        return delete_title;
    }

    public void setDelete_title(String delete_title) {
        this.delete_title = delete_title;
    }
}

总结

        电视开发的难点其实不多,只要心中有焦点在哪个位置的逼数,大概就能驰骋绝大部分tv项目了,因为tv的UI其实很固定。

其实android的难点也不多,除了贼复杂的自定义view(算法)以及不用自己去编译什么so库(其他语言),android应该没什

么难点存在了。面试也有好多问react native混合开发的,我除了对热修复一知半解外也是一脸懵逼,这也是我今年的研究方向。

















阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Coder_Hcy/article/details/79964937
个人分类: Android
上一篇写在离职前
下一篇Android基于DiskLruCache做一个数据对象的缓存工具
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭