【Android】RecyclerView详解(二)


上篇文章介绍了RecyclerView的基本使用,今天我们来着重实践一下RecyclerView瀑布流的效果以及上拉加载的功能;如果您对RecyclerView还不太了解的话请先阅读下上篇文章【Android】RecyclerView详解(一)

首先我们要实现下面这个效果:

这里写图片描述

1.普通瀑布流

  • 开始之前先添加依赖库
    compile 'com.android.support:recyclerview-v7:23.4.0'
    compile 'com.android.support:cardview-v7:23.4.0'
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.squareup.picasso:picasso:2.5.2'
  • activity_main.xml文件代码问下,很简单就一个RecyclerView组件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</RelativeLayout>
  • item.xml文件代码问下,添加了一个ImageView组件,外层用cardview包裹
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    app:cardBackgroundColor="@android:color/white"
    app:cardCornerRadius="5dp"
    app:cardElevation="5dp"
    app:contentPadding="10dp" >

    <ImageView
        android:id="@+id/item_image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scaleType="fitXY"
        />

</android.support.v7.widget.CardView>
  • 添加适配器Adapter
 class MyAdapter extends RecyclerView.Adapter<ViewHolder>
    {
        public List<String> datas = null;

        public MyAdapter(List<String> datas)
        {
            this.datas = datas;
        }

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

        @Override
        public void onBindViewHolder(ViewHolder viewHolder, int i)
        {
            //加载图片
            ImageLoader.getInstance().displayImage(datas.get(i), ((MyViewHolder)viewHolder).image);
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position)
        {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
            ViewHolder vh = new MyViewHolder(view);
            return vh;
        }

    }

    public static class MyViewHolder extends RecyclerView.ViewHolder
    {

        public ImageView image;

        public MyViewHolder(View itemView)
        {
            super(itemView);
            image = (ImageView)itemView.findViewById(R.id.item_image);
        }

    }
  • activity中控制显示,至此,简单的瀑布流效果也就完成了,效果图如上图;
recyclerView = (RecyclerView)findViewById(R.id.rv_view);
        // 设置瀑布流布局
        StaggeredGridLayoutManager stagManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(stagManager);
        //模拟数据
        for (int i = 0; i < ImageURL.length; i++)
        {
            datas.add(ImageURL[i]);
        }
        MyAdapter adapter = new MyAdapter(datas);
        recyclerView.setAdapter(adapter);

2.上拉加载

当然这可能无法满足项目使用,一般都会结合上拉和下拉一起使用,关于下拉刷新这里不作介绍,比较简单,这里重点介绍一下RecyclerView各种layoutManager上拉加载功能,此处参考_区长的实现思路,略作改动,下面一起来看下如何实现的吧!

  • 添加上拉加载接口回调OnLoadMoreListener
public interface OnLoadMoreListener<T> {
    /**
     * 加载更多前回调,比如显示Footer的操作
     */
    void onStart();

    /**
     * 加载更多业务处理,如网络请求数据
     */
    void onLoadMore();

    /**
     * 由于onLoadMore可能是异步调用的,所以onFinish需要手动调用,完成数据的刷新,隐藏Footer等
     * @param list onLoadMore中返回的数据
     */
    void onFinish(List<T> list);
}


- 实现上拉加载主要还是要为RecyclerView添加头和尾,目前普遍的方式是根据 getItemViewType方法,依据返回的type类型来加载不同的布局来实现;


  @Override
    public int getItemViewType(int type)


  • 至于RecyclerView何时出发上拉加载操作,Recyclerview给我们提供了OnScrollListener监听,我们可以监听当前显示item的最后一项显示的时候来出发上拉加载操作;
public abstract class OnRecyclerViewScrollListener<T extends String> extends RecyclerView.OnScrollListener implements OnLoadMoreListener<T> {

    public static enum layoutManagerType {
        LINEAR_LAYOUT,
        GRID_LAYOUT,
        STAGGERED_GRID_LAYOUT
    }

    protected layoutManagerType mLayoutManagerType;
    private boolean mIsLoadingMore = false;
    public boolean isLoadingMore() {
        return mIsLoadingMore;
    }
    public void setLoadingMore(boolean loadingMore) {
        mIsLoadingMore = loadingMore;
    }



