一、RecyclerView的焦点记忆封装的学习
https://www.jianshu.com/p/75465a7fdd0f
二、整体代码压缩包
三、实现步骤:
1.将聚焦封装,继承Recyclerview即可
2.将原有的适配器的recyclerview改成FocusKeepRecyclerView
(1)在activity_main.xml中将RecyclerView改成FocusKeepRecyclerView<br /><br /> (2)在RecyclerViewAdapterDemo中将RecyclerView改成FocusKeepRecyclerView<br /><br /><br /> (3)在MainActivity中将RecyclerView改成FocusKeepRecyclerView<br />
3.添加item点击,以获取焦点
(1)定义一个接口,创建item点击方法<br /><br /> (2)创建点击事件,当它不为空的时候,获取点击的位置<br /><br /> (3)获取到的点击位置,按确定时,弹窗显示当前获取的位置数<br />
4.创建item_seletor.xml布局文件,选中和未选中的背景颜色的变化
四、整体代码
1.activity_main.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.customrecyclerview.FocusKeepRecyclerView
android:id="@+id/rv_demo"
android:layout_width="match_parent"
android:focusable="true"
android:layout_height="match_parent"/>
</LinearLayout>
2.item_recycler_view.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:background="@drawable/item_seletor"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
android:text="测试"
android:textSize="20sp"
android:textColor="#E2583F"
android:focusable="true"/>
</LinearLayout>
3.item_seletor.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@color/teal_200"/>
<item android:state_focused="false" android:drawable="@color/teal_700"/>
</selector>
4.FocusKeepRecyclerView文件代码
package com.example.customrecyclerview;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class FocusKeepRecyclerView extends RecyclerView {
private static final String TAG = FocusKeepRecyclerView.class.getSimpleName();
//是否可以纵向移出
private boolean mCanFocusOutVertical = true;
//是否可以横向移出
private boolean mCanFocusOutHorizontal = true;
//焦点移出recyclerview的事件监听
private FocusLostListener mFocusLostListener;
//焦点移入recyclerview的事件监听
private FocusGainListener mFocusGainListener;
//默认第一次选中第一个位置
private int mCurrentFocusPosition;
public FocusKeepRecyclerView(@NonNull Context context) {
super(context, null);
}
public FocusKeepRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs, 0);
}
public FocusKeepRecyclerView(@NonNull Context context,
@Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
setChildrenDrawingOrderEnabled(true);
setItemAnimator(null);
this.setFocusable(true);
}
public boolean isCanFocusOutVertical() {
return mCanFocusOutVertical;
}
public void setCanFocusOutVertical(boolean canFocusOutVertical) {
mCanFocusOutVertical = canFocusOutVertical;
}
public boolean isCanFocusOutHorizontal() {
return mCanFocusOutHorizontal;
}
public void setCanFocusOutHorizontal(boolean canFocusOutHorizontal) {
this.mCanFocusOutHorizontal = canFocusOutHorizontal;
}
public View focusSearch(View focused, int direction) {
Log.i(TAG, "focusSearch" + focused + ",direction=" + direction);
View view = super.focusSearch(focused, direction);
if (focused == null) {
return view;
}
if (view != null) {
View nextFocusItemView = findContainingItemView(view);
if (nextFocusItemView == null) {
if (!mCanFocusOutVertical && (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP)) {
//屏蔽焦点纵向移出recyclerview
return focused;
}
if (!mCanFocusOutHorizontal && (direction == View.FOCUS_LEFT || direction == View.FOCUS_RIGHT)) {
//屏蔽焦点横向移出recyclerview
return focused;
}
//调用移出的监听
if (mFocusLostListener != null) {
mFocusLostListener.onFocusLost(focused, direction);
}
return view;
}
}
return view;
}
public void setFocusLostListener(FocusLostListener focusLostListener) {
this.mFocusLostListener = focusLostListener;
}
public interface FocusLostListener {
void onFocusLost(View lastFocusChild, int direction);
}
public void setFocusGainListener(FocusGainListener focusGainListener) {
this.mFocusGainListener = focusGainListener;
}
public interface FocusGainListener {
void onFocusGain(View child, View focused);
}
@Override
public void requestChildFocus(View child, View focused) {
mCurrentFocusPosition = getChildViewHolder(child).getAdapterPosition();
Log.i(TAG, "focusPos=" + focused);
if (!hasFocus()) {
//recyclerview 子view重新获取焦点,调用移入焦点的事件监听
if (mFocusGainListener != null) {
mFocusGainListener.onFocusGain(child, focused);
}
}
//执行过super.requestChildFocus之后hasFocus会变成true
super.requestChildFocus(child, focused);
mCurrentFocusPosition = getChildViewHolder(child).getAdapterPosition();
Log.i(TAG, "focusPos=" + mCurrentFocusPosition);
}
//实现焦点记忆的关键代码
@Override
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
View view = null;
if (this.hasFocus() || mCurrentFocusPosition < 0 || (view = getLayoutManager().findViewByPosition(mCurrentFocusPosition)) == null) {
super.addFocusables(views, direction, focusableMode);
} else if (view.isFocusable()) {
//将当前的view放到Focusable views列表中,再次移入焦点是会取到该view,实现焦点记忆功能
views.add(view);
} else {
super.addFocusables(views, direction, focusableMode);
}
}
//控制当前焦点最后绘制,防止焦点放大后被遮挡
//原顺序123456789,当4是focus时,绘制顺序变为123567894
@Override
protected int getChildDrawingOrder(int childCount, int i) {
View focusedChild = getFocusedChild();
Log.i(TAG, "focusedChild=" + focusedChild);
if (focusedChild == null) {
return super.getChildDrawingOrder(childCount, i);
} else {
int index = indexOfChild(focusedChild);
Log.i(TAG, "index=" + index + ",i=" + i + "count=" + childCount);
if (i == childCount - 1) {
return index;
}
if (i < index) {
return i;
}
return i + 1;
}
}
}
5.RecyclerViewAdapterDemo文件代码
package com.example.customrecyclerview;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
//import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
// 1.创建适配器类继承自RecyclerView.Adapter,泛型传入RecyclerView.ViewHolder类。
public class RecyclerViewAdapterDemo extends
FocusKeepRecyclerView.Adapter<RecyclerViewAdapterDemo.MyViewHolder> {
private Context context;
private List<String> list;
private View inflater;
public RecyclerViewAdapterDemo(Context context, List<String> list){
this.context=context;
this.list=list;
}
private OnItemClickListener onItemClickListener;
public interface OnItemClickListener{
void onClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
this.onItemClickListener = listener;
}
//3.重写RecyclerView.Adapter类的相关方法。
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
inflater= LayoutInflater.from(context).inflate(R.layout.item_recycler_view,parent,false);
MyViewHolder viewHolder=new MyViewHolder(inflater);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.tv_view.setText(list.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(onItemClickListener!=null){
onItemClickListener.onClick(position);
}
}
});
}
@Override
public int getItemCount() {
return list.size();
}
// 2.创建内部类即RecyclerView.ViewHolder类的子类MyViewHolder,并初始化item的控件。
class MyViewHolder extends FocusKeepRecyclerView.ViewHolder{
private TextView tv_view;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
tv_view=itemView.findViewById(R.id.tv_view);
}
}
}
6.MainActivity文件代码
package com.example.customrecyclerview;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.Context;
import android.os.Bundle;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
// 1.获取RecyclerView对象
private FocusKeepRecyclerView recyclerView;
private RecyclerViewAdapterDemo adapterDemo;
private Context context;
private List<String> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context=this;
// 2.初始化数据
recyclerView=findViewById(R.id.rv_demo);
//3.适配器实例化
list=new ArrayList<>();
for (int i=0;i<9;i++){
list.add("第"+i+"个测试");
}
adapterDemo=new RecyclerViewAdapterDemo(context,list);
// 4.设置LayoutManager
GridLayoutManager manager=new GridLayoutManager(context,3);
manager.setOrientation(GridLayoutManager.VERTICAL);
// LinearLayoutManager manager=new LinearLayoutManager(context);
// manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
// 5. 设置Adapter
recyclerView.setAdapter(adapterDemo);
adapterDemo.setOnItemClickListener(new RecyclerViewAdapterDemo.OnItemClickListener() {
@Override
public void onClick(int position) {
Toast.makeText(MainActivity.this,""+position,Toast.LENGTH_SHORT).show();
}
});
}
}
7.结果图:<br />