在新公司干了快一个月了,感觉对tv开发有了一定的经验。主要对recyclerview有了一定的认识。
1.调试盒子。
盒子的ip地址与电脑的ip地址保持一致
adb connect ip地址
adb uninstall 包名
2.焦点控制需要的一些方法。
requestFocus() //获得焦点
setNextFocusXXX//就是让下一个按上下左右键聚焦的view
3.Recyclerview的用法
TV布局:1.如果是简单的页面(死界面),建议直接就用textview直接布局,焦点逻辑用setNextFocusXXX就可以了。像这种:
动态界面:较复杂的那种,就需要自定义recyclerview,像:
主要是一个recyclerview来布局这个页面,用到recyclerview的分类,gridlayout的“合并单元格”的知识点。这个已经总结过了。下面开始总结我开发中问道的头疼的问题。
.滑动焦点错乱了
.当页面遥控器控制不了滑动了怎么办
.如何判定分页以及分页加载刷新后焦点逻辑
.删除后我的焦点该如何控制
.导航栏下来如何保存导航的状态以及第一个聚焦的始终是第一个item。
滑动焦点错乱了
.当遥控一直按住的时候,突然不知道焦点飞哪去了。
1.看看position是否在乱跳,我的主要是position乱跳导致焦点乱跳,然后我把布局都用LinearLayout限定宽高解决的
2.当遥控器按到底部的时候,如果底部还有数据但并没有显示在屏幕上,可能也会导致焦点乱跳。所以尽量让底部的界面露出来一些像上图一样。当然也可代码判断当焦点失去的时候,让页面先滑上来一些
3.什么去除recyclerview的动画以及写找不到焦点的处理方法
private void initView() {
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setHasFixedSize(true);
setWillNotDraw(true);
setOverScrollMode(View.OVER_SCROLL_NEVER);
setChildrenDrawingOrderEnabled(true);
setClipChildren(false);
setClipToPadding(false);
setClickable(false);
setFocusable(true);
setFocusableInTouchMode(true);
/**
防止RecyclerView刷新时焦点不错乱bug的步骤如下:
(1)adapter执行setHasStableIds(true)方法
(2)重写getItemId()方法,让每个view都有各自的id
(3)RecyclerView的动画必须去掉
*/
setItemAnimator(null);
}
onSearchFocusfailed方法百度很多,我没用到过
当页面遥控器控制不了滑动了怎么办
当遥控器向下按时没问题的,但向上按的时候滑不上去了比如下图:
此时屏幕上部应该还有数据,但并不显示在屏幕上,此时如果焦点在第一个item上,遥控器向上按就没反应,超出屏幕的部分应该是获取不到焦点的。
解决方案有两种:
1.同事的方案:
package com.familybox.ui.family.widget;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
/**
* 日期:2018/4/4
*
* 作者:xudiwei
*
* 描述:用于TV垂直方向的RecyclerView.
*/
public class ParentRecyclerView extends RecyclerView {
private static final String TAG = "ParentSchoolRecyclerVie";
public ParentRecyclerView(Context context) {
super(context);
init();
}
public ParentRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ParentRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setClipChildren(false);
setClipToPadding(false);
setWillNotDraw(false);
setChildrenDrawingOrderEnabled(true);
}
@Override
public void smoothScrollBy(int dx, int dy) {
if (dy > 0) {
super.smoothScrollBy(dx, dy + 350);
} else if (dy < 0) {
super.smoothScrollBy(dx, dy - 350);
} else {
super.smoothScrollBy(dx, dy);
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.d(TAG, "dispatchKeyEvent:--> "+event.isLongPress());
return super.dispatchKeyEvent(event);
}
private int focusViewIndex;
private int focusId;
/**
* 自定义重绘排序,好让获取焦点的子view最后绘制,这样子view放大时不会被遮住.
*
* @param childCount
* @param i
* @return
*/
@Override
protected int getChildDrawingOrder(int childCount, int i) {
focusViewIndex = indexOfChild(getFocusedChild());
Log.d(TAG, "getChildDrawingOrder:--> Index: " + focusViewIndex);
if (focusViewIndex == -1) {
return i;
}
if (focusViewIndex == i) {
focusId = i;
return childCount - 1;
} else if (i == childCount - 1) {
return focusId;
} else {
return i;
}
}
}
重写了smoothScrollby的方法,让每次recyclerview默认滑动的时候多滑一段距离。就是始终保持顶部会露出一点(有缺陷的,不适用于这个界面,适用于界面比较固定,不会有二级标题的那种
焦点搜索失败同样也处理了
package com.familybox.ui.family.widget;
import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**
* 日期:2018/4/8
*
* 作者:XXX
*
* 描述:处理找不到焦点时的处理方式的GridLayoutManager
*/
public class FocusGridLayoutManager extends GridLayoutManager {
private static final String TAG = "FocusGridLayoutManager";
public FocusGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public FocusGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
}
public FocusGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
}
/* @Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
Log.e("probe", "meet a IOOBE in RecyclerView");
}
}*/
@Override
public View onFocusSearchFailed(View focused, int focusDirection, RecyclerView.Recycler recycler, RecyclerView.State state) {
// ViewParent parent = focused.getParent();
// if (parent instanceof RecyclerView) {
// RecyclerView recyclerView = (RecyclerView) parent;
// if (focusDirection == View.FOCUS_DOWN) {
// recyclerView.smoothScrollBy(0, 1);
// } else if (focusDirection == View.FOCUS_UP) {
// recyclerView.smoothScrollBy(0, -1);
// }
// }
return focused;
// return super.onFocusSearchFailed(focused, focusDirection, recycler, state);
}
}
我的方案就是重写了recyclerview分发事件的方法,当滑动的时候如果焦点得不到了(指的是超出屏幕外,就让它滑出来)缺点也很明显,先滑出来再按一下才聚焦
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.i("VVCCC::", event.getKeyCode() + "");
if (mInterceptLister != null && mInterceptLister.onIntercept(event)) {
return true;
}
if (KeyEvent.KEYCODE_BACK == event.getKeyCode()) {
Log.i("RRRRFF::", "isreturn");
return false;
}
boolean result = super.dispatchKeyEvent(event);
View focusView = this.getFocusedChild();
currentfocus = getChildPosition(focusView);//把当前的聚焦对象的位置记录下来
if (focusView == null) {
Log.d(TAG, "dispatchKeyEvent:--> focusView == null" + currentfocus);
return result;
} else {
int dy = 0;
int dx = 0;
if (getChildCount() > 0) {
View firstView = this.getChildAt(0);
dy = firstView.getHeight();
dx = firstView.getWidth();
}
if (event.getAction() == KeyEvent.ACTION_UP) {
Log.i("FFDDD225::", "");
return true;
} else {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_RIGHT:
View rightView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
View leftViews = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
if (rightView == null && leftViews == null) {
if (getAdapter() != null) {
((PersonalityGrowthAdapter) getAdapter()).func(1);
}
}
Log.i(TAG, "rightView is null:" + (rightView == null));
Log.i(TAG, "leftViews is null:" + (leftViews == null));
if (rightView != null) {
rightView.requestFocus();
return true;
} else {
this.smoothScrollBy(dx, 0);
return true;
}
case KeyEvent.KEYCODE_SETTINGS:
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.ParentsSubscribe();
}
return true;
case KeyEvent.KEYCODE_DPAD_LEFT:
View leftView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
View rightViews = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
if (rightViews == null && leftView == null) {
if (getAdapter() != null) {
((PersonalityGrowthAdapter) getAdapter()).func(-1);
}
}
Log.i(TAG, "leftView is null:" + (leftView == null));
if (leftView != null) {
leftView.requestFocus();
return true;
} else {
this.smoothScrollBy(-dx, 0);
return true;
}
case KeyEvent.KEYCODE_DPAD_DOWN:
//让第一个默认选中
View lView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
View rView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
View downView;
if (lView == null && rView == null) {
//焦点左边的view与焦点右边的view都是空的话,说明此事焦点在头部,
// 说明此时是从导航栏按下来,要让第一个item获得焦点
downView = getChildAt(2);//第一个item就是第二个position的位置
} else {
downView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_DOWN);
}
Log.i(TAG, " downView is null:" + (downView == null));
if (downView != null) {
downView.requestFocus();
} else {
this.smoothScrollBy(0, dy);
}
if (mOnLoadMoreListener != null) {
if (downView == null) {
if (isSlideToBottom(this)) {
mOnLoadMoreListener.onLoadMore(currentfocus);
}
}
}
return true;
case KeyEvent.KEYCODE_DPAD_UP:
View upView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_UP);
// Log.i(TAG, "upView is null:" + (upView == null));
if (event.getAction() == KeyEvent.ACTION_UP) {
Log.i("FFDDD::", "66666");
return true;
} else {
if (upView != null) {
Log.i("FFDDD::", "DDDD");
View topView = FocusFinder.getInstance().findNextFocus(this, upView, View.FOCUS_UP);
if (topView != null) {
this.smoothScrollBy(0, -dy);
} else {
//this.smoothMoveToPosition(0);
// this.smoothScrollBy(0, -this.getChildAt(0).getHeight()*2);
}
upView.requestFocus();
return true;
} else {
Log.i("FFDDD::", "顶部view为空");
LayoutManager layoutManager = getLayoutManager();
int childLayoutPosition = getChildLayoutPosition(focusView);
if (layoutManager instanceof GridLayoutManager) {
int spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
if (childLayoutPosition <= spanCount) {
return result;
}
} else if (layoutManager instanceof LinearLayoutManager) {
if (childLayoutPosition == 0) {
return result;
}
}
this.smoothScrollBy(0, -dy);
return true;
}
}
}
}
}
return result;
}
如何判定分页以及分页加载刷新后焦点逻辑
判定分页,也就是判定滑动到底部了,通常按道理来说可以和手机recyeclerview一样在这里处理
@Override
public void onScrollStateChanged(int state) {
if (state == SCROLL_STATE_IDLE) {
// 加载更多回调,个人认为要保存一下上次焦点的位置
/*if (null != mOnLoadMoreListener) {
if (getLastVisiblePosition() ==getAdapter().getItemCount() - (1 + mLoadMoreBeforehandCount)) {
mOnLoadMoreListener.onLoadMore(currentfocus);
isBottom=true;
}else{
isBottom=false;
}
}*/
}
super.onScrollStateChanged(state);
if (mShouldScroll) {
mShouldScroll = false;
smoothMoveToPosition(mToPosition);
}
}
但我用了之后发现了一个问题:就是我遥控器控制的焦点还没有到底部的时候,就触发了。我的方案是在遥控器下按的时候,判断是否完全到了屏幕底部,在触发加载更多的事件
case KeyEvent.KEYCODE_DPAD_DOWN:
//让第一个默认选中
View lView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
View rView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
View downView;
if (lView == null && rView == null) {
//焦点左边的view与焦点右边的view都是空的话,说明此事焦点在头部,
// 说明此时是从导航栏按下来,要让第一个item获得焦点
downView = getChildAt(2);//第一个item就是第二个position的位置
} else {
downView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_DOWN);
}
Log.i(TAG, " downView is null:" + (downView == null));
if (downView != null) {
downView.requestFocus();
} else {
this.smoothScrollBy(0, dy);
}
if (mOnLoadMoreListener != null) { if (downView == null) { if (isSlideToBottom(this)) { mOnLoadMoreListener.onLoadMore(currentfocus); } } }
return true;
判断当前recyclerview是否真正的滑到底部
public static boolean isSlideToBottom(RecyclerView recyclerView) {
if (recyclerView == null) return false;
if (recyclerView.computeVerticalScrollExtent() + recyclerView.computeVerticalScrollOffset()
>= recyclerView.computeVerticalScrollRange())
return true;
return false;
}
至于分页加载之后,焦点逻辑的处理,始终头脑里要清醒知道,不能全部刷新,而要局部刷新
notifyItemChanged(int position) 对单个item进行刷新
notifyItemRangeChanged(int startpos,int n)从起始位置(startpos)开始刷新 n个item
这两个方法是我用到最多的方法。主要其他的什么notifyXXXinsert 或者 notifyXXXRemove这两个有几个大坑,就是貌似界面刷新了,但position的位置其实没改动,就会导致数据错乱。至于起始点的确认这边就需要算法了。一般是数据源的删除后的大小减去一,如果有头部的话就是数据源删除后的大小。
//加载新一页的数据
public void loadmore(List list) {
if (dataList != null && dataList.size() != 0 && list != null && list.size() != 0) {
Log.i("NNMMM::", "DD");
int start = dataList.size();
int length = list.size();
dataList.addAll(list);
if (!onBind) {
notifyItemRangeChanged(start, length);//处理焦点的错乱
}
} else if (dataList.size() != 0 && list.size() == 0) {
Log.i("NNMMM2::", "DD");
//没数据不刷新
} else if (dataList.size() == 0 && list.size() != 0) {
Log.i("NNMMM3::", "DD");
int start = 0;
int length = list.size();
dataList.addAll(list);
if (!onBind) {
notifyItemRangeChanged(start + 1, length);//处理焦点的错乱
}
} else if (dataList.size() == 0 && list.size() == 0) {
Log.i("NNMMM4::", "DD");
if (remoteControlListener != null) {
remoteControlListener.showEmpty();
}
}
}
删除后的焦点该如何控制
这里还是涉及到一定的算法,我分为正向删除(从上往下删)和反向删除(从最后向上删除),屏幕里全都删除完了,就默认加载下一页的数据。
正向删除:我把焦点控制在下一个标题的第一个item。主要记录我当前取消订阅的position的位置+2-删除的条数(包括取消)。如下图:
删除后数据刷新就要考虑下了,我应该是从要删除的数据第一个item的位置刷新,刷新的条目应该是剩余的所有数据的个数。(如果控制的个数不对就会导致数据错乱)
反向删除:就是从屏幕最后一条开始删除,这个简单,我应该是从要删除的数据第一个item的位置刷新,刷新的个数就是删除的个数。
public void cancelSubscribe(String title) {
if (deleteIdList != null) {
deleteIdList.add(title);//将要取消订阅的id存储起来
}
List readyToDeleteList = new ArrayList();
List moveList = new ArrayList();
for (int i = 0; i < dataList.size(); i++) {
if (dataList.get(i).getDelete_title().equals(title) || dataList.get(i).getConnect_title().equals(title)) {
readyToDeleteList.add(dataList.get(i));
moveList.add(i + 1);
}
}
currentCancelFocus = moveList.get(moveList.size() - 1) + 2 - moveList.size();
Log.i("BBVVSS::",currentCancelFocus+"");
int flashCount=dataList.size()-moveList.get(moveList.size()-1);
dataList.removeAll(readyToDeleteList);//将数据删掉
if (currentCancelFocus
刷新数据的时候要有默认标记位,标记此次是删除操作,处理焦点的ui变化。
.导航栏下来如何保存导航的状态以及第一个聚焦的始终是第一个item。
确定焦点在导航栏,判断此时聚焦的view左右两边的view都为null,我就认为是导航。然后我给每个导航的textview设置向下按的焦点ID为XXX,在adapter中判断第3个item的设置ID也为XXX就可以了
附件:
package com.familybox.adapter;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.familybox.R;
import com.familybox.model.bean.PersonalityGrowthBean;
import com.shan.library.glide.ImageUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Created by hcy on 2018/4/11.
* func:个性成长详情页的适配器
*/
public class PersonalityGrowthAdapter extends RecyclerView.Adapter {
//定义数据
private List 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;
//当取消订阅页面没有数据的时候,
// 就会自动加载下一页,如果下一页没数据的话,要刷新一次界面
public void setTheLastPageHasNoData() {
if (getCurrentNavIndex() == 2) {
if (isCancel) {
notifyDataSetChanged();
}
}
}
public void clearData() {
dataList.clear();
notifyDataSetChanged();
}
private boolean isFocus = false;
//强制给第一个内容配置焦点
public void forceGiveFocus() {
isFocus = true;
}
//重置删除列表
public void resetDeleteList() {
if (deleteIdList != null) {
deleteIdList = new ArrayList();
}
}
public void flashHasSee(int position) {
Log.i("BBVVV::", position + "");
dataList.get(position - 1).setSelect(true);
notifyItemChanged(position);
}
public interface onRemoteControlListener {
//遥控器确定操作
void makeSure(int navPosition, int currentItemFocusPostion, String content_id);
//切换导航
void changeNav(int navPosition);
//取消订阅
void cancelSubscribe(String title);
//调用家长订阅下一页的数据
void getNextPageData();
//显示没有数据的界面
void showEmpty();
//影藏没数据的界面
void dissEmpty();
}
private onRemoteControlListener remoteControlListener;
public void setRemoteControlListener(onRemoteControlListener remoteControlListener) {
this.remoteControlListener = remoteControlListener;
}
public String getDeleteList() {
if (deleteIdList != null && deleteIdList.size() != 0) {
//里面有要取消的内容
StringBuffer sb = new StringBuffer();
for (int i = 0; i < deleteIdList.size(); i++) {
sb.append(deleteIdList.get(i)).append(",");
}
Log.i("取消订阅id集合222::", sb.toString().substring(0, sb.toString().length() - 1));
return sb.toString().substring(0, sb.toString().length() - 1);
}
return null;
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
return TYPE_HEAD;
} else {
PersonalityGrowthBean testData01 = dataList.get(position - 1);
if (testData01.getType() == 0) {
return TYPE_TITLE;
} else if (testData01.getType() == 1) {
return TYPE_CONTENT;
}
}
return super.getItemViewType(position);
}
private LinearLayout ll_tbar;
private int currentNavIndex = 0;
public int getCurrentNavIndex() {
return currentNavIndex;
}
//用于导航栏左右逻辑
public void func(int dir) {
//当前是否在导航栏上,正数向右,负数向左
// Toast.makeText(context,"当前在导航栏上", Toast.LENGTH_SHORT).show();
if (dir > 0) {
if (currentNavIndex >= 0 && currentNavIndex < 2) {
currentNavIndex++;
if (currentNavIndex >= 0 && currentNavIndex <= 2) {
Log.i("BBNVVV::", currentNavIndex + "");
remoteControlListener.changeNav(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--;
isCancel = false;//关闭取消订阅焦点聚焦在第一个的位置
if (currentNavIndex >= 0 && currentNavIndex <= 2) {
Log.i("BBNVVV::", currentNavIndex + "");
remoteControlListener.changeNav(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) {
if (currentNavIndex == 2) {
//家长订阅
//重置取消id的列表
//deleteIdList = new ArrayList();
} else {
//取消订阅了
if (currentNavIndex == 1) {
if (deleteIdList != null && deleteIdList.size() != 0) {
//里面有要取消的内容
StringBuffer sb = new StringBuffer();
for (int i = 0; i < deleteIdList.size(); i++) {
sb.append(deleteIdList.get(i)).append(",");
}
Log.i("取消订阅id集合::", sb.toString().substring(0, sb.toString().length() - 1));
remoteControlListener.cancelSubscribe(sb.toString().substring(0, sb.toString().length() - 1));
}
}
}
/* isCancel = false;//关闭取消订阅焦点聚焦在第一个的位置
if (currentNavIndex>=0&¤tNavIndex<=2){
Log.i("BBNVVV::",currentNavIndex)
remoteControlListener.changeNav(currentNavIndex);
}*/
}
}
public PersonalityGrowthAdapter(Context context) {
dataList = new ArrayList();
this.context = context;
inflater = LayoutInflater.from(context);
deleteIdList = new ArrayList();
}
@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);
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;
}
}
});
}
}
private void itemFocus(RecyclerView.ViewHolder holder, final int position, PersonalityGrowthBean testData01, View view) {
onBind = true;
mCurrentfocus = position;
/* if (testData01.isSelect()) {
((PGView_ContentHolder) holder).iv_isHasSee.setVisibility(View.GONE);
} else {
((PGView_ContentHolder) holder).iv_isHasSee.setVisibility(View.GONE);
}*/
Drawable drawable = context.getResources().getDrawable(R.drawable.pg_content_bg);
((PGView_ContentHolder) holder).rl_top.setBackgroundDrawable(drawable);
((PGView_ContentHolder) holder).iv_isHasSee.setVisibility(View.GONE);
if (!testData01.isFunc()) {
((PGView_ContentHolder) holder).rl_content_s.setVisibility(View.VISIBLE);
((PGView_ContentHolder) holder).tv_content.setVisibility(View.GONE);
}
enlargeAnim(view);
onBind = false;
}
private void itemNotFocus(RecyclerView.ViewHolder holder, final int position, PersonalityGrowthBean testData01, View view, boolean isAnim) {
onBind = true;
if (testData01.isSelect()) {
((PGView_ContentHolder) holder).iv_isHasSee.setVisibility(View.VISIBLE);
} else {
((PGView_ContentHolder) holder).iv_isHasSee.setVisibility(View.GONE);
}
Drawable drawable = context.getResources().getDrawable(R.drawable.pg_bg_unselect);
((PGView_ContentHolder) holder).rl_top.setBackgroundDrawable(drawable);
if (!testData01.isFunc()) {
((PGView_ContentHolder) holder).rl_content_s.setVisibility(View.GONE);
((PGView_ContentHolder) holder).tv_content.setVisibility(View.VISIBLE);
}
if (isAnim) {
reduceAnim(view);
}
onBind = false;
}
private Boolean isCancel = false;
private int currentCancelFocus;
private List deleteIdList;//取消订阅的id集合
public void cancelSubscribe(String title) {
if (deleteIdList != null) {
deleteIdList.add(title);//将要取消订阅的id存储起来
}
List readyToDeleteList = new ArrayList();
List moveList = new ArrayList();
for (int i = 0; i < dataList.size(); i++) {
if (dataList.get(i).getDelete_title().equals(title) || dataList.get(i).getConnect_title().equals(title)) {
readyToDeleteList.add(dataList.get(i));
moveList.add(i + 1);
}
}
currentCancelFocus = moveList.get(moveList.size() - 1) + 2 - moveList.size();
Log.i("BBVVSS::",currentCancelFocus+"");
int flashCount=dataList.size()-moveList.get(moveList.size()-1);
dataList.removeAll(readyToDeleteList);//将数据删掉
if (currentCancelFocus list) {
dataList = list;
if (remoteControlListener != null) {
if (dataList.size() == 0) {
remoteControlListener.showEmpty();
} else {
remoteControlListener.dissEmpty();
}
}
if (!onBind) {
notifyDataSetChanged();
}
}
//加载新一页的数据
public void loadmore(List list) {
if (dataList != null && dataList.size() != 0 && list != null && list.size() != 0) {
Log.i("NNMMM::", "DD");
int start = dataList.size();
int length = list.size();
dataList.addAll(list);
if (!onBind) {
notifyItemRangeChanged(start, length);//处理焦点的错乱
}
} else if (dataList.size() != 0 && list.size() == 0) {
Log.i("NNMMM2::", "DD");
//没数据不刷新
} else if (dataList.size() == 0 && list.size() != 0) {
Log.i("NNMMM3::", "DD");
int start = 0;
int length = list.size();
dataList.addAll(list);
if (!onBind) {
notifyItemRangeChanged(start + 1, length);//处理焦点的错乱
}
} else if (dataList.size() == 0 && list.size() == 0) {
Log.i("NNMMM4::", "DD");
if (remoteControlListener != null) {
remoteControlListener.showEmpty();
}
}
}
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_s;
private TextView tv_content;
private ImageView iv_ico;
private ImageView iv_isHasSee;//用于标记是否已看
private RelativeLayout rl_top;
private RelativeLayout rl_content_s;
public PGView_ContentHolder(View itemView) {
super(itemView);
itemView.setFocusable(true);
rl_top = itemView.findViewById(R.id.rl_top);
rl_content_s = itemView.findViewById(R.id.rl_content_s);
tv_content_s = itemView.findViewById(R.id.tv_content_s);
tv_content = itemView.findViewById(R.id.tv_content);
iv_isHasSee = itemView.findViewById(R.id.iv_isHasSee);
iv_ico = itemView.findViewById(R.id.iv_ico);
}
}
//获取焦点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.pg_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.pg_uikit_reduce);
a.setFillAfter(true);
view.clearAnimation();
view.startAnimation(a);
a.start();
}
}
}
package com.familybox.ui.family.widget.thrid_party;
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.familybox.R;
import com.familybox.adapter.PersonalityGrowthAdapter;
/**
* Created by liuyu on 17/1/6.
*/
public class PGRecyclerView 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 PGRecyclerView(Context context) {
this(context, null);
}
public PGRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public PGRecyclerView(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);
void ParentsSubscribe();
}
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;
}
private boolean isBottom = false;
@Override
public void onScrollStateChanged(int state) {
if (state == SCROLL_STATE_IDLE) {
// 加载更多回调,个人认为要保存一下上次焦点的位置
/*if (null != mOnLoadMoreListener) {
if (getLastVisiblePosition() ==getAdapter().getItemCount() - (1 + mLoadMoreBeforehandCount)) {
mOnLoadMoreListener.onLoadMore(currentfocus);
isBottom=true;
}else{
isBottom=false;
}
}*/
}
super.onScrollStateChanged(state);
if (mShouldScroll) {
mShouldScroll = false;
smoothMoveToPosition(mToPosition);
}
}
/**
* 子类如果要重写dispatchKeyEvent的话请调用个
*
* @param event
* @return
*/
protected boolean customDispatchKeyEvent(KeyEvent event) {
return super.dispatchKeyEvent(event);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.i("VVCCC::", event.getKeyCode() + "");
if (mInterceptLister != null && mInterceptLister.onIntercept(event)) {
return true;
}
if (KeyEvent.KEYCODE_BACK == event.getKeyCode()) {
Log.i("RRRRFF::", "isreturn");
return false;
}
boolean result = super.dispatchKeyEvent(event);
View focusView = this.getFocusedChild();
currentfocus = getChildPosition(focusView);//把当前的聚焦对象的位置记录下来
if (focusView == null) {
Log.d(TAG, "dispatchKeyEvent:--> focusView == null" + currentfocus);
return result;
} else {
int dy = 0;
int dx = 0;
if (getChildCount() > 0) {
View firstView = this.getChildAt(0);
dy = firstView.getHeight();
dx = firstView.getWidth();
}
if (event.getAction() == KeyEvent.ACTION_UP) {
Log.i("FFDDD225::", "");
return true;
} else {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_RIGHT:
View rightView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
View leftViews = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
if (rightView == null && leftViews == null) {
if (getAdapter() != null) {
((PersonalityGrowthAdapter) getAdapter()).func(1);
}
}
Log.i(TAG, "rightView is null:" + (rightView == null));
Log.i(TAG, "leftViews is null:" + (leftViews == null));
if (rightView != null) {
rightView.requestFocus();
return true;
} else {
this.smoothScrollBy(dx, 0);
return true;
}
case KeyEvent.KEYCODE_SETTINGS:
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.ParentsSubscribe();
}
return true;
case KeyEvent.KEYCODE_DPAD_LEFT:
View leftView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
View rightViews = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
if (rightViews == null && leftView == null) {
if (getAdapter() != null) {
((PersonalityGrowthAdapter) getAdapter()).func(-1);
}
}
Log.i(TAG, "leftView is null:" + (leftView == null));
if (leftView != null) {
leftView.requestFocus();
return true;
} else {
this.smoothScrollBy(-dx, 0);
return true;
}
case KeyEvent.KEYCODE_DPAD_DOWN:
//让第一个默认选中
View lView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_LEFT);
View rView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_RIGHT);
View downView;
if (lView == null && rView == null) {
//焦点左边的view与焦点右边的view都是空的话,说明此事焦点在头部,
// 说明此时是从导航栏按下来,要让第一个item获得焦点
downView = getChildAt(2);//第一个item就是第二个position的位置
} else {
downView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_DOWN);
}
Log.i(TAG, " downView is null:" + (downView == null));
if (downView != null) {
downView.requestFocus();
} else {
this.smoothScrollBy(0, dy);
}
if (mOnLoadMoreListener != null) {
if (downView == null) {
if (isSlideToBottom(this)) {
mOnLoadMoreListener.onLoadMore(currentfocus);
}
}
}
return true;
case KeyEvent.KEYCODE_DPAD_UP:
View upView = FocusFinder.getInstance().findNextFocus(this, focusView, View.FOCUS_UP);
// Log.i(TAG, "upView is null:" + (upView == null));
if (event.getAction() == KeyEvent.ACTION_UP) {
Log.i("FFDDD::", "66666");
return true;
} else {
if (upView != null) {
Log.i("FFDDD::", "DDDD");
View topView = FocusFinder.getInstance().findNextFocus(this, upView, View.FOCUS_UP);
if (topView != null) {
this.smoothScrollBy(0, -dy);
} else {
//this.smoothMoveToPosition(0);
// this.smoothScrollBy(0, -this.getChildAt(0).getHeight()*2);
}
upView.requestFocus();
return true;
} else {
Log.i("FFDDD::", "顶部view为空");
LayoutManager layoutManager = getLayoutManager();
int childLayoutPosition = getChildLayoutPosition(focusView);
if (layoutManager instanceof GridLayoutManager) {
int spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
if (childLayoutPosition <= spanCount) {
return result;
}
} else if (layoutManager instanceof LinearLayoutManager) {
if (childLayoutPosition == 0) {
return result;
}
}
this.smoothScrollBy(0, -dy);
return true;
}
}
}
}
}
return result;
}
public static boolean isSlideToBottom(RecyclerView recyclerView) {
if (recyclerView == null) return false;
if (recyclerView.computeVerticalScrollExtent() + recyclerView.computeVerticalScrollOffset()
>= recyclerView.computeVerticalScrollRange())
return true;
return false;
}
@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;
}
/**
* 目标项是否在最后一个可见项之后
*/
private boolean mShouldScroll;
/**
* 记录目标项位置
*/
private int mToPosition;
/**
* 滑动到指定位置
*
* @param
* @param position
*/
public void smoothMoveToPosition(int position) {
// 第一个可见位置
int firstItem = getChildLayoutPosition(getChildAt(0));
// 最后一个可见位置
int lastItem = getChildLayoutPosition(getChildAt(getChildCount() - 1));
if (position < firstItem) {
// 如果跳转位置在第一个可见位置之前,就smoothScrollToPosition可以直接跳转
smoothScrollToPosition(position);
} else if (position <= lastItem) {
// 跳转位置在第一个可见项之后,最后一个可见项之前
// smoothScrollToPosition根本不会动,此时调用smoothScrollBy来滑动到指定位置
int movePosition = position - firstItem;
if (movePosition >= 0 && movePosition < getChildCount()) {
int top = getChildAt(movePosition).getTop();
smoothScrollBy(0, top);
}
} else {
// 如果要跳转的位置在最后可见项之后,则先调用smoothScrollToPosition将要跳转的位置滚动到可见位置
// 再通过onScrollStateChanged控制再次调用smoothMoveToPosition,执行上一个判断中的方法
smoothScrollToPosition(position);
mToPosition = position;
mShouldScroll = true;
}
}
}
package com.familybox.ui.baby.personality_growth;
import android.app.Activity;
import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.widget.GridLayoutManager;
import android.util.Log;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import android.widget.Toast;
import com.familybox.R;
import com.familybox.ui.BaseActivity;
import com.familybox.ui.IView.PersonalityGrowthActivityView;
import com.familybox.ui.family.behavior_correct.BehaviorCorrectActivity;
import com.familybox.ui.family.widget.thrid_party.PGRecyclerView;
import com.familybox.utils.PGRecyclerViewItemDecoration;
import com.familybox.presenter.personality_growth.PersonalityGrowthActivityPresenter;
import com.familybox.adapter.PersonalityGrowthAdapter;
import com.familybox.model.bean.PersonalityGrowthBean;
import com.familybox.video.PlayerActivity;
import java.util.List;
import butterknife.BindView;
/**
* Created by hcy on 2018/4/11.
* 个性成长
* func:家长订阅的取消订阅的功能
*/
public class PersonalityGrowthActivity extends BaseActivity implements PersonalityGrowthActivityView {
@BindView(R.id.rv_main)
PGRecyclerView rv_main;
private GridLayoutManager gridLayoutManager;
private static final String TAG = "PGActivity:::";
private PersonalityGrowthAdapter adapter;
private PersonalityGrowthActivityPresenter presenter;
private int currentPage = 1;
@BindView(R.id.tv_tag_01)
TextView tv_tag_01;
@BindView(R.id.tv_tag_02)
TextView tv_tag_02;
@BindView(R.id.ll_tag)
LinearLayout ll_tag;
@BindView(R.id.ll_empty)
LinearLayout ll_empty;
private int playPosition = 0;
@Override
protected void onResume() {
super.onResume();
dismissPopWindow();
}
public static void launch(Activity activity) {
Intent intent = new Intent(activity, PersonalityGrowthActivity.class);
activity.startActivity(intent);
}
@Override
protected int setLayoutId() {
return R.layout.activty_personality_growth;
}
@Override
protected void init() {
initUI();
initData();
}
@Override
protected void requestInitFocus() {
}
private Boolean load_more_tag = true;
private void initData() {
//数据模拟
//定义数据:0代表课程相关 1代表测试推荐,2代表家长订阅
presenter = new PersonalityGrowthActivityPresenter(this);
//首次加载家长订阅的第一页数据;
currentPage = 1;
presenter.getDataFromServer(adapter.getCurrentNavIndex(), currentPage, PersonalityGrowthActivity.this);
adapter.setRemoteControlListener(new PersonalityGrowthAdapter.onRemoteControlListener() {
@Override
public void makeSure(int navPosition, int currentItemFocusPostion, String content_id) {
playPosition = currentItemFocusPostion;
PlayerActivity.launch(PersonalityGrowthActivity.this, 1, Integer.valueOf(content_id));
}
@Override
public void changeNav(int navPosition) {
SparseArray> sparseArray = presenter.getSparseArray();
Log.i("缓存?::", navPosition + "");
List testData01s = (List) sparseArray.get(navPosition);
if (testData01s != null) {
Log.i("缓存::", "缓存有数据");
if (adapter != null) {
adapter.setData(testData01s);
}
} else {
Log.i("缓存::", "缓存没数据");
if (adapter != null) {
adapter.clearData();
showEmptys();
}
}
currentPage = 1;
isBottom = false;
load_more_tag = true;
presenter.getDataFromServer(adapter.getCurrentNavIndex(), currentPage, PersonalityGrowthActivity.this);
}
@Override
public void cancelSubscribe(String title) {
presenter.cancelSubscribe(title, PersonalityGrowthActivity.this);
}
@Override
public void getNextPageData() {
currentPage++;
presenter.load_more_data(adapter.getCurrentNavIndex(), currentPage, PersonalityGrowthActivity.this);
}
@Override
public void showEmpty() {
showEmptys();
}
@Override
public void dissEmpty() {
ll_empty.setVisibility(View.GONE);
}
});
rv_main.setOnLoadMoreListener(new PGRecyclerView.OnLoadMoreListener() {
@Override
public void onLoadMore(int currentfocus) {
if (adapter != null) {
if (adapter.getItemCount() != 1) {
if (load_more_tag) {
load_more_tag = false;
if (!isBottom) {
currentPage++;
//Toast.makeText(PersonalityGrowthActivity.this,"正在加载"+currentPage+"页的数据",Toast.LENGTH_SHORT).show();
presenter.load_more_data(adapter.getCurrentNavIndex(), currentPage, PersonalityGrowthActivity.this);
}
}
}
}
}
@Override
public void ParentsSubscribe() {
//处理家长订阅的时候,如果有取消订阅的话,强求取消订阅的接口
if (adapter != null) {
String title = adapter.getDeleteList();
if (title != null) {
presenter.cancelSubscribe(title, PersonalityGrowthActivity.this);
}
}
showPopWindow();
//跳转到家长订阅
// startActivity(new Intent(PersonalityGrowthActivity.this, BehaviorCorrectActivity.class));
}
});
}
private void initUI() {
initPopWindow();
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);
}
private void showEmptys() {
Log.i("BBB::", "显示kong");
ll_empty.setVisibility(View.VISIBLE);
switch (adapter.getCurrentNavIndex()) {
case 0:
ll_tag.setVisibility(View.GONE);
tv_tag_01.setText("未收到课程相关推荐");
tv_tag_02.setVisibility(View.VISIBLE);
tv_tag_02.setText("可能是老师尚未制定本月教学内容");
break;
case 1:
ll_tag.setVisibility(View.GONE);
tv_tag_01.setText("暂未收到测评相关推荐");
tv_tag_02.setVisibility(View.VISIBLE);
tv_tag_02.setText("测评内容需要老师观察并记录每周六进行发送");
break;
case 2:
tv_tag_01.setText("尚未订阅节目内容");
tv_tag_02.setVisibility(View.GONE);
ll_tag.setVisibility(View.VISIBLE);
break;
}
}
@Override
public void showProgress() {
}
@Override
public void hideProgress() {
}
@Override
public void showMessage(String msg) {
}
@Override
public void return_data(List list) {
// if (currentPage == 1) {
// if (list == null || list.size() == 0) {
// ll_empty.setVisibility(View.VISIBLE);//落网状态下
// showEmpty();
// } else {
// ll_empty.setVisibility(View.GONE);
// }
// }
if (adapter != null && list != null) {
adapter.setData(list);
}
}
private boolean isBottom;
@Override
public void loadmore(List list) {
load_more_tag = true;
if (list == null || list.size() == 0) {
currentPage--;
isBottom = true;
// Toast.makeText(PersonalityGrowthActivity.this, "没有更多数据了", Toast.LENGTH_SHORT).show();
if (adapter != null) {
//通知取消订阅页面没有数据了
adapter.loadmore(list);
}
} else {
if (adapter != null) {
adapter.loadmore(list);
if (adapter.getItemCount() > 1) {
rv_main.scrollBy(0, 100);
}
}
}
}
@Override
public void cancelSubscribe(boolean isCancel) {
//取消删除成功
//重置删除列表
if (adapter != null) {
adapter.resetDeleteList();
}
}
private PopupWindow subWindow;
private ImageView iv_tag;
private void initPopWindow() {
View dialogView = LayoutInflater.from(PersonalityGrowthActivity.this).inflate(R.layout.dialog_sublesson, null);
subWindow = new PopupWindow(dialogView, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
subWindow.setAnimationStyle(R.style.take_photo_anim);
subWindow.setFocusable(true);
subWindow.setOutsideTouchable(true);
subWindow.setBackgroundDrawable(new BitmapDrawable());
iv_tag = dialogView.findViewById(R.id.iv_tag);
iv_tag.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dismissPopWindow();
//跳转之前要保存焦点位置
startActivityForResult(new Intent(PersonalityGrowthActivity.this, BehaviorCorrectActivity.class), REQUEST_CODE);
}
});
}
private static final int REQUEST_CODE = 1;
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == 100) {
Log.i("HHGGDD::", "YYGGG");
//当订阅的页面,用户成功订阅的时候才会提示个性成长页面家长页面刷新数据
if (adapter.getCurrentNavIndex() == 2) {
currentPage = 1;
rv_main.smoothMoveToPosition(0);//滑到顶部加载数据
/* if (adapter!=null){
adapter.forceGiveFocus();
}*/
presenter.getDataFromServer(adapter.getCurrentNavIndex(), currentPage, PersonalityGrowthActivity.this);
}
}
if (requestCode == REQUEST_CODE && resultCode == 200) {
//刷新已观看
Log.i("HHGGDD::", "YYGGG222");
adapter.flashHasSee(playPosition);
}
}
private void showPopWindow() {
if (subWindow != null && !subWindow.isShowing()) {
subWindow.showAtLocation(rv_main, Gravity.LEFT, 0, 0);
}
}
private void dismissPopWindow() {
if (subWindow != null && subWindow.isShowing()) {
subWindow.dismiss();
}
}
@Override
protected void onPause() {
super.onPause();
isBottom = false;
load_more_tag = true;
}
@Override
protected void onDestroy() {
super.onDestroy();
//当在家长订阅的页面退出的时候,也要判断用户是否操作了取消订阅,然后与服务器同步数据
if (adapter != null) {
if (adapter.getCurrentNavIndex() == 2) {
String title = adapter.getDeleteList();
if (title != null) {
presenter.cancelSubscribe(title, PersonalityGrowthActivity.this);
}
}
}
}
}