0
点赞
收藏
分享

微信扫一扫

Android 探究RecyclerView onViewAttachedToWindow 触发时机



文章目录

  • ​​RecyclerView 基本使用​​
  • ​​FruitAdapter​​
  • ​​Adapter中的 onAttachedToRecyclerView​​
  • ​​Adapter中的 onViewAttachedToWindow​​
  • ​​瀑布流 `StaggeredGridLayoutManager` 设置第一个 item 占据一整行​​

RecyclerView 基本使用

​activity_main.xml​

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/fruit_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</androidx.appcompat.widget.LinearLayoutCompat>

​item_layout.xml​

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#ddd"
android:layout_marginTop="10dp"
android:orientation="vertical">

<TextView
android:id="@+id/fruit_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="30sp"
android:layout_gravity="center"
android:gravity="center"
android:textColor="#aaffcc" />
</LinearLayout>

​Fruit​

public class Fruit {
private String fruitName;

public String getFruitName() {
return fruitName;
}

public Fruit(String fruitName) {
this.fruitName = fruitName;
}

//获取数据
public static List<Fruit> initFruitList() {
List<Fruit> fruitList = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Fruit apple = new Fruit("Item " + i);
fruitList.add(apple);
}
return fruitList;
}
}

​FruitAdapter​

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {

private List<Fruit> mFruitList;

public FruitAdapter(List<Fruit> fruitList) {
mFruitList = fruitList;
}

@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}

@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
Fruit fruit = mFruitList.get(position);
viewHolder.fruitName.setText(fruit.getFruitName());
}

@Override
public int getItemCount() {
return mFruitList.size();
}

public class ViewHolder extends RecyclerView.ViewHolder {
TextView fruitName;

public ViewHolder(@NonNull View itemView) {
super(itemView);
fruitName = itemView.findViewById(R.id.fruit_name);
}
}

@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
}

@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}

@Override
public void onViewAttachedToWindow(@NonNull ViewHolder holder) {
super.onViewAttachedToWindow(holder);
}

@Override
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
}
}

​MainActivity​

class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding
private var adapter: FruitAdapter? = null
private var list = mutableListOf<Fruit>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

list = Fruit.initFruitList()
adapter = FruitAdapter(list)

binding.fruitRecyclerView.layoutManager = LinearLayoutManager(this)
binding.fruitRecyclerView.adapter = adapter

}
}

FruitAdapter

在上面,FruitAdapter 有四个方法

@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
}

@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}

@Override
public void onViewAttachedToWindow(@NonNull ViewHolder holder) {
super.onViewAttachedToWindow(holder);
}

@Override
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
}

Adapter中的 onAttachedToRecyclerView

​onAttachedToRecyclerView​​​ 、 ​​onDetachedFromRecyclerView​​​ 这两个方法是对于 ​​adapter​​​ 来说的,当 ​​adapter ​​​和 ​​recyclerView​​​ 进行绑定的时候会回调 ​​onAttachedToRecyclerView​

什么时候会回调 ​​onDetachedFromRecyclerView​​ ?

答案:recyclerView 已经有 adapter 了,又绑定了一个新的 adapter ,就会执行 ​​onDetachedFromRecyclerView​

对于一个 adapter 实例,​​onAttachedToRecyclerView​​​ 、 ​​onDetachedFromRecyclerView​​ 这两个方法只会回调一次。

源码分析:

​onAttachedToRecyclerView​​​ 、 ​​onDetachedFromRecyclerView​​​ 在 ​​RecyclerView​​ 都是空方法。

Android 探究RecyclerView onViewAttachedToWindow 触发时机_onViewDetached

首先看看 ​​RecyclerView​​​ 的 ​​setAdapter​​ 方法

Android 探究RecyclerView onViewAttachedToWindow 触发时机_onViewAttached_02

​setAdapter​​​ 方法里,调用 ​​setAdapterInternal​​ 方法

Android 探究RecyclerView onViewAttachedToWindow 触发时机_android_03

设置一个新的 ​​adapter​​​ , 如果 ​​recyclerView​​​ 已经绑定过 ​​adapter​​ 。

那么就对于老的​​adapter​​​调用 ​​onDetachedFromRecyclerView(this)​

新的 ​​adapter ​​​调用 ​​onAttachedToRecyclerView(this)​

Adapter中的 onViewAttachedToWindow

@Override
public void onViewAttachedToWindow(@NonNull ViewHolder holder) {
super.onViewAttachedToWindow(holder);
int position = holder.getLayoutPosition();
Log.d("yu--", "attach " + position);
}

@Override
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
int position = holder.getLayoutPosition();
Log.d("yu--", "detach " + position);
}

​RecyclerView ​​​本质上也是一个 ​​ViewGroup​​​,那么它的 ​​Item​​​ 要显示出来,自然需要 ​​addView() ​​​进来,移出的时候,当然也要 ​​removeView()​​​ 出去,所以对应的自然是 ​​onViewAttachedToWindow()​​​ 和 ​​onViewAttachedToWindow()​​ 了。

所以在特定场景下,可以通过这两个回调来解决少量 Item 移出屏幕,移进屏幕所需要的工作。为什么说特定场景下呢,由于假如调用了 ​​notifyDataSetChanged() ​​​ 方法的话,会触发当前在屏幕中的所有 Item 的 ​​onViewAttachedToWindow()​​ 。

当第一次 setAdapter 的时候,屏幕内所有 item 会调用 ​​onViewAttachedToWindow(ViewHolder holder) ​​ , 日志如下:

yu--: attach 0
yu--: attach 1
yu--: attach 2
yu--: attach 3
yu--: attach 4
yu--: attach 5
yu--: attach 6

调用 ​​notifyDataSetChanged​​​ ,屏幕类所有的 item 会先执行 ​​onViewDetachedFromWindow​​​ , 然后执行 ​​onViewAttachedToWindow​

yu--: detach 6
yu--: detach 5
yu--: detach 4
yu--: detach 3
yu--: detach 2
yu--: detach 1
yu--: detach 0
yu--: attach 0
yu--: attach 1
yu--: attach 2
yu--: attach 3
yu--: attach 4
yu--: attach 5
yu--: attach 6

综上,Adapter 的 ​​onViewAttachedToWindow​​ 适合做 item 曝光埋点,但是要注意,这个方法可能会执行多次。

瀑布流 StaggeredGridLayoutManager 设置第一个 item 占据一整行

@Override
public void onViewAttachedToWindow(@NonNull ViewHolder holder) {
super.onViewAttachedToWindow(holder);
int position = holder.getLayoutPosition();
StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
if (position == 0) {
//占据一整行
layoutParams.setFullSpan(true);
} else {
layoutParams.setFullSpan(false);
}
}

效果图:

Android 探究RecyclerView onViewAttachedToWindow 触发时机_android_04


举报

相关推荐

0 条评论