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

Android TV端的(RecyclerView)水平滚动焦点错乱问题的更多相关文章

  1. Android为TV端助力:RecyclerView更新数据时焦点丢失

    1.adapter的setHasStableIds设置成true 2.重写adapter的getItemId方法 @Override public long getItemId(int positio ...

  2. Android RecyclerView 水平滚动+自动循环轮播

    主要处理的地方: 1.RecyclerView中Adapter的item个人可以无限轮询. 2.RecyclerView自动滑动 3.手指按下时滑动停止,手指抬起后继续自动滑动 public clas ...

  3. Android为TV端助力(转载)

    作者地址http://www.jianshu.com/u/63915ef020e2 针对Android Tv的自定义RecyclerView 作者 wenju_song 关注 2016.12.09 1 ...

  4. 用RecyclerView实现水平滚动和网格视图

    建立RecyclerViewActivity.java文件 1 public class RecyclerViewActivity extends AppCompatActivity { 2 priv ...

  5. Android为TV端助力 转载:RecyclerView分页加载

    package com.android.ryane.pulltoloaddata_recyclerview; import android.os.Handler;import android.os.L ...

  6. Android为TV端助力 listview与recyclerview上下联动

    首先是主布局fragment里面的xml文件 <?xml version="1.0" encoding="utf-8"?><RelativeL ...

  7. [转]HorizontalScrollView介绍--支持水平滚动的android布局容器

    类概述 用 于布局的容器,可以放置让用户使用滚动条查看的视图层次结构,允许视图结构比手机的屏幕大.HorizontalScrollView是一种 FrameLayout(框架布局),其子项被滚动查看时 ...

  8. Android中如何实现多行、水平滚动的分页的Gridview?

    功能要求: (1)比如每页显示2X2,总共2XN,每个item显示图片+文字(点击有链接). 如果单行水平滚动,可以用Horizontalscrollview实现. 如果是多行水平滚动,则结合Grid ...

  9. Android为TV端助力 转载自jguangyou的博客,XML基本属性大全

    android:layout_width 指定组件布局宽度 android:layout_height 指定组件布局高度 android:alpha 设置组件透明度 android:backgroun ...

随机推荐

  1. 最快效率求出乱序数组中第k小的数

    题目:以尽量高的效率求出一个乱序数组中按数值顺序的第k 的元素值 思路:这里很容易想到直接排序然后顺序查找,可以使用效率较高的快排,但是它的时间复杂度是O(nlgn),我们这里可以用一种简便的方法,不 ...

  2. SDL 开发实战(五): SDL 纹理渲染

    本文我们讲一下如何使用SDL_Texture将视频纹理渲染出来. 1. SDL 视频渲染相关对象 SDL 视频渲染主要涉及到四个对象:SDL_Window.SDL_Render.SDL_Texture ...

  3. [Swift]LeetCode60. 第k个排列 | Permutation Sequence

    The set [1,2,3,...,n] contains a total of n! unique permutations. By listing and labeling all of the ...

  4. [Swift]LeetCode485. 最大连续1的个数 | Max Consecutive Ones

    Given a binary array, find the maximum number of consecutive 1s in this array. Example 1: Input: [1, ...

  5. 洛谷P1036选数(素数+组合数)

    题目链接:https://www.luogu.org/problemnew/show/P1036 主要考两个知识点:判断一个数是否为素数.从n个数中选出m个数的组合 判断一个数是否为素数: 素数一定是 ...

  6. Python爬虫入门项目

    Python是什么 Python是著名的"龟叔"Guido van Rossum在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言. 创始人Guido van Ros ...

  7. HashMap? ConcurrentHashMap? 相信看完这篇没人能难住你!

    前言 Map 这样的 Key Value 在软件开发中是非常经典的结构,常用于在内存中存放数据. 本篇主要想讨论 ConcurrentHashMap 这样一个并发容器,在正式开始之前我觉得有必要谈谈 ...

  8. Redis哨兵

    Redis Sentinel Redis哨兵为Redis提供高可用.这就意味着你用哨兵可以创建一个Redis部署,在没有人为干预的情况下抵抗某些失败.(PS:自动故障转移) Redis哨兵还提供其他的 ...

  9. synchronized底层实现学习

    上文我们总结了 synchronized 关键字的基本用法以及作用,并未涉及 synchronized 底层是如何实现的,所谓刨根问底,本文我们就开始 synchronized 原理的探索之旅吧(*& ...

  10. centos系统安装第三方源EPEL

    epel没安装呗 相当于扩展型软件仓库,EPEL (Extra Packages for Enterprise Linux,企业版Linux的额外软件包) 是Fedora小组维护的一个软件仓库项目,为 ...