仿选股宝选个头条上下拉加载一页的功能

一、前言

最近有一个需求就是上拉、下拉整体刷新一页数据,并且将下一页数据整体划出显示,上一页数据则消失。这个类似于选股宝的头条选股的效果,先上图。
仿选股宝选个头条上下拉加载一页的功能

二、实现心路历程

拿到这个需求,我就想,我靠,没搞过啊,这个怎么弄!仔细看,这好像就是上拉刷新、下拉加载呀,怎么实现?套两层recycleView?里面负责显示,外面一层负责动画?添加header 和 footer?然后增加动画,还有阻尼效果?不行不行,实现时间太长,不允许!找找网上有没有实现好的开源代码,然后:“仿选股宝选股头条上拉加载”等等,都没有。那只能自己实现了,然后自己再细看这玩意,越看越像之前ListView时代的PullToRefresh,然后我就想,找一个这样的上下组件,然后这种入场方式就自己实现:先上拉,数据出来之后,记录新加载的第一条数据,然后从该条开始,向上或者向下移动。最后和大佬说这个方案的时候,他给我提意见:你这样就弄得整个recycleView的逻辑很复杂,你还不如利用Fragment显示,让它处理数据显示,动画,进出场,recycleView只负责列表显示。瞬间茅塞顿开。

三、上代码

上下拉加载,我就自己不造*了,有很多大牛写的很好,找了几个,最终选中了https://www.cnblogs.com/zhujiabin/p/7425535.html 这个,我对他的代码做了小小的改动,增加下拉加载时,没有数据,显示到顶了,类似于上拉没有数据的那种效果,具体修改思路按照代码中底部加载完成字段isMore 增加isPreviousData字段控制,代码就不贴了,比较简单,稍微改下就行。

3.1具体使用

3.1.1 用一个Activity承接Fragment

PullRefreshActivity.java

public class PullRefreshActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pull_refresh);
        BaseFragment fragment = new BaseFragment();
        BaseFragment fragment2 = new BaseFragment();
        getSupportFragmentManager()
               .beginTransaction()
                        .replace(R.id.fl_root, PullRefreshFragment.newInstance(it))
                        .setCustomAnimations(R.anim.fragment_top_in, R.anim.fragment_bottom_out, R.anim.fragment_bottom_in, R.anim.fragment_top_out)
                        .commit();
    }
}

其XML文件很简单,就一个FrameLayout,这里就不贴出了,说明一下,这个代码仅用于demo,具体项目中,这Activity得加载一次数据,在数据加载成功回调中切换Fragment,然后在Fragment中,Fragment代码如下:
PullRefreshFragment.kt

class PullRefreshFragment: Fragment() {
    private var list: PullRefreshRecyclerView? = null
    private var pageNum: Int = 0
    private var adapter: MyAdapter? = null
    private val data = ArrayList<String>()
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_six, container, false)
        list = view.findViewById<PullRefreshRecyclerView>(R.id.list)
        PullRefreshUtil.setRefresh(list as PullRefreshView, true, true)

        adapter = MyAdapter(activity, data)
        list?.setLayoutManager(LinearLayoutManager(activity))
        list?.setAdapter(adapter)
        loadData()
        list?.setOnPullDownRefreshListener {
            list?.isPreviousData(true)
            pageNum = 1
            loadData()
            list?.refreshFinish()
        }

        list?.setOnPullUpRefreshListener {
            pageNum++
            list?.isMore(true)
            loadData()
            list?.refreshFinish()
        }
