您的位置:首页 > 其它

RecyclerView重新得到焦点返回上次失去焦点的位置

2017-06-14 18:53 471 查看

RecyclerView焦点处理

 
   近期在做一个TV项目,焦点的问题处理比较麻烦。在网上搜索都找不到这类处理的方法。所以分享一个处理方法给大家。重写了一个RecyclerView和FrameLayout。详情请大家去细看,我这里就不赘述。代码如下:
1、布局
activity_main
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/bg_ii"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="350dp"
android:paddingLeft="200dp"
android:paddingTop="100dp">

<ImageView
android:id="@+id/head_icon"
android:layout_width="250dp"
android:layout_height="250dp"
android:src="@mipmap/ic_launcher" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="20dp"
android:orientation="vertical">

<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="杨丽萍"
android:textColor="#ffffff"
android:textSize="30sp" />

<TextView
android:id="@+id/tv_introduce"
android:layout_width="300dp"
android:layout_height="100dp"
android:layout_marginTop="10dp"
android:ellipsize="end"
android:lines="3"
android:text="杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介杨丽萍的简介"
android:textColor="#c6c5d0"
android:textSize="28sp" />

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginBottom="5dp"
android:layout_marginTop="10dp">

<ImageView
android:id="@+id/iv_play"
android:layout_width="70dp"
android:layout_height="70dp"
android:background="@drawable/play_select_bg"
android:focusableInTouchMode="true" />

<ImageView
android:id="@+id/iv_like"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/iv_play"
android:background="@drawable/praise_select_bg"
android:focusableInTouchMode="true" />

<ImageView
android:id="@+id/iv_collect"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/iv_like"
android:background="@drawable/like_select_bg"
android:focusableInTouchMode="true" />

</RelativeLayout>
</LinearLayout>

</LinearLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="300dp"
android:paddingLeft="120dp"
android:paddingRight="120dp">

<com.valiantman.itemfocusdispose.SWRecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingBottom="20dp"
android:paddingTop="20dp" />
</RelativeLayout>
</LinearLayout>


item_layout_nomal

<com.valiantman.itemfocusdispose.SWFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true">

<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:scaleType="centerCrop"
/>

<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="bottom"
android:background="#40000000"
android:duplicateParentState="true"
android:ellipsize="marquee"
android:gravity="center"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:text="这只是一个标题"
android:textColor="#fff"
android:textSize="20sp" />
</com.valiantman.itemfocusdispose.SWFrameLayout>


2、适配器
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.Holder>{
private static final String TAG = RecyclerViewAdapter.class.getSimpleName();
private Context mContext;
private List<Bean> data;
private int mCurPosition = 0;
private int layoutManager = LAYOUT_MANAGER_LINEAR;

public static final int LAYOUT_MANAGER_LINEAR = 0;
public static final int LAYOUT_MANAGER_GRID = 1;
public static final int LAYOUT_MANAGER_LINEAR_1 = 2;
public static final int LAYOUT_MANAGER_GRID_1 = 3;

public RecyclerViewAdapter(Context context, List<Bean> data, int layoutManager) {
this.mContext = context;
this.data = data;
this.layoutManager = layoutManager;
}

public void setData(List<Bean> data) {
this.data = data;
}

@Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
view = LayoutInflater.from(mContext).inflate(R.layout.item_layout_nomal, parent, false);
return new Holder(view);
}

@Override
public void onBindViewHolder(final Holder holder, final int position) {
holder.title.setText(data.get(position).name);
holder.itemView.setFocusable(true);
holder.itemView.setTag(position);
holder.thumbnail.setImageResource(R.mipmap.test_bg);

FrameLayout.LayoutParams params = null;
switch (layoutManager) {
case LAYOUT_MANAGER_LINEAR:
params = setLayoutSize(490,262,0,0,10,10);
break;
case LAYOUT_MANAGER_GRID:
params = setLayoutSize(365,200,0,0,10,0);
break;
case LAYOUT_MANAGER_LINEAR_1:
params = setLayoutSize(250,160,0,0,10,0);
break;
case LAYOUT_MANAGER_GRID_1:
params = setLayoutSize(450,260,0,0,10,10);
break;
}
holder.root.setLayoutParams(params);

if (mOnItemKeyListener != null) {
holder.itemView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
return mOnItemKeyListener.onKey(v,keyCode,event);
}
});
}
if (mOnItemFocusChangeListener != null) {
holder.itemView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
Log.i(TAG, "has focus:" + position + "--" + hasFocus);
mCurPosition = (int) holder.itemView.getTag();
mOnItemFocusChangeListener.onItemFocusChangeListener(holder.itemView, hasFocus, mCurPosition);
}
});
}
if(mOnItemClickListener !=null){
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v,holder.getLayoutPosition());
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mOnItemClickListene
f079
r.onItemLongClick(v, holder.getLayoutPosition());
return true;
}
});
}
}

