Android RecyclerView使用详解

简介

RecyclerView是Google在android-supportv7包中推出的一个新的控件,该控件的主要作用是用于替代ListView、GridView,相比较于这两种控件RecyclerView有以下有点:

  • RecyclerView内部封装了ViewHolder,更加方便Item的复用
  • 提供了一种插拔式的体验,高度解耦,异常灵活,增加控件的扩展性。

RecyclerView的灵活性可以体现在以下几点

  • 通过RecyclerView.setLayoutManager()方法添加布局管理器
  • 通过RecyclerView.addItemDecoration()方法添加item分割线
  • 通过RecyclerView.setItemAnimator()方法添加item增删动画

LayoutManager布局管理器介绍

  • LinearLayoutManager:线性布局
  • GridLayoutManager:网格布局
  • StaggeredGridLayoutManager:瀑布流布局

RecyclerView使用

导入类库

  • 对于使用eclipse开发用户,需要导入sdk/extras/android/support/v7目录下recyclerview类库
  • 对于Android studio开发用户,直接通过修改build.gradle添加类库即可

通过RecyclerView实现ListView:

效果如下:
这里写图片描述

实现代码如下:

package com.example.zhangke.recyclerviewdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private ArrayList<String> mDatas;
    private RecyclerViewAdapter mAdapter;
    private StaggeredGridLayoutAdapter mStagAdapter;
    private RecyclerView.ItemDecoration mItemDecoration;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerView = (RecyclerView) findViewById(R.id.recycleview);
        initData();

        //垂直listview
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        //设置分割线
        mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
        mRecyclerView.addItemDecoration(mItemDecoration);
        // 设置增删动画
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        mAdapter = new RecyclerViewAdapter(this, mDatas);

        //设置点击事件
        mAdapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "onItemClick " + position, Toast.LENGTH_SHORT).show();
            }
        });

        mAdapter.setOnLongItemClickListener(new RecyclerViewAdapter.OnLongItemClickListener() {
            @Override
            public void onLongItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "onLongItemClick " + position, Toast.LENGTH_SHORT).show();
            }
        });

        //设置Adapter
        mRecyclerView.setAdapter(mAdapter);

    }

    /**
     * 初始化数据
     */
    private void initData() {
        mDatas = new ArrayList<String>();
        for (int i = 0; i < 100; i++) {
            mDatas.add("item " + i);
        }

    }


    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case R.id.add:
                mAdapter.addItem(1, "item" + 1);
                break;
            case R.id.remove:
                mAdapter.removeItem(1);
                break;

        }
        return true;
    }
}

RecyclerView.Adapter说明

同ListView一样,RecyclerView现实数据需要通过Adapter实现,Google已给我们提供了RecyclerView.Adapter的内部抽象类,所以必须通过继承该类实现一个Adapter。继承该类需要复写三个方法:

  1. public int getItemCount():获得item数目
  2. public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType):创建一个ViewHolder
  3. public void onBindViewHolder(final RecyclerViewHolder holder, int position):绑定Viewholder即对Item进行具体操作

注意:由于Adapter会在内部封装一个ViewHolder,所以我们还要自定义一个ViewHolder。

RecyclerView item点击事件

对于RecyviewView,系统并没有提供item点击事件,所以在自定义Adapter时,通常会自己定义Item的点击事件的接口以实现item点击。

具体代码如下:

package com.example.zhangke.recyclerviewdemo;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.text.Layout;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;

/**
 * Created by zhangke on 16/6/15.
 */
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {


    private Context mContext;
    private ArrayList<String> mDatas;
    private OnItemClickListener mOnItemClickListener;
    private OnLongItemClickListener mOnLongItemClickListener;

    public RecyclerViewAdapter(Context context, ArrayList<String> datas) {
        this.mContext = context;
        this.mDatas = datas;
    }


    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        RecyclerViewHolder holder = new RecyclerViewHolder(View.inflate(mContext, R.layout.item, null));
        return holder;
    }

    @Override
    public void onBindViewHolder(final RecyclerViewHolder holder, int position) {
        holder.mTextView.setText(mDatas.get(position));
        if (mOnItemClickListener != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int layoutPosition = holder.getLayoutPosition();
                    mOnItemClickListener.onItemClick(v, layoutPosition);
                }
            });
        }
        if (mOnLongItemClickListener != null) {
            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    int layoutPosition = holder.getLayoutPosition();
                    mOnLongItemClickListener.onLongItemClick(v, layoutPosition);
                    return true;
                }
            });
        }
    }

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

    /**
     * viewholder
     */
    class RecyclerViewHolder extends RecyclerView.ViewHolder {

        private TextView mTextView;

        public RecyclerViewHolder(View itemView) {
            super(itemView);

            mTextView = (TextView) itemView.findViewById(R.id.textview);
        }
    }

    /**
     * 添加
     * @param position
     * @param item
     */
    public void addItem(int position, String item){
        mDatas.add(position, item+position);
        notifyItemInserted(position);
    }

    /**
     * 删除
     * @param position
     */
    public void removeItem(int position){
        mDatas.remove(position);
        notifyItemRemoved(position);
    }

    /**
     * item点击事件
     */
    public interface OnItemClickListener {
        /**
         * item点击事件
         *
         * @param view
         * @param position
         */
        void onItemClick(View view, int position);
    }

    public interface OnLongItemClickListener {
        /**
         * item长按点击事件
         *
         * @param view
         * @param position
         */
        void onLongItemClick(View view, int position);
    }


    public void setOnItemClickListener(OnItemClickListener listener) {
        this.mOnItemClickListener = listener;
    }

    public void setOnLongItemClickListener(OnLongItemClickListener listener) {
        this.mOnLongItemClickListener = listener;
    }
}

ItemDecoration分割线

在之前已经提到,RecyclerView有addItemDecoration()的方法,该方法的作用是为RecyclerView添加item分割线,为此我们不难想象RecyclerView默认是没有分割线的。但是google官方并没有提供具体ItemDecoration的方法,仅仅是定义了一个ItemDecoration的抽象类,所以要想实现分割线效果,必须要自己实现一个ItemDecoration。

ItemDecoration抽象方法介绍:

  1. public void onDraw(Canvas c, RecyclerView parent, State state):绘制分割线
  2. public void onDrawOver(Canvas c, RecyclerView parent, State state):绘制分割线,该方法后于onDraw,一般实现一个即可
  3. public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state):用于计算item分割线的偏移量

以下是通过ItemDecoration实现ListView和GridView的分割线:

ListView分割线:

package com.example.zhangke.recyclerviewdemo;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

/**
 * item分割线
 * <p/>
 * Created by zhangke on 16/6/15.
 */
public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    /**
     * 分割线
     */
    private static int[] ATTRS = new int[]{android.R.attr.listDivider};

    /**
     * 水平分割线
     */
    public static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL;
    /**
     * 垂直分割线
     */
    public static final int VERTICAL = LinearLayoutManager.VERTICAL;
    /**
     * 上下文
     */
    private Context mContext;
    /**
     * 分割线
     */
    private Drawable mDivider;

    private int mOrientation;

    /**
     * @param context     上下文
     * @param orientation 分割线方向
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public DividerItemDecoration(Context context, int orientation) {
        this.mContext = context;
//        TypedArray typedArray = mContext.obtainStyledAttributes(ATTRS);
//        mDivider = typedArray.getDrawable(0);
//        typedArray.recycle();

        mDivider = context.getDrawable(R.drawable.divider_view);
        setOrientation(orientation);
    }

    /**
     * 设置分割线方向
     *
     * @param orientation
     */
    private void setOrientation(int orientation) {
        if (orientation != HORIZONTAL && orientation != VERTICAL) {
            throw new IllegalArgumentException("参数异常");
        }
        this.mOrientation = orientation;
    }


    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == HORIZONTAL) {
            drawHorizontal(c, parent, state);
        } else {
            drawVertical(c, parent, state);
        }

        super.onDraw(c, parent, state);
    }

    /**
     * 绘制垂直方向分割线
     *
     * @param c
     * @param parent
     * @param state
     */
    private void drawVertical(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = parent.getChildAt(i);

            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int top = childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
            int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    /**
     * 绘制水平方向分割线
     *
     * @param c
     * @param parent
     * @param state
     */
    private void drawHorizontal(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int top = parent.getPaddingTop();
        int bottom = parent.getHeight() - parent.getPaddingBottom();

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getRight() + layoutParams.rightMargin + Math.round(ViewCompat.getTranslationX(childView));
            int right = left + mDivider.getIntrinsicWidth();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    /**
     * 设置分割线宽偏移量:
     * 通过RecyclerView源码可知:getItemOffset方法会在measureChild方法中调用,我们通过给方法的参数outRect
     * 设置left、top、right、bottom,设置的值会被增加到childView的padding值中。
     *
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

        if (mOrientation == HORIZONTAL) {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        } else {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        }
    }


}

GridView分割线:

package com.example.zhangke.recyclerviewdemo;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

/**
 * item分割线
 * <p/>
 * Created by zhangke on 16/6/15.
 */
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {

    /**
     * 分割线
     */
    private static int[] ATTRS = new int[]{android.R.attr.listDivider};

    /**
     * 水平分割线
     */
    public static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL;
    /**
     * 垂直分割线
     */
    public static final int VERTICAL = LinearLayoutManager.VERTICAL;
    /**
     * 上下文
     */
    private Context mContext;
    /**
     * 分割线
     */
    private Drawable mDivider;

    private int mOrientation;
    private int spanCount;

    /**
     * @param context     上下文
     * @param orientation 分割线方向
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public DividerGridItemDecoration(Context context, int orientation) {
        this.mContext = context;
//        TypedArray typedArray = mContext.obtainStyledAttributes(ATTRS);
//        mDivider = typedArray.getDrawable(0);
//        typedArray.recycle();

        mDivider = context.getDrawable(R.drawable.divider_view);
        setOrientation(orientation);
    }

    /**
     * 设置分割线方向
     *
     * @param orientation
     */
    private void setOrientation(int orientation) {
        if (orientation != HORIZONTAL && orientation != VERTICAL) {
            throw new IllegalArgumentException("参数异常");
        }
        this.mOrientation = orientation;
    }


    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        drawHorizontal(c, parent, state);
        drawVertical(c, parent, state);
    }

    /**
     * 绘制垂直方向分割线
     *
     * @param c
     * @param parent
     * @param state
     */
    private void drawVertical(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (GridLayoutManager.LayoutParams) child.getLayoutParams();
            int left = child.getLeft() - params.leftMargin;
            int right = child.getRight()+ params.rightMargin;
            int top = child.getBottom() + params.bottomMargin;
            int bottom = top + mDivider.getIntrinsicHeight();

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }

    }

    /**
     * 绘制水平方向分割线
     *
     * @param c
     * @param parent
     * @param state
     */
    private void drawHorizontal(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (GridLayoutManager.LayoutParams) child.getLayoutParams();
            int left = child.getRight() + params.rightMargin;
            int right = left + mDivider.getIntrinsicWidth();
            int top = child.getTop() - params.topMargin;
            int bottom = child.getBottom() + params.bottomMargin;

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    /**
     * 设置分割线宽偏移量:
     * 通过RecyclerView源码可知:getItemOffset方法会在measureChild方法中调用,我们通过给方法的参数outRect
     * 设置left、top、right、bottom,设置的值会被增加到childView的padding值中。
     */
    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        if (isLastColum(itemPosition, parent)) {
            //最后一列
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else if (isLastRow(itemPosition, parent)) {
            //最后一行
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());
        }

    }

    /**
     * 是否是最后一行
     *
     * @param itemPosition
     * @param parent
     * @return
     */
    private boolean isLastRow(int itemPosition, RecyclerView parent) {
        int spanCount = getSpanCount(parent);
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            int childCount = parent.getAdapter().getItemCount();

            int lastRowCount = childCount %  spanCount;
            if (lastRowCount == 0) {
                return false;
            } else if(itemPosition >= (childCount - lastRowCount)){
                return true;
            }
        }
        return false;
    }

    /*
    * 是否是最后一列
     */
    private boolean isLastColum(int itemPosition, RecyclerView parent) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            int spanCount = getSpanCount(parent);
            if ((itemPosition + 1) % spanCount == 0) {
                return true;
            }
        }
        return false;
    }

    private int getSpanCount(RecyclerView parent) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            GridLayoutManager lm = (GridLayoutManager) layoutManager;
            int spanCount = lm.getSpanCount();
            return spanCount;
        }
        return 0;
    }

}

通过LayoutManager实现不同效果

效果图如下:
这里写图片描述

代码如下:

package com.example.zhangke.recyclerviewdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private ArrayList<String> mDatas;
    private RecyclerViewAdapter mAdapter;
    private StaggeredGridLayoutAdapter mStagAdapter;
    private RecyclerView.ItemDecoration mItemDecoration;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerView = (RecyclerView) findViewById(R.id.recycleview);
        initData();

        //垂直listview
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        //设置分割线
        mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
        mRecyclerView.addItemDecoration(mItemDecoration);
        // 设置增删动画
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        mAdapter = new RecyclerViewAdapter(this, mDatas);

        //设置点击事件
        mAdapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "onItemClick " + position, Toast.LENGTH_SHORT).show();
            }
        });

        mAdapter.setOnLongItemClickListener(new RecyclerViewAdapter.OnLongItemClickListener() {
            @Override
            public void onLongItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "onLongItemClick " + position, Toast.LENGTH_SHORT).show();
            }
        });

        //设置Adapter
        mRecyclerView.setAdapter(mAdapter);

    }

    /**
     * 初始化数据
     */
    private void initData() {
        mDatas = new ArrayList<String>();
        for (int i = 0; i < 100; i++) {
            mDatas.add("item " + i);
        }

    }


    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mItemDecoration != null) {
            mRecyclerView.removeItemDecoration(mItemDecoration);
        }
        switch (item.getItemId()) {
            case R.id.add:

                mAdapter.addItem(1, "item" + 1);
                break;

            case R.id.remove:
                mAdapter.removeItem(1);
                break;

            case R.id.showVertical:
                //垂直listview
                mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
                mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mAdapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, int position) {
                        Toast.makeText(MainActivity.this, "onItemClick " + position, Toast.LENGTH_SHORT).show();
                    }
                });

                mAdapter.setOnLongItemClickListener(new RecyclerViewAdapter.OnLongItemClickListener() {
                    @Override
                    public void onLongItemClick(View view, int position) {
                        Toast.makeText(MainActivity.this, "onLongItemClick " + position, Toast.LENGTH_SHORT).show();
                    }
                });

                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showHorizontal:
                //水平listview
                mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
                mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showGridHorizontal:
                // 水平gridview
                mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4, LinearLayoutManager.HORIZONTAL, false));
                mItemDecoration = new DividerGridItemDecoration(this, DividerGridItemDecoration.HORIZONTAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showGridVertical:
                // 垂直gridview
                mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4));
                mItemDecoration = new DividerGridItemDecoration(this, DividerGridItemDecoration.VERTICAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showStaggle:
                // 瀑布流
                mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, LinearLayoutManager.VERTICAL));
                mStagAdapter = new StaggeredGridLayoutAdapter(this, mDatas);
                mRecyclerView.setAdapter(mStagAdapter);
                break;
        }
        return true;
    }
}

源代码:
https://github.com/kerwin1321/Study/tree/master/MaterialDesign/RecyclerView

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值