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中含有CheckBox,Button等控件失去焦点的解决办法
- Android ListView/recyclerView条目中EditText容易失去焦点的问题和取出横向时上下滚动有阴影
- jQuery的input失去焦点之后,不能使用focus()重新得到焦点
- Recyclerview抢占焦点,导致切换fragment后位置错乱
- WPF 在TextBox失去焦点时检测数据,出错重新获得焦点解决办法
- 文本框得到焦点後,清空内容,失去焦点,还原成默认值
- WPF 在TextBox失去焦点时检测数据,出错重新获得焦点解决办法
- jquery-1.2.6得到焦点与失去焦点的写法
- 【技巧】DataGridView重新绑定时保持上次滚动位置(SamWang)
- 如何在org.eclipse.ui.part.ViewPart失去焦点时完成某些操作
- 得到光标上次所在文本框的位置
- jQuery插件---文本框得到和失去焦点插件
- 当tableview失去焦点时,消除高亮方法
- 输入关键字失去焦点后重新获得
- 文本框得到焦点後,清空内容,失去焦点,还原成默认值
- jQuery文本框得到,失去焦点
- input得到焦点和失去焦点的属性
- 得到和失去焦点
- 文本框得到焦点後,清空内容,失去焦点,还原成默认值
- jquery 文本框得到失去的焦点