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;
    }
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值