    private int[] lastPositions;
    private int lastVisibleItemPosition;
    private int currentScrollState = 0;

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        //获取当前layoutManager类型
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        //指定当前layoutManager类型
        if (mLayoutManagerType == null) {
            if (layoutManager instanceof LinearLayoutManager) {
                mLayoutManagerType = layoutManagerType.LINEAR_LAYOUT;
            } else if (layoutManager instanceof GridLayoutManager) {
                mLayoutManagerType = layoutManagerType.GRID_LAYOUT;
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                mLayoutManagerType = layoutManagerType.STAGGERED_GRID_LAYOUT;
            } else {
                throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager");
            }
        }

        switch (mLayoutManagerType) {
            case LINEAR_LAYOUT:
                //找到当前显示的所有item的最后一项
                lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
                break;
            case GRID_LAYOUT:
                //找到当前显示的所有item的最后一项
                lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
                break;
            case STAGGERED_GRID_LAYOUT:
                //找到当前显示的所有item的最后一项
                StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
                if (lastPositions == null) {
                    lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
                }
                staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
                lastVisibleItemPosition = findMax(lastPositions);
                break;
            default:
                break;
        }
    }

    /**
     * recyclerView滑动状态改变时调用此方法
     * @param recyclerView
     * @param newState
     */
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        currentScrollState = newState;
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        int visibleItemCount = layoutManager.getChildCount();
        int totalItemCount = layoutManager.getItemCount();
        if (visibleItemCount > 0 && currentScrollState == RecyclerView.SCROLL_STATE_IDLE
                && lastVisibleItemPosition >= totalItemCount - 1) {
            if (!isLoadingMore()){
                mIsLoadingMore =true;
                onStart();
                onLoadMore();
            }
        }
    }

    private int findMax(int[] lastPositions) {
        int max = lastPositions[0];
        for (int value : lastPositions) {
            if (value > max) {
                max = value;
            }
        }
        return max;
    }
}


  • 处理的关键还是适配器的处理,下面看下区长封装的Adapter基类
public abstract class RecyclerViewAdapter<T extends String> extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{

    /**
     * 不同的布局类型,0代表头部,1代表尾部,2代表普通布局
     */
    public interface Item
    {
        int TYPE_HEADER = 0;
        int TYPE_FOOTER = 1;
        int TYPE_NORMAL = 2;
    }

    //数据源
    private List<T> list = null;

    //头部layout资源id
    private int headerViewRes;

    //尾部layout资源id
    private int footerViewRes;

    //当前是否有头
    private boolean hasHeader = false;

    //当前是否有尾
    private boolean hasFooter = false;

    public List<T> getList()
    {
        return list;
    }

    public void setList(List<T> list)
    {
        this.list = list;
    }

    /**
     * 判断position位置的item是否是头head
     * @param position
     * @return
     */
    public boolean isHeader(int position)
    {
        return hasHeader() && position == 0;
    }

    /**
     * 判断position位置的item是否是尾foot
     * @param position
     * @return
     */
    public boolean isFooter(int position)
    {
        if (hasHeader())
        {
            return hasFooter() && position == list.size() + 1;
        } else
        {
            return hasFooter() && position == list.size();
        }
    }

    public boolean hasHeader()
    {
        return hasHeader;
    }

    public boolean hasFooter()
    {
        return hasFooter;
    }


    public int getHeaderView()
    {
        return headerViewRes;
    }

    public int getFooterView()
    {
        return footerViewRes;
    }

    /**
     * 设置头部layout布局文件
     *
     * @param headerViewRes
     */
    public void setHeaderView(int headerViewRes)
    {
        if (headerViewRes != 0)
        {
            if (!hasHeader())
            {
                this.headerViewRes = headerViewRes;
                this.hasHeader = true;
                notifyItemInserted(0);
            } else
            {
                this.headerViewRes = headerViewRes;
                notifyDataSetChanged();
            }
        } else
        {
            if (hasHeader())
            {
                this.hasHeader = false;
                notifyItemRemoved(0);
            }
        }
    }

