新公司业务主要是电视端。之前也做过,但也是浅尝辄止。主要是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.Re