100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 自定义控件三部曲视图篇(四)——RecyclerView系列之一简单使用

自定义控件三部曲视图篇(四)——RecyclerView系列之一简单使用

时间:2020-07-08 07:11:08

相关推荐

自定义控件三部曲视图篇(四)——RecyclerView系列之一简单使用

绝望的时候不要那么绝望,高兴的时候不要那么高兴,是你慢慢会学会的。 ——董卿

系列文章:Android自定义控件三部曲文章索引:/harvic880925/article/details/50995268

转了一年多,又回来继续做Android。果然还是看到代码最让我兴奋……但有些事,没经历过,总归还是遗憾的。在VIVO的游戏中心,有一个特别炫酷的功能:

这个功能就是使用RecyclerView来实现的,在本系列中,我们将最终制作出这样的一个效果出来。

一、导入Support-v7包

工欲善其事必先利其器,RecyclerView存在于support-v7包中,我们需要在新建的gradle工程中导入support-v7包:

compile 'com.android.support:recyclerview-v7:21.0.3'

gralde 版本较高的同学,会发现compile关键字这里会报警告,在高版本gradle中compile已经弃用了,改成了implementation,所以你可以改为:

implementation 'com.android.support:recyclerview-v7:21.0.3'

加上上面的依赖代码以后,会发现根本不好使,依赖库根本拉不下来。这是为什么呢?

support-v7包并不是通过maven从远程下载的,而是通过Android Studio的SDK Manager来下载到本地,然后再引用的。本地有没有support-v7包,大家可以看下SDK的这个位置(Sdk\extras\android\m2repository\com\android\support):

从这里可以看到,在我的com/android/support目录下有所有的support包,这里也有recyclerview-v7包。如果在该文件夹下,你没有的话,可以通过SDK Manager引入:

下载完成后,在这个文件夹下就会有对应的包存在了,当我们点进去recyclerview-v7文件夹里面,可以看到各种版本:

大家就可以选择这里已有的版本引入了,比如我这里是有25.3.1的,所以我这里最终的引用包是:

implementation 'com.android.support:recyclerview-v7:25.3.1'

在引入support包时,需要有两个注意事项:

引入的support包的版本要比targetSdkVersion要高,不然会报错如果引入了多个support包组件,它们的版本号要保持一致,不然有可能因为不是同一个版本,代码不配套而出现错误,比如我同时引入appcompat包和recyclerview包,那么它们的写法应该是:

implementation 'com.android.support:appcompat-v7:25.3.1'implementation 'com.android.support:recyclerview-v7:25.3.1'

二、简单使用

在这部分,我们首先做出一个最简单的例子,来看下RecyclerView的使用方法。本小节所实现的效果如下图所示:

2.1 引入RecyclerView

首先,在XML中引入RecyclerView:

<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="/apk/res/android"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".LinearActivity"><android.support.v7.widget.RecyclerViewandroid:id="@+id/linear_recycler_view"android:layout_width="match_parent"android:layout_height="match_parent"></android.support.v7.widget.RecyclerView></android.support.constraint.ConstraintLayout>

2.2 实现Adapter

与listView一样,同样需要一个Adapter来将数据和Item视图绑定起来,但不同的的是RecyclerView的Adapter需要派生自RecyclerView.Adapter<RecyclerView.ViewHolder>

当我们写一个Adapter的类派生自RecyclerView.Adapter<RecyclerView.ViewHolder>时,最简单的形式是这样的:

public class RecyclerAdapter extends RecyclerView.Adapter<ViewHolder> {@Overridepublic ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {return null;}@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {}@Overridepublic int getItemCount() {return 0;}}

这三个函数是强制必须重写的,其中:

onCreateViewHolder:用于得到我们自定义的ViewHolder,在listView中,我们也会定义ViewHolder来承载视图中的元素.onBindViewHolder:是用于将指定位置的数据和视图绑定起来的getItemCount:用于获取列表总共的item数

可见,这三项其实在listview中也都是需要做的,只是这里单独通过回调给列出来了,我们只需要补充上这三个函数,就算实现了Adapter了.

在填充RecyclerAdapter之前,我们知道,一般而言ListView的数据都是从外部传进来的,所以我们需要给RecyclerAdapter添加上一个构造函数,将数据从外部传进来:

private Context mContext;private ArrayList<String> mDatas;public RecyclerAdapter(Context context, ArrayList<String> datas) {mContext = context;mDatas = datas;}