    /**
     * 设置尾部layout布局文件
     *
     * @param footerViewRes
     */
    public void setFooterView(int footerViewRes)
    {
        if (footerViewRes != 0)
        {
            if (!hasFooter())
            {
                this.footerViewRes = footerViewRes;
                this.hasFooter = true;
                if (hasHeader())
                {
                    notifyItemInserted(list.size() + 1);
                } else
                {
                    notifyItemInserted(list.size());
                }
            } else
            {
                this.footerViewRes = footerViewRes;
                notifyDataSetChanged();
            }

        } else
        {
            if (hasFooter())
            {
                this.hasFooter = false;
                if (hasHeader())
                {
                    notifyItemRemoved(list.size() + 1);
                } else
                {
                    notifyItemRemoved(list.size());
                }
            }
        }
    }

    /**
     * 构造函数
     * @param list
     */
    public RecyclerViewAdapter(List<T> list) {
        this.list = list;
    }

    /**
     * 构造函数
     * @param list
     * @param headerViewRes
     */
    public RecyclerViewAdapter(List<T> list, int headerViewRes) {
        this.list = list;
        setHeaderView(headerViewRes);
    }

    /**
     * 构造函数
     * @param list
     * @param headerViewRes
     * @param footerViewRes
     */
    public RecyclerViewAdapter(List<T> list, int headerViewRes,int footerViewRes)
    {
        this.list = list;
        setHeaderView(headerViewRes);
        setFooterView(footerViewRes);
    }


    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        /**
         * 根据不同的类型加载不同的ViewHolder
         */
        if (hasHeader() && viewType == Item.TYPE_HEADER) {
            View v = LayoutInflater.from(parent.getContext()).inflate(getHeaderView(), parent, false);
            return new HeaderViewHolder(v);
        } else if (hasFooter() && viewType == Item.TYPE_FOOTER) {
            View v = LayoutInflater.from(parent.getContext()).inflate(getFooterView(), parent, false);
            return new FooterViewHolder(v);
        } else {
            return onCreateHolder(parent, viewType);
        }
    }

    /*以下是抽象出来的公共方法,用于实现普通布局的处理*/

    public abstract RecyclerView.ViewHolder onCreateHolder(ViewGroup parent, int viewType);

    protected abstract void onBindHeaderView(View headerView);

    protected abstract void onBindFooterView(View footerView);

    protected abstract void onBindItemView(RecyclerView.ViewHolder holder, T item);

    /*抽象结束*/

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
    {
        /**
         * 根据类型来绑定数据
         */

        if (getItemViewType(position) == Item.TYPE_HEADER) {
            HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
            View headerView = headerHolder.itemView;

            onBindHeaderView(headerView);
        } else if (getItemViewType(position) == Item.TYPE_FOOTER) {
            FooterViewHolder footerHolder = (FooterViewHolder) holder;
            View footerView = footerHolder.itemView;

            onBindFooterView(footerView);
        } else {
            T i = getItemByPosition(position);
            onBindItemView(holder, i);

        }
    }

    /**
     * 返回item数量
     * @return
     */
    @Override
    public int getItemCount() {
        int count = 0;
        count += (hasHeader() ? 1 : 0);
        count += (hasFooter() ? 1 : 0);
        count += list.size();
        return count;
    }

    /**
     * 获取position位置的数据值
     * @param position
     * @return
     */
    protected T getItemByPosition(int position) {
        if (hasHeader()) {
            return list.get(position - 1);
        } else {
            return list.get(position);
        }
    }


    @Override
    public int getItemViewType(int position)
    {
        int size = list.size();
        if (hasHeader()) {
            //包含头部并且position为0时返回Header类型
            if (position == 0) {
                return Item.TYPE_HEADER;
            } else {
                if (position == size + 1) {
                    return Item.TYPE_FOOTER;
                } else {
                    return Item.TYPE_NORMAL;
                }
            }

        } else {
            if (position == size) {
                return Item.TYPE_FOOTER;
            } else {
                return Item.TYPE_NORMAL;
            }
        }
    }

    /**
     * 头部viewhodler
     */
    static class HeaderViewHolder extends RecyclerView.ViewHolder {
        public HeaderViewHolder(View itemView) {
            super(itemView);
        }
    }

    /**
     * 尾部viewhodler
     */
    static class FooterViewHolder extends RecyclerView.ViewHolder {
        public FooterViewHolder(View itemView) {
            super(itemView);
        }
    }

}
  • 我们在使用时只需要继承RecyclerViewAdapter,来实现自己需要的adapter,下面我们分别来实现一下,看看效果;

  • LinearLayoutAdapter

public abstract class LinearLayoutAdapter extends RecyclerViewAdapter
{

    public LinearLayoutAdapter(List list)
    {
        super(list);
    }

    public LinearLayoutAdapter(List list, int headerViewRes)
    {
        super(list, headerViewRes);
    }

    public LinearLayoutAdapter(List list, int headerViewRes, int footerViewRes)
    {
        super(list, headerViewRes, footerViewRes);
    }
}


  • MyLinearAdapter继承LinearLayoutAdapter进行细节处理
public class MyLinearAdapter extends LinearLayoutAdapter
{
    public MyLinearAdapter(List list)
    {
        super(list);
    }

    public MyLinearAdapter(List list, int headerViewRes)
    {
        super(list, headerViewRes);
    }

    public MyLinearAdapter(List list, int headerViewRes, int footerViewRes)
    {
        super(list, headerViewRes, footerViewRes);
    }

    @Override
    public RecyclerView.ViewHolder onCreateHolder(ViewGroup parent, int viewType)
    {
        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
        return new ItemViewHolder(view);
    }

    @Override
    protected void onBindHeaderView(View headerView)
    {
        //头部绑定后进行一些处理操作
    }

    @Override
    protected void onBindFooterView(View footerView)
    {
        //尾部绑定后进行一些处理操作
    }

    @Override
    protected void onBindItemView(RecyclerView.ViewHolder holder, String item)
    {
        ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
        Picasso.with(holder.itemView.getContext()).load(item).into(itemViewHolder.icon);
    }

    static class ItemViewHolder extends RecyclerView.ViewHolder {
        ImageView icon;
        public ItemViewHolder(View itemView) {
            super(itemView);
            icon = (ImageView) itemView.findViewById(R.id.item_image);
        }
    }
}
  • Activity中设置相关配置
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
adapter =  new MyLinearAdapter(datas);
recyclerView.setAdapter(adapter);

recyclerView.addOnScrollListener(new OnRecyclerViewScrollListener<String>(){
            @Override
            public void onStart() {
                adapter.setFooterView(R.layout.footer);
                if (adapter.hasHeader()){
                    recyclerView.smoothScrollToPosition(adapter.getItemCount()+1);
                }else{
                    recyclerView.smoothScrollToPosition(adapter.getItemCount());
                }
            }

            @Override
            public void onLoadMore() {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(3000);
                            //手动调用onFinish()
                            onFinish(arrayList);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }

            @Override
            public void onFinish(List<String> contents) {
                Message message=Message.obtain();
                message.obj=contents;
                handler.sendMessage(message);
                setLoadingMore(false);
            }
        });
  • handler中更新界面
private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg)
        {
            List<String> list= (List<String>) msg.obj;
            adapter.getList().addAll(list);
            adapter.notifyDataSetChanged();
            adapter.setFooterView(0);
        }
    };
  • 效果图如下:

这里写图片描述

  • GridLayoutAdapter如下:
public abstract class GridLayoutAdapter<T extends String> extends RecyclerViewAdapter {

    public GridLayoutAdapter(List list) {
        super(list);
    }
    public GridLayoutAdapter(List list, int headerViewRes) {
        super(list, headerViewRes);
    }

    public GridLayoutAdapter(List list, int headerViewRes, int footerViewRes) {
        super(list, headerViewRes, footerViewRes);
    }

    private GridSpanSizeLookup mGridSpanSizeLookup;
    private GridLayoutManager gridManager;
    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if (manager instanceof GridLayoutManager) {
            gridManager = ((GridLayoutManager) manager);
            if (mGridSpanSizeLookup == null) {
                mGridSpanSizeLookup = new GridSpanSizeLookup();
            }
            gridManager.setSpanSizeLookup(mGridSpanSizeLookup);
        }
    }

    class GridSpanSizeLookup extends GridLayoutManager.SpanSizeLookup {
        @Override
        public int getSpanSize(int position) {
            if (isHeader(position) || isFooter(position)) {
                //包含头部和尾部时合并成一行显示,getSpanCount代表列数
                return gridManager.getSpanCount();
            }
            return 1;
        }
    }


}
  • MyGridAdapter继承GridlayoutAdapter进行细节处理:
public class MyGridAdapter extends GridLayoutAdapter{


    public MyGridAdapter(List<String> list, int headerViewRes) {
        super(list, headerViewRes);
    }

    public MyGridAdapter(List<String> list) {
        super(list);
    }

    public MyGridAdapter(List<String> list, int headerViewRes, int footerViewRes) {
        super(list, headerViewRes, footerViewRes);
    }

    @Override
    public RecyclerView.ViewHolder onCreateHolder(ViewGroup parent, int viewType) {
        View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
        return new ItemViewHolder(view);
    }

    @Override
    protected void onBindHeaderView(View headerView) {
        //这是HeadView数据绑定的过程
    }

    @Override
    protected void onBindFooterView(View footerView) {
        //这是FootView数据绑定的过程
    }

    @Override
    protected void onBindItemView(RecyclerView.ViewHolder holder, String item)
    {
        ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
        Picasso.with(holder.itemView.getContext()).load(item).into(itemViewHolder.icon);
    }

    static class ItemViewHolder extends RecyclerView.ViewHolder {
        ImageView icon;
        public ItemViewHolder(View itemView) {
            super(itemView);
            icon = (ImageView) itemView.findViewById(R.id.item_image);
        }
    }
}
  • 效果图:

这里写图片描述

  • 瀑布流adapterGridLayoutAdapter如下:
public abstract class StaggeredGridLayoutAdapter<T extends String> extends RecyclerViewAdapter<T> {
    public StaggeredGridLayoutAdapter(List<T> list) {
        super(list);
    }

    public StaggeredGridLayoutAdapter(List<T> list, int headerViewRes) {
        super(list, headerViewRes);
    }

    public StaggeredGridLayoutAdapter(List<T> list, int headerViewRes, int footerViewRes) {
        super(list, headerViewRes, footerViewRes);
    }

    @Override
    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
        super.onViewAttachedToWindow(holder);
        if (isStaggeredGridLayout(holder)) {
            handleLayoutIfStaggeredGridLayout(holder, holder.getLayoutPosition());
        }
    }

    private boolean isStaggeredGridLayout(RecyclerView.ViewHolder holder) {
        ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        if (layoutParams != null && layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) {
            return true;
        }
        return false;
    }

    protected void handleLayoutIfStaggeredGridLayout(RecyclerView.ViewHolder holder, int position) {
        if (isHeader(position) || isFooter(position)) {
            StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
            //有头尾时合并一列显示
            p.setFullSpan(true);
        }
    }
}
  • MyRecAdapter 继承StaggeredGridLayoutAdapter进行细节处理:
public class MyRecAdapter extends StaggeredGridLayoutAdapter{


    public MyRecAdapter(List<String> list, int headerViewRes) {
        super(list, headerViewRes);
    }

    public MyRecAdapter(List<String> list) {
        super(list);
    }

    public MyRecAdapter(List<String> list, int headerViewRes, int footerViewRes) {
        super(list, headerViewRes, footerViewRes);
    }

    @Override
    public RecyclerView.ViewHolder onCreateHolder(ViewGroup parent, int viewType) {
        View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
        return new ItemViewHolder(view);
    }

    @Override
    protected void onBindHeaderView(View headerView) {
        //这是HeadView数据绑定的过程
    }

    @Override
    protected void onBindFooterView(View footerView) {
        //这是FootView数据绑定的过程
    }

    @Override
    protected void onBindItemView(RecyclerView.ViewHolder holder, String item)
    {
        ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
        Picasso.with(holder.itemView.getContext()).load(item).into(itemViewHolder.icon);
    }

    static class ItemViewHolder extends RecyclerView.ViewHolder {
        ImageView icon;
        public ItemViewHolder(View itemView) {
            super(itemView);
            icon = (ImageView) itemView.findViewById(R.id.item_image);
        }
    }
}
  • 效果图:

这里写图片描述




记录下学习过程,希望能帮到您!源码下载




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值