list?.setOnHeadStateListener(object : PullRefreshView.OnHeadStateListener{
            override fun onRetractHead(head: View?) {
                activity?.supportFragmentManager?.beginTransaction()
                    ?..setCustomAnimations(R.anim.fragment_top_in, R.anim.fragment_bottom_out, R.anim.fragment_bottom_in, R.anim.fragment_top_out)
                    ?.replace(R.id.fl_root, PullRefreshFragment(), TAG2)?.commit()
                (head as HeadView).onRetractHead(head)
            }

            override fun onRefreshHead(head: View?) {
                (head as HeadView).onRefreshHead(head)
            }

            override fun onScrollChange(head: View?, scrollOffset: Int, scrollRatio: Int) {
                (head as HeadView).onScrollChange(head, scrollOffset, scrollRatio)
            }

            override fun onNotMore(head: View?) {
                (head as HeadView).onNotMore(head)
            }

            override fun onHasMore(head: View?) {
                (head as HeadView).onHasMore(head)
            }
        })
       //以下两个监听,为了切换Fragment,实际开发中我们不需要这两个监听,我们只要在数据加载成功回调中处理即可 ,下面会有实际中的运用的代码
        list?.setOnTailStateListener(object : PullRefreshView.OnTailStateListener{
            override fun onScrollChange(tail: View?, scrollOffset: Int, scrollRatio: Int) {
                (tail as TailView).onScrollChange(tail, scrollOffset, scrollRatio)
            }

            override fun onRefreshTail(tail: View?) {
                (tail as TailView).onRefreshTail(tail)
            }

            override fun onRetractTail(tail: View?) {
                activity?.supportFragmentManager?.beginTransaction()
                    ?..setCustomAnimations(R.anim.fragment_top_in, R.anim.fragment_bottom_out, R.anim.fragment_bottom_in, R.anim.fragment_top_out)
                    ?.replace(R.id.fl_root, PullRefreshFragment())?.commit()
                (tail as TailView).onRetractTail(tail)
            }

            override fun onNotMore(tail: View?) {
                (tail as TailView).onNotMore(tail)
            }

            override fun onHasMore(tail: View?) {
                (tail as TailView).onHasMore(tail)
            }
        })


        return view
    }

    private fun loadData() {
        for (i in 0..14) {
            data.add(i.toString() + "")
        }
        adapter?.setData(data)
        adapter?.notifyDataSetChanged()
    }

    var mOnLoadSuccess: onl oadSuccess? = null
    fun setOnLoadSucess(onLoadSuccess: onl oadSuccess) {
        this.mOnLoadSuccess = onl oadSuccess
    }
    interface onl oadSuccess {
        fun loadSuccess()
    }
}

接上实际开发中的代码:

mViewModel.headLineData.observe(this, Observer {
            var enter: Int?
            var exit: Int?
            var popEnter: Int?
            var popExit: Int?
            if (isUp) {
                enter = R.anim.fragment_bottom_in
                exit = R.anim.fragment_top_out
                popEnter = R.anim.fragment_top_in
                popExit = R.anim.fragment_bottom_out
            } else {
                enter = R.anim.fragment_top_in
                exit = R.anim.fragment_bottom_out
                popEnter = R.anim.fragment_bottom_in
                popExit = R.anim.fragment_top_out
            }
            mBinding?.recycleView?.refreshFinish()
            activity?.supportFragmentManager?.beginTransaction()
                    ?.setCustomAnimations(enter, exit, popEnter, popExit)
                    ?.replace(R.id.fl_root, newInstance(it))?.commit()
        })

MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private List<String> mData;
    private Context mContext;

    public MyAdapter(Context context, ArrayList<String> dataList) {
        this.mContext = context;
        this.mData = dataList;
    }

    public void setData(List<String> data) {
       this.mData = data;
    }

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

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.tv.setText(mData.get(position));
    }

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

    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView tv;

        public ViewHolder(View itemView) {
            super(itemView);
            tv = itemView.findViewById(R.id.tv);
        }
    }
}

其中item_adapter.xml文件很简单,只有一个TextView,这里就不贴出来了,另外Fragment进出场的四个动画如下:
fragment_bottom_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
            android:fromYDelta="100%p"
            android:toYDelta="0%p"
            android:duration="300"/>
    <alpha
            android:fromAlpha="0.5"
            android:toAlpha="1.0"
            android:duration="300"/>
</set>

fragment_top_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
            android:fromYDelta="0%p"
            android:toYDelta="-100%p"
            android:duration="300"/>
    <alpha
            android:fromAlpha="1.0"
            android:toAlpha="0.5"
            android:duration="300"/>
</set>

fragment_top_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
            android:fromYDelta="-100%p"
            android:toYDelta="0%p"
            android:duration="300"/>
    <alpha
            android:fromAlpha="0.5"
            android:toAlpha="1.0"
            android:duration="300"/>
</set>

fragment_bottom_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
            android:fromYDelta="0%p"
            android:toYDelta="100%p"
            android:duration="300"/>
    <alpha
            android:fromAlpha="1.0"
            android:toAlpha="0.5"
            android:duration="300"/>
</set>

具体效果如下:
仿选股宝选个头条上下拉加载一页的功能

上一篇:CF1498-B.Box Fitting


下一篇:[CF1498B]Box Fitting