@NonNull
private FrameLayout.LayoutParams setLayoutSize(int width,int height,int left,int top,int right,int bottom) {
FrameLayout.LayoutParams params;
//        width = matrixing(width);
//        height = matrixing(height);
params = new FrameLayout.LayoutParams(width, height);
params.setMargins(left, top, right, bottom);
return params;
}

private int matrixing(int num) {
return ((int) (num * 2f / 3));
}

@Override
public int getItemCount() {
return data == null ? 0 : data.size();
}

class Holder extends RecyclerView.ViewHolder {
TextView title;
ImageView thumbnail;
SWFrameLayout root;

Holder(View view) {
super(view);
root = ((SWFrameLayout) view.findViewById(R.id.root));
title = ((TextView) view.findViewById(R.id.text));
thumbnail = ((ImageView) view.findViewById(R.id.image));
}
}

/*设置item点击事件的接口*/
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}

/*设置item选中的接口*/
public interface OnItemFocusChangeListener{
void onItemFocusChangeListener(View v, boolean hasFocus, int position);
}

/*设置item的onKey事件的接口*/
public interface OnItemKeyListener {
boolean onKey(View v, int keyCode, KeyEvent event);
}

private OnItemClickListener mOnItemClickListener;
private OnItemFocusChangeListener mOnItemFocusChangeListener;
private OnItemKeyListener mOnItemKeyListener;

public void setOnItemFocusChangeListener(OnItemFocusChangeListener listener){
mOnItemFocusChangeListener = listener;
}

public void setOnItemClickListener(OnItemClickListener listener){
mOnItemClickListener = listener;
}

public void setOnItemKeyListener(OnItemKeyListener listener) {
mOnItemKeyListener = listener;
}
}


3、MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener,RecyclerViewAdapter.OnItemFocusChangeListener {
private static final String TAG = MainActivity.class.getName();
private SWRecyclerView mRecyclerView;
private ImageView mHeadIcon;
private TextView mName;
private TextView mIntroduce;
private ImageView mPlay;
private ImageView mLike;
private ImageView mCollect;
private List<Bean> mData;
private Bean danceBean;
private LinearLayoutManager mLayoutManager;
private RecyclerViewAdapter mDanceTypeAdapter;
private int mIVFlag = 0;
private int mRVFlag = 0;
private int mRVCurrentPosition;
private long mMediaDetailId;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

initView();
initData();
initAdapter();
}

protected void initView() {
mRecyclerView = ((SWRecyclerView) findViewById(R.id.recycler_view));
mHeadIcon = ((ImageView) findViewById(R.id.head_icon));
mPlay = ((ImageView) findViewById(R.id.iv_play));
mLike = ((ImageView) findViewById(R.id.iv_like));
mCollect = ((ImageView) findViewById(R.id.iv_collect));
mName = ((TextView) findViewById(R.id.tv_name));
mIntroduce = ((TextView) findViewById(R.id.tv_introduce));

mPlay.setOnClickListener(this);
mLike.setOnClickListener(this);
mCollect.setOnClickListener(this);
}

protected void initData() {
mData = new ArrayList<>();
for (int i = 0; i < 6; i++) {
mData.add(new Bean("我爱北京天门-" + i));
}
}

private void initAdapter() {
mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
mDanceTypeAdapter = new RecyclerViewAdapter(this, mData, RecyclerViewAdapter.LAYOUT_MANAGER_LINEAR_1);
mDanceTypeAdapter.setOnItemFocusChangeListener(this);
mPlay.requestFocus();
useLinearLayout();
}

private void useLinearLayout() {
mRecyclerView.setAdapter(mDanceTypeAdapter);
mRecyclerView.setLayoutManager(mLayoutManager);
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean ret = false;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
if (mPlay.hasFocus() || mLike.hasFocus() || mCollect.hasFocus()) {  //如果是三个图标获得焦点按上键不会下发
ret = true;
}else {
switch (mIVFlag) {      //判断上次失去焦点是哪个图标
case 0:
mPlay.requestFocus();
break;
case 1:
mLike.requestFocus();
break;
case 2:
mCollect.requestFocus();
break;
}
ret = false;
}
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
if (mPlay.hasFocus()) {     //失去焦点时标记
mIVFlag = 0;
} else if (mLike.hasFocus()) {
mIVFlag = 1;
} else {
mIVFlag = 2;
}
mRecyclerView.setDefaultSelect(mRVCurrentPosition);  //设置RecyclerView获得焦点的位置,首次是默认首个item获得焦点
ret = false;
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
if (mPlay.hasFocus()) {     //如果是播放图标获得焦点按左键就不会下发
ret = true;
} else {
ret = false;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (mCollect.hasFocus()) {   //如果是收藏图标获得焦点按右键就不会下发
ret = true;
} else {
ret = false;
}
break;
case KeyEvent.KEYCODE_MENU:
ret = true;
break;
case KeyEvent.KEYCODE_BACK:
finish();
ret = true;
break;
default:
break;
}
return ret;
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.iv_play:
Log.i("play", "点击了play");
Toast.makeText(this, "点击了播放", Toast.LENGTH_SHORT).show();
break;
case R.id.iv_like:
Log.i("play", "点击了like");
break;
case R.id.iv_collect:
Log.i("play", "点击了collect");
break;
}
}

@Override
public void onItemFocusChangeListener(View v, boolean hasFocus, int position) {
mRVCurrentPosition = position;      //记录失去焦点的位置
if (v instanceof SWFrameLayout)
((SWFrameLayout)v).onFocusChange(v, hasFocus); //被拦截,需要手动开启,不然item不能放大。
}
}


4、自定义View
public class SWFrameLayout extends FrameLayout implements View.OnFocusChangeListener, View.OnClickListener{

private static final String TAG = SWFrameLayout.class.getSimpleName();
protected static final int ANIMATION_DURATION = 300;
protected static final float FOCUS_SCALE = 1.1f;
protected static final float NORMAL_SCALE = 1.0f;
private boolean mFocus = true;

public SWFrameLayout(Context context) {
this(context, null, 0);
}

public SWFrameLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public SWFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SWFrameLayout, defStyleAttr, 0);
mFocus = typedArray.getBoolean(R.styleable.SWFrameLayout_focus, true);
init();
typedArray.recycle();
}

protected void init() {
setOnClickListener(this);
setOnFocusChangeListener(this);

setBackgroundResource(R.drawable.home_fragment_item_selector);
setFocusable(mFocus);
setFocusableInTouchMode(mFocus);

}

@Override
public void onClick(View view) {

}

@Override
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
Log.i(TAG, "onFocusChanged: gainFocus = "+gainFocus+", direction = "+direction);
}

@Override
public void onFocusChange(View v, boolean hasFocus) {
if (null == v){
Log.i(TAG, "onFocusChange: v == null");
return;
}
Log.i(TAG, "onFocusChange: hasFocus = " +hasFocus);
if (hasFocus) {
v.bringToFront();
v.animate().scaleX(FOCUS_SCALE).scaleY(FOCUS_SCALE).setDuration(ANIMATION_DURATION).start();
v.setSelected(true);
} else {
v.animate().scaleX(NORMAL_SCALE).scaleY(NORMAL_SCALE).setDuration(ANIMATION_DURATION).start();
v.setSelected(false);
}
}
}
public class SWRecyclerView extends RecyclerView {

private static final String TAG = SWRecyclerView.class.getSimpleName();
private boolean mSelectedItemCentered = true;
private int mSelectedItemOffsetStart;
private int mSelectedItemOffsetEnd;
private int offset = -1;
private int mDuration = 0;

public SWRecyclerView(Context context) {
super(context);
}

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

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

@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 parentTop = getPaddingTop();
final int parentRight = getWidth() - getPaddingRight();
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 offScreenTop = Math.min(0, childTop - parentTop - mSelectedItemOffsetStart);
final int offScreenRight = Math.max(0, childRight - parentRight + mSelectedItemOffsetEnd);
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;
}

offset = isVertical() ? dy : dx;

if (dx != 0 || dy != 0) {
if (mDuration == 0)
smoothScrollBy(dx, dy);
else
smoothScrollBy(dx, dy, mDuration);
Log.i(TAG, "requestChildRectangleOnScreen: immediate--->"+immediate);
return true;
}

// 重绘是为了选中item置顶,具体请参考getChildDrawingOrder方法
postInvalidate();

return false;
}

private int getFreeWidth() {
return getWidth() - getPaddingLeft() - getPaddingRight();
}

private int getFreeHeight() {
return getHeight() - getPaddingTop() - getPaddingBottom();
}

/**
* 设置默认选中.
*/
public void setDefaultSelect(int pos) {
ViewHolder vh = (ViewHolder) findViewHolderForAdapterPosition(pos);
requestFocusFromTouch();
if (vh != null)
vh.itemView.requestFocus();
}

/**
* 设置SWRecyclerView的滑动速度
* @param duration
*/
public void setDuration(int duration){
mDuration = duration;
}

/**
* 设置选中的Item居中;
* @param isCentered
*/
public void setSelectedItemAtCentered(boolean isCentered) {
this.mSelectedItemCentered = isCentered;
}

/**
* 判断是垂直,还是横向.
*/
private boolean isVertical() {
LinearLayoutManager layout = (LinearLayoutManager) getLayoutManager();
return layout.getOrientation() == LinearLayoutManager.VERTICAL;
}

/**
* 利用反射拿到RecyclerView中的mViewFlinger属性,
* 再调用其smoothScrollBy(int dx, int dy, int duration) 方法实现RecyclerViewTV速度的控制
* @param dx
* @param dy
* @param duration
*/
public void smoothScrollBy(int dx, int dy, int duration) {
try {
Class<?> c = null;
try {
c = Class.forName("android.support.v7.widget.RecyclerView");//获得Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
return;
}
Field mLayoutField = c.getDeclaredField("mLayout");     //根据属性名称,获得类的属性成员Field
mLayoutField.setAccessible(true);                       //设置为可访问状态
LayoutManager mLayout = null;
try {
mLayout = (LayoutManager) mLayoutField.get(this);   //获得该属性对应的对象
if(mLayout == null){
return;
}
} catch (IllegalAccessException e) {
e.printStackTrace();
return;
}
Field mLayoutFrozen = c.getDeclaredField("mLayoutFrozen");
mLayoutFrozen.setAccessible(true);
try {
if((Boolean)mLayoutFrozen.get(this)){
return;
}
} catch (IllegalAccessException e) {
e.printStackTrace();
return;
}
if (!mLayout.canScrollHorizontally()) {
dx = 0;
}
if (!mLayout.canScrollVertically()) {
dy = 0;
}
Field mViewFlingerField = c.getDeclaredField("mViewFlinger");
mViewFlingerField.setAccessible(true);
try {
Class<?> ViewFlingerClass = null;
try {
//由于内部类是私有的,所以不能直接得到内部类名,
//通过mViewFlingerField.getType().getName()
//可以得到私有内部类的完整类名
ViewFlingerClass = Class.forName(mViewFlingerField.getType().getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
return;
}

//根据方法名,获得我们的目标方法对象。第一个参数是方法名,后面的是该方法的入参类型。
// 注意Integer.class与int.class的不同。
Method smoothScrollBy = ViewFlingerClass.getDeclaredMethod("smoothScrollBy",
int.class, int.class, int.class);
smoothScrollBy.setAccessible(true);//设置为可操作状态
if (dx != 0 || dy != 0) {
Log.d("MySmoothScrollBy", "dx="+dx + " dy="+dy);
try {
//唤醒(调用)方法,
// mViewFlingerField.get(this)指明是哪个对象调用smoothScrollBy。
// dx, dy, duration 是smoothScrollBy所需参数
smoothScrollBy.invoke(mViewFlingerField.get(this), dx, dy, duration);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
return;
}
}catch (NoSuchFieldException e){
return;
}
}
}


5、资源文件

home_fragment_item_selector focus是一张结尾.9的透明图片

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@mipmap/focus" />
<item android:state_selected="true" android:drawable="@mipmap/focus" />
<item android:drawable="@color/transparent" />
</selector>


播放、收藏、点赞这个三个图标随意用其它图片代替
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/like_o" android:state_focused="true"/>
<item android:drawable="@drawable/like_w" android:state_focused="false"/>
</selector>


获得焦点切换不同的图片




时间紧任务重,展示效果不是很好。凑合着看哈,重要的是处理方法。有兴趣的朋友可以试试。有不同见解请留言。互相学习。觉得不错就点个赞,谢谢...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  焦点 RecyclerView 定位