viewpager2 动态刷新效果
viewpager2具体使用和引入依赖就不再多说了,现在想实现一个功能,怎么动态更改fragment的顺序,毕竟有些产品会提出tab可以拖动变化,那么fragment也得跟着tab的顺序重新排序。
如果想做到这些,就要先了解到viewpager2实现的adapter
先了解一下FragmentStateAdapter:
默认的实现方法有getItemCount\createFragment,这两个方法
getItemCount就不说了
createFragment:
意思就是
1、Fragment将用于显示item
2、当Fragment离视口太远时,就是切换到老前面了,它将被销毁,其状态将被保存。当item再次靠近视口时,将请求一个新的Fragment,并使用以前保存的状态来初始化它。recycler的
相当于做了recyclerview的复用加载
另外还有两个方法介绍一下:
1、默认实现适用于不添加、移动和移除项的集合,在这里可以进行移动的操作
2、重写时,还要重写containsItem(long)
3、如果该项不是集合的一部分,则返回RecyclerView.NO_标识
1、默认实现适用于不添加、移动和移除项的集合
2、重写时,还要重写getItemId(int)
按照我个人的理解,就是每个fragment需要有一个独立的标识,通过这个标识来进行移动替换和删除操作,
getItemId就是作为获取唯一标识的方法,FragmentStateAdapter类的源码里,实际上是处于recyclerview里的obBindVIewHolder的方法里面
@Override
public final void onBindViewHolder(final @NonNull FragmentViewHolder holder, int position) {
final long itemId = holder.getItemId();
final int viewHolderId = holder.getContainer().getId();
final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
if (boundItemId != null && boundItemId != itemId) {
removeFragment(boundItemId);
mItemIdToViewHolder.remove(boundItemId);
}
mItemIdToViewHolder.put(itemId, viewHolderId); // this might overwrite an existing entry
ensureFragment(position);
/** Special case when {@link RecyclerView} decides to keep the {@link container}
* attached to the window, but not to the view hierarchy (i.e. parent is null) */
final FrameLayout container = holder.getContainer();
if (ViewCompat.isAttachedToWindow(container)) {
if (container.getParent() != null) {
throw new IllegalStateException("Design assumption violated.");
}
container.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (container.getParent() != null) {
container.removeOnLayoutChangeListener(this);
placeFragmentInViewHolder(holder);
}
}
});
}
here
gcFragments();
}
这里就明显是为了获取一个itemId,用于对比是不是在列表已然存在,其实再往上走就会发现他是recyclerview里面的getItem,就是将fragment自定义了一个标识,默认的是下标自然不行,所以这里要自定义。
然后再配合containsItem来进行增删的标识判断,相当于包了一层。
private void ensureFragment(int position) {
///here/
long itemId = getItemId(position);
if (!mFragments.containsKey(itemId)) {
// TODO(133419201): check if a Fragment provided here is a new Fragment
Fragment newFragment = createFragment(position);
newFragment.setInitialSavedState(mSavedStates.get(itemId));
mFragments.put(itemId, newFragment);
}
}
然后看看containsItem在源码里做了什么:节选一个removeFragment的方法,这个方法很明显是为了删除已有的fragment的。containsItem就作为了是否已被移除的标准,如果里面不存在了标识,那么就会被删除
private void removeFragment(long itemId) {
Fragment fragment = mFragments.get(itemId);
if (fragment == null) {
return;
}
if (fragment.getView() != null) {
ViewParent viewParent = fragment.getView().getParent();
if (viewParent != null) {
((FrameLayout) viewParent).removeAllViews();
}
}
if (!containsItem(itemId)) {
mSavedStates.remove(itemId);
}
if (!fragment.isAdded()) {
mFragments.remove(itemId);
return;
}
if (shouldDelayFragmentTransactions()) {
mHasStaleFragments = true;
return;
}
if (fragment.isAdded() && containsItem(itemId)) {
mSavedStates.put(itemId, mFragmentManager.saveFragmentInstanceState(fragment));
}
mFragmentManager.beginTransaction().remove(fragment).commitNow();
mFragments.remove(itemId);
}
实际上在创建的时候就调用了这个方法
@Override
public final void onBindViewHolder(final @NonNull FragmentViewHolder holder, int position) {
final long itemId = holder.getItemId();
final int viewHolderId = holder.getContainer().getId();
final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
if (boundItemId != null && boundItemId != itemId) {
removeFragment(boundItemId);
mItemIdToViewHolder.remove(boundItemId);
}
mItemIdToViewHolder.put(itemId, viewHolderId); // this might overwrite an existing entry
ensureFragment(position);
/** Special case when {@link RecyclerView} decides to keep the {@link container}
* attached to the window, but not to the view hierarchy (i.e. parent is null) */
final FrameLayout container = holder.getContainer();
if (ViewCompat.isAttachedToWindow(container)) {
if (container.getParent() != null) {
throw new IllegalStateException("Design assumption violated.");
}
container.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (container.getParent() != null) {
container.removeOnLayoutChangeListener(this);
placeFragmentInViewHolder(holder);
}
}
});
}
gcFragments();
}
下面就根据这俩特性开始研究一下实现。这个博客是我借鉴的一篇,但是只有代码
https://zhuanlan.zhihu.com/p/105700960
然后上自己的代码
public class ViewPager2Adapter extends FragmentStateAdapter {
private List<Fragment> fragmentList;
private List<Long> fragmentIds = new ArrayList<>();//用于存储更新fragment的特定标识
private HashSet<Long> creatIds = new HashSet<>();//得用hashset防重,用于存储adapter内的顺序
public ViewPager2Adapter(@NonNull FragmentActivity fragmentActivity, List<Fragment> fragmentList) {
super(fragmentActivity);
this.fragmentList = fragmentList;
update(fragmentList);
}
public void update(List<Fragment> fragmentLists) {
fragmentIds.clear();
for (int i = 0; i< fragmentLists.size(); i++){
fragmentIds.add(((ViewPagerFragment)fragmentLists.get(i)).getTypeId());
}
}
@NonNull
@Override
public Fragment createFragment(int position) {
Long ids = fragmentIds.get(position);
creatIds.add(ids);//创建的时候将未添加的fragment添加进来,每次刷新都会调用这里,其次调用containsItem
return fragmentList.get(position);
}
@Override
public int getItemCount() {
return fragmentList.size();
}
/**
* 这两个方法必须重写,作为数据改变刷新检测的工具
* @param position
* @return
*/
@Override
public long getItemId(int position) {
return fragmentIds.get(position);
}
@Override
public boolean containsItem(long itemId) {
return creatIds.contains(itemId);
}
}
下面这里简单用了一下notifyDataSetChanged的方法,这个更新不过就是利用了recyclerview的更新操作,实现思路在这里,具体优化具体分析
public class ViewPagerActivity extends AppCompatActivity {
private List<Fragment> fragmentList = new ArrayList<>();
private ViewPager2Adapter adapter;
private ViewPagerFragment viewPagerFragment1;
private ViewPagerFragment viewPagerFragment2;
private ViewPagerFragment viewPagerFragment3;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewpager2_view);
initView();
}
private void initView() {
viewPagerFragment1 = new ViewPagerFragment(1l).newInstance(1l);
viewPagerFragment2 = new ViewPagerFragment(2l).newInstance(2l);
viewPagerFragment3 = new ViewPagerFragment(3l).newInstance(3l);
fragmentList.add(viewPagerFragment1);
fragmentList.add(viewPagerFragment2);
fragmentList.add(viewPagerFragment3);
ViewPager2 viewPager2 = findViewById(R.id.viewpager);
Button change = findViewById(R.id.change);
adapter = new ViewPager2Adapter(this, fragmentList);
viewPager2.setAdapter(adapter);
change.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Collections.shuffle(fragmentList);//随机排序
adapter.update(fragmentList);
// adapter.notifyItemRemoved(0);//删除操作
adapter.notifyDataSetChanged();
}
});
}
}
public class ViewPagerFragment extends Fragment {
private long key;
public ViewPagerFragment(long l) {
this.key = l;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View inflate = inflater.inflate(R.layout.viewpagerfragment_layout, container);
return inflate;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
key = getArguments().getLong("key");
TextView textview = view.findViewById(R.id.textview);
textview.setText("我是"+key);
}
public long getTypeId() {
return key;
}
public ViewPagerFragment newInstance(long l) {
ViewPagerFragment viewPagerFragment = new ViewPagerFragment(l);
Bundle bundle = new Bundle();
bundle.putLong("key", l);
viewPagerFragment.setArguments(bundle);
return viewPagerFragment;
}
}
本文地址:https://blog.csdn.net/czZ__czZ/article/details/110226631