为了方便起来,我们传进来的数据非常简单,就是一个String字符串,同时,由于在RecyclerAdater中,经常会用到Context,所以我们也把Context传进来,并且保存起来.

接下来,我们就是先创建一个HolderView,然后填充那三个函数。我们都知道HolderView主要是为了保存每一个Item的视图的控件元素。所以我们要先创建一个Item的xml(item_layout.xml):

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:id="@+id/item_tv"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:padding="10dp" /></LinearLayout>

在这个item中,只有一个TextView,所以我们先写一个ViewHolder,ViewHolder的主要作用就是将XML中的控件以变量的形式保存起来,方便我们后面数据绑定.

public class NormalHolder extends RecyclerView.ViewHolder{public TextView mTV;public NormalHolder(View itemView) {super(itemView);mTV = (TextView) itemView.findViewById(R.id.item_tv);mTV.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(mContext,mTV.getText(),Toast.LENGTH_SHORT).show();}});}}

在这里,在创建ViewHolder时,将整个ItemView传了进来,然后将TextView从ItemView中取出来保存在mTV变量中.并且,在点击mTV后,弹出这个TextView的内容.

在写好ViewHolder以后,我们就要逐个填充RecyclerAdapter的三个函数了。首先是onCreateViewHolder:

public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {LayoutInflater inflater = LayoutInflater.from(mContext);return new NormalHolder(inflater.inflate(R.layout.item_layout,parent,false));}

在每一次需要创建ViewHolder时,都会调用onCreateViewHolder函数,所以我们需要在onCreateViewHolder中返回我们创建的ViewHolder实例。

然后在onBindViewHolder中,将数据与ViewHolder绑定起来:

public void onBindViewHolder(ViewHolder holder, int position) {NormalHolder normalHolder = (NormalHolder)holder;normalHolder.mTV.setText(mDatas.get(position));}

最后,在getItemCount中返回数据的个数:

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

到这里,整个RecyclerAdapter就实现完了,完整的代码如下,供大家参考:

public class RecyclerAdapter extends RecyclerView.Adapter<ViewHolder> {private Context mContext;private ArrayList<String> mDatas;public RecyclerAdapter(Context context, ArrayList<String> datas) {mContext = context;mDatas = datas;}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {LayoutInflater inflater = LayoutInflater.from(mContext);return new NormalHolder(inflater.inflate(R.layout.item_layout, parent, false));}@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {NormalHolder normalHolder = (NormalHolder) holder;normalHolder.mTV.setText(mDatas.get(position));}@Overridepublic int getItemCount() {return mDatas.size();}public class NormalHolder extends RecyclerView.ViewHolder {public TextView mTV;public NormalHolder(View itemView) {super(itemView);mTV = (TextView) itemView.findViewById(R.id.item_tv);mTV.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(mContext, mTV.getText(), Toast.LENGTH_SHORT).show();}});}}}

2.3 填充RecyclerView

之后,回到Activity中,首先,构造一个模拟数据的函数,用于填充RecyclerVIew:

public class LinearActivity extends AppCompatActivity {…………private ArrayList<String> mDatas =new ArrayList<>();private void generateDatas(){for (int i=0;i<200;i++){datas.add("第 " + i +" 个item");}}}

之后,在OnCreate函数中填充RecyclerView:

protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_linear);generateDatas();RecyclerView mRecyclerView = (RecyclerView)findViewById(R.id.linear_recycler_view);//线性布局LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);mRecyclerView.setLayoutManager(linearLayoutManager);RecyclerAdapter adapter = new RecyclerAdapter(this, mDatas);mRecyclerView.setAdapter(adapter);}

这里与listVIew唯一的区别是,这里需要设置一个LayoutManager对象,这里设置的是LinearLayoutManager,也就是垂直列表。我们知道Adapter的职责是用数据将每个Item的控件填充起来。而RecycerView与ListView不一样的是,它不仅能实现传统的滑动的列表,还能实现GridView和瀑布流造型,或者其它各式各样的特殊造型。而这些造型的实现就是通过LayoutManger来实现的,我们通过Adapter将Item填充了以后,那每个Item怎么摆放是由谁来做的呢?摆放Item的操作就是使用LayoutManager来实现出来的。所以每个LayoutManger所实现的摆放Item的方式都是不一样的,比如:LinearLayoutManager就是传统的ListView的功能,上下滚动或者左右滚动。而GridLayoutManager则是网格摆放,而StaggeredGridLayoutMnager则是瀑布流摆放。

到这里,我们就实现了本部分开头的上下滚动的效果了。

*源码在文章底部给出

三、其它LayoutManager

从上面的分析可以看出,摆放Item的操作主要是由LayoutManager来实现的,这也就是RecyclerView可以制作出各种特殊列表样式的原因。系统为我们提供了几个已经写好的LayoutManager:

其中WearableLinearLayoutManager用于在穿戴设备上使用,比如智能手表等,所以我们这里不讨论它。下面我们逐个看下这些LayoutManger所实现的效果。

3.1 GridLayoutManager

我们知道了,LayoutManger的职责就是如何摆放Item,所以对于Adapter与RecyclerView是没有影响的,除非我们为了迎合LayoutManger要改Item的布局,比如为了实现瀑布流效果而需要改变每个Item的宽或高等。一般而言,我们更改LayoutManager,不需要对其它对象操作。所以这也是RecyclerView比较好的一个地方,通过RecycerView本身,Adapter,LayoutManger实现了完全解耦。各自实现各自的功能,与其它部分无关。而GridLayoutManager的主要作用就是将Item进行网格状摆放,进而实现网格布局效果。

所以我们要设置GridLayoutManager时,也只需要更改Acitivity中的设置LayoutManager这块代码即可,其它都不需要动:

protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_grid);generateDatas();RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.grid_recycler_view);//如果是横向滚动,后面的数值表示的是几行,如果是竖向滚动,后面的数值表示的是几列GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 5);gridLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);mRecyclerView.setLayoutManager(gridLayoutManager);RecyclerAdapter adapter = new RecyclerAdapter(this, mDatas);mRecyclerView.setAdapter(adapter);}private void generateDatas() {for (int i = 0; i < 200; i++) {mDatas.add("第 " + i + " 个item");}}

其中:

public GridLayoutManager(Context context, int spanCount)

spanCount:如果是竖向滚动,则表示当前划分为几列;如果是横向滚动,则表示当前划分为几行。

效果如下:

可以看到,这里就实现了网格效果,并且是上下滚动。我们通过gridLayoutManager.setOrientation();可以设置RecyclerView的滚动方向,取值有LinearLayoutManager.VERTICAL和LinearLayoutManager.HORIZONTAL

如果我们将它改为横向滚动:

GridLayoutManager gridLayoutManager = new GridLayoutManager(this,5);gridLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);mRecyclerView.setLayoutManager(gridLayoutManager);

效果如下:

在横向滚动的情况下,列表就变成了五行了。

3.2 StaggeredGridLayoutMnager

StaggeredGridLayoutMnager主要用来实现瀑布流效果。同样,我们直接把LayoutManager改为StaggeredGridLayoutMnager:

protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_staggered);generateDatas();RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.stagger_recycler_view);//瀑布流布局StaggeredGridLayoutManager staggeredManager = new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.VERTICAL);mRecyclerView.setLayoutManager(staggeredManager);……}

其中public StaggeredGridLayoutManager(int spanCount, int orientation)

spanCount:同样表示行数或列数,如果是竖向滚动,则表示当前划分为几列;如果是横向滚动,则表示当前划分为几行。orientation:表示滚动方向,取值有:StaggeredGridLayoutManager.HORIZONTAL和StaggeredGridLayoutManager.VERTICAL

下面来看下效果:

可以看到,这里由于每个Item的高度是一定的,所有的Item的高度都一样,导致所实现的瀑布流布局跟网格布局完全相同,所以如果想实现瀑布流布局,那必然需要每个Item的高度是不一样的。

所以我们需要修改Adapter,在代码中动态设置每个Item的高度,让每个Item的高度尽量都不一样,这样就可以看到瀑布流效果了。

所以,主要修改了这两个地方:

@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {NormalHolder normalHolder = (NormalHolder)holder;normalHolder.mTV.setText(mDatas.get(position));ViewGroup.LayoutParams lp = normalHolder.mTV.getLayoutParams();lp.height = getRandomHeight();normalHolder.mTV.setLayoutParams(lp);}private int getRandomHeight(){int randomHeight = 0;do{randomHeight = (int)(Math.random()*300);}while (randomHeight == 0);return randomHeight;}

定义了一个getRandomHeight()函数,得到一个0-300之间的一个数值。然后在onBindViewHolder中,将这个数值设置给TextView,作为TextVIew的高度。

完整的改写后的Adapter的代码如下:

public class StaggerRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {private Context mContext;private ArrayList<String> mDatas;public StaggerRecyclerAdapter(Context context, ArrayList<String> datas) {mContext = context;mDatas = datas;}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {LayoutInflater inflater = LayoutInflater.from(mContext);return new NormalHolder(inflater.inflate(R.layout.item_layout, parent, false));}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {NormalHolder normalHolder = (NormalHolder) holder;normalHolder.mTV.setText(mDatas.get(position));ViewGroup.LayoutParams lp = normalHolder.mTV.getLayoutParams();lp.height = getRandomHeight();normalHolder.mTV.setLayoutParams(lp);}private int getRandomHeight() {int randomHeight = 0;do {randomHeight = (int) (Math.random() * 300);} while (randomHeight == 0);return randomHeight;}@Overridepublic int getItemCount() {return mDatas.size();}public class NormalHolder extends RecyclerView.ViewHolder {public TextView mTV;public NormalHolder(View itemView) {super(itemView);mTV = (TextView) itemView.findViewById(R.id.item_tv);mTV.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(mContext, mTV.getText(), Toast.LENGTH_SHORT).show();}});}}}

改写后的效果如下:

首先,可以看到,这里已经实现了瀑布流效果,但是每当滚动到顶部时,所有的Item会跳动一下,重新布局,这是为什么呢?

这是因为在每次拉到顶部的时候,所有的Item会重新执行一次onBindViewHolder函数,因为我们Item的高度就是在这个函数中随机生成的,所以在拉到顶部时,每个Item的高度就会重新生成,造成的结果就是看起来跳了一下,重新布局了。

所以,要解决这个问题也比较简单,那就是用一个数组,在Adapter初始化的时候,就把每个Item的高度生成好,然后在onBindViewHolder中直接取出即可。

所以,我们可以先申请一个数组,并且在Adapter初始化时,保存每个Item的高度

public class StaggeredRecyclerAdapter extends Adapter<ViewHolder> {private Context mContext;private ArrayList<String> mDatas;private ArrayList<Integer> mHeights = new ArrayList<>();public StaggeredRecyclerAdapter(Context context, ArrayList<String> datas) {mContext = context;mDatas = datas;if (mDatas.size()>0){for (int i = 0;i<mDatas.size();i++){mHeights.add(getRandomHeight());}}}……}

然后在onBindViewHolder中,直接使用数组中的高度即可:

public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {NormalHolder normalHolder = (NormalHolder) holder;normalHolder.mTV.setText(mDatas.get(position));ViewGroup.LayoutParams lp = normalHolder.mTV.getLayoutParams();lp.height = mHeights.get(position);normalHolder.mTV.setLayoutParams(lp);}

完整代码不再贴出,有需要的同学可以看源码。

效果如下图所示:

可以看到,这时候,在拉到顶部时,就不会出现重新布局的情况了。

到这里,RecyclerView的基本使用方法,就讲完了,下面我们来看看,怎么加载不同类型的ITEM吧。

*源码在文章底部给出

四、加载不同类型的View

在listview中,我们经常也会遇到加载不同类型视图的情况,下面我们同样使用RecyclerView来实现加载不同布局视图的效果,在这部分,我们实现一个很常见的分组视图的效果:

4.1 主要原理

有细心的同学,估计已经注意到,在public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)中,第二个参数就是View的类型,而且这个类型,我们是可以人为更改的。我们就可以根据类型的不同,传进去不同的参数。

在自定义的Adapter中,我们可以通过getItemViewType函数来返回每个position所对应的类型

@Overridepublic int getItemViewType(int position) {…………}

4.2 主要实现

下面,我们就在上面的代码上继续更改。

4.2.1 添加一个ViewHolder

首先,我们需要再写一个ViewHolder,用于保存另外一个显示为组标题的Item的视图。

这个item的xml为:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:background="@android:color/holo_green_dark"android:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:id="@+id/item_section_tv"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:padding="10dp" /></LinearLayout>

将它设置为绿色背景,并且中间只有一个TextView,居中显示

然后在RecyclerAdapter中,另外再添加一个ViewHolder,命名为SectionHolder:

public class SectionHolder extends RecyclerView.ViewHolder{public TextView mSectionTv;public SectionHolder(View itemView) {super(itemView);mSectionTv = (TextView)itemView.findViewById(R.id.item_section_tv);}}

同样,是把xml中的控件取出来,以变量的形式保存在这个ViewHolder中。

4.2.2 绑定ViewHolder

首先,我们需要在getItemViewType中,根据位置返回不同的类型:

public static enum ITEM_TYPE {ITEM_TYPE_SECTION,ITEM_TYPE_ITEM}private int M_SECTION_ITEM_NUM = 10;@Overridepublic int getItemViewType(int position) {if (position % M_SECTION_ITEM_NUM == 0){return ITEM_TYPE.ITEM_TYPE_SECTION.ordinal();}return ITEM_TYPE.ITEM_TYPE_ITEM.ordinal();}

我们假设每十个常规Item添加一个组item,所以这里定义了一个常量M_SECTION_ITEM_NUM ,它的值设定为10,每十个元素返回一个组item的类型数值。为了标识组类型和常规item类型,这里定义一个枚举类型,在其中定义两个值。而ITEM_TYPE.ITEM_TYPE_SECTION.ordinal()的ordinal()函数,则是返回当前枚举值的位置索引。

然后在onCreateViewHolder(ViewGroup parent, int viewType)中根据不同的viewType返回不同的viewHolder:

@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {LayoutInflater inflater = LayoutInflater.from(mContext);if (viewType == ITEM_TYPE.ITEM_TYPE_ITEM.ordinal()){return new NormalHolder(inflater.inflate(R.layout.item_layout,parent,false));}return new SectionHolder(inflater.inflate(R.layout.item_section_layout,parent,false));}

如果是常规类型就返回NormalHolder的实例,如果是标题样式,则返回SectionHolder的实例。

最后,是在onBindViewHolder(ViewHolder holder, int position)中将数据与VolderHolder绑定

@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {if (holder instanceof SectionHolder){SectionHolder sectionHolder = (SectionHolder)holder;sectionHolder.mSectionTv.setText("第 "+position/M_SECTION_ITEM_NUM +" 组");}else if (holder instanceof NormalHolder){NormalHolder normalHolder = (NormalHolder)holder;normalHolder.mTV.setText(mDatas.get(position));}}

代码难度不大,就不再讲了。到这里完整的加载不同类型View的功能就实现出来了,

完整的RecyclerAdapter的代码如下:

public class RecyclerAdapter extends RecyclerView.Adapter<ViewHolder> {private Context mContext;private ArrayList<String> mDatas;public static enum ITEM_TYPE {ITEM_TYPE_SECTION,ITEM_TYPE_ITEM}private int M_SECTION_ITEM_NUM = 10;public RecyclerAdapter(Context context, ArrayList<String> datas) {mContext = context;mDatas = datas;}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {LayoutInflater inflater = LayoutInflater.from(mContext);if (viewType == ITEM_TYPE.ITEM_TYPE_ITEM.ordinal()) {return new NormalHolder(inflater.inflate(R.layout.item_layout, parent, false));}return new SectionHolder(inflater.inflate(R.layout.item_section_layout, parent, false));}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {if (holder instanceof SectionHolder) {SectionHolder sectionHolder = (SectionHolder) holder;sectionHolder.mSectionTv.setText("第 " + position / M_SECTION_ITEM_NUM + " 组");} else if (holder instanceof NormalHolder) {NormalHolder normalHolder = (NormalHolder) holder;normalHolder.mTV.setText(mDatas.get(position));}}@Overridepublic int getItemCount() {return mDatas.size();}@Overridepublic int getItemViewType(int position) {if (position % M_SECTION_ITEM_NUM == 0) {return ITEM_TYPE.ITEM_TYPE_SECTION.ordinal();}return ITEM_TYPE.ITEM_TYPE_ITEM.ordinal();}public class NormalHolder extends RecyclerView.ViewHolder {public TextView mTV;public NormalHolder(View itemView) {super(itemView);mTV = (TextView) itemView.findViewById(R.id.item_tv);mTV.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(mContext, mTV.getText(), Toast.LENGTH_SHORT).show();}});}}public class SectionHolder extends RecyclerView.ViewHolder {public TextView mSectionTv;public SectionHolder(View itemView) {super(itemView);mSectionTv = (TextView) itemView.findViewById(R.id.item_section_tv);}}}

*源码在文章底部给出

这篇的内容就到这里了,内容比较简单,单纯只是为了介绍RecyclerView的基本用法,下篇我们就要开始放大招了,讲解ItemDecoration的实现、原理以及炫酷的效果哦。

如果本文有帮到你,记得加关注哦

CSDN源码现在不能零分下载了,必须强制最低一分,我设置为了最低分,如果没分的同学,可以从github上下载。

源码地址:/download/harvic880925/10674920

github代码地址:/harvic/harvic_blg_share 位于RecylcerView(一)

转载请标明出处,/harvic880925/article/details/82656394 谢谢

如果你喜欢我的文章,你可能更喜欢我的公众号

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。