RecyclerView
Recyclerview是现在比较常用的控件,有三种布局方式
- GridLayoutManager: //卡片布局
- LinearLayoutManager: //线性布局 可以设置横向或者纵向
- StaggeredGridLayoutManager: //瀑布流布局
RecyclerView 刷新数据有两种方式
- notifyDataSetChanged() //刷新全局
- notifyItemRangeChanged //刷新局部
导入库
kotlin
implementation 'androidx.recyclerview:recyclerview:1.1.0'1
设置布局样式
- GridLayoutManager 卡片布局
kotlin
val manager = GridLayoutManager(context,2)
recycler.layoutManager = manager1
2
2
- LinearLayoutManager 线性布局
kotlin
val manager = LinearLayoutManager(context)
recycler.layoutManager = manager
// 设置分页
val snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(recycler)1
2
3
4
5
2
3
4
5
- StaggeredGridLayoutManager 瀑布流布局
kotlin
val mLayoutManager = StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(mLayoutManager);1
2
2
创建适配器adapter
kotlin
class MainRecycleAdapter(list:MutableList<PhotoModel>,pContext: Context): RecyclerView.Adapter<MainRecycleAdapter.Viewholder>() {
var datas = list
val context = pContext
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Viewholder {
return Viewholder(LayoutInflater.from(parent.context).inflate(R.layout.recycle_item,parent,false))
}
override fun getItemCount(): Int = datas.size
override fun onBindViewHolder(holder: Viewholder, position: Int) {
val model = datas[position]
Glide.with(holder.imgView).load(model.imgUrl).into(holder.imgView)
holder.imgView.setOnClickListener {
val intent = Intent(context,PhotoShowActivity::class.java)
intent.putExtra("linkurl",model.link)
intent.putExtra("position",position)
intent.putExtra("datas",ArrayList<Parcelable>(datas))
context.startActivity(intent)
}
}
class Viewholder(view: View):RecyclerView.ViewHolder(view){
val imgView: ImageView = view.findViewById(R.id.recycler_img)
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
下拉刷新 SwipeRefreshLayout
- 导入
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" - SwipeRefreshLayout包裹住刷新的控件
kotlin
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/swipe">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main_recycle">
</androidx.recyclerview.widget.RecyclerView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 使用方法
kotlin
// 设置下拉刷新
swipe.setOnRefreshListener{
// 在这里进行下一页的加载
// 加载数据完成后,通知界面刷新,关闭刷新动画
adapter?.notifyDataSetChanged()
swipe.isRefreshing = false
}
// 刷新渐变颜色
swipe.setColorSchemeResources(
R.color.colorPrimary,
R.color.colorPrimaryDark,
R.color.colorAccent
);1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
上拉加载
上拉加载更多是通过监听滚动视图的滚动位置进行计算的,当最后一个视图即将显示的时候,并且手指离开了屏幕,开始加载下一页数据
kotlin
main_recycle.addOnScrollListener(object : RecyclerView.OnScrollListener(){ //添加滑动监听
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { // 当状态进行改变时
super.onScrollStateChanged(recyclerView, newState)
var last = layoutManager.findLastVisibleItemPosition() // 获取到最后一个即将显示的视图位置
var sum = adapter?.itemCount
if (newState == RecyclerView.SCROLL_STATE_IDLE){ // 当停止滚动时
// 在这里进行下一页数据加载
}
}
})1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
刷新局部数据的方法
kotlin
public void setMoreData(List<BasePubuBean> puBuList) {
int start = mayContentList.size();
if (puBuList!=null && puBuList.size()!=0){
mayContentList.addAll(puBuList);
int end = mayContentList.size();
mRecommendPuBuAdapter.notifyItemRangeInserted(start,end);
}
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
瀑布流
瀑布流主要在适配器onBindViewHolder绑定数据的时候,使用getLayoutParams().height给对应的视图赋值高度
public class ColorAdapter extends RecyclerView.Adapter<ColorAdapter.ViewHolder>{
private String[] mDataSet;
private Context mContext;
private Random mRandom = new Random();
public ColorAdapter(Context context, String[] DataSet){
mDataSet = DataSet;
mContext = context;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ViewHolder vh = new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view,parent,false));
return vh;
}
@Override
public int getItemCount() {
return mDataSet.length;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.mTextView.setText(mDataSet[position]);
holder.mTextView.getLayoutParams().height = getRandomIntInRange(300,180); //关键在于这句高度赋值
holder.mTextView.setBackgroundColor(getRandomHSVColor());
}
// 获取随机高度
protected int getRandomIntInRange(int max, int min){
return mRandom.nextInt(max)+min;
}
// 获取随机颜色
protected int getRandomHSVColor(){
// Generate a random hue value between 0 to 360
int hue = mRandom.nextInt(361);
// We make the color depth full
float saturation = 1.0f;
// We make a full bright color
float value = 1.0f;
// We avoid color transparency
int alpha = 255;
// Finally, generate the color
int color = Color.HSVToColor(alpha, new float[]{hue, saturation, value});
// Return the color
return color;
}
public static class ViewHolder extends RecyclerView.ViewHolder{
public TextView mTextView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
mTextView = (TextView)itemView.findViewById(R.id.tv);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
瀑布流获取最后显示位置的方法,因为瀑布流的item高矮不确定性,所以在获取最后一个显示的position的时候和线性不一样
kotlin
main_recycler_view.addOnScrollListener(object: RecyclerView.OnScrollListener(){
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE){
val position = manager.findFirstCompletelyVisibleItemPositions(null) // 获取显示的位置,数组形式
for (i in position){
if (i >= ApiClient.videoList.size -3 ){
}
}
}
}
})1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
滚动到指定位置
kotlin
// scrollBy(x, y)这个方法是自己去控制移动的距离,单位是像素,使用scrollBy(x, y)需要自己去计算移动的高度或宽度
recyclerView.scrollBy(x, y)
// scrollToPosition(position)这个方法的作用是定位到指定项,就是把你想显示的项显示出来,但是在屏幕的什么位置是不管的,
recyclerView.scrollToPosition(3)
//smoothScrollToPosition(position)和scrollToPosition(position)效果基本相似,不同的是smoothScrollToPosition是平滑到你想显示的项,而scrollToPosition是直接定位显示
recyclerView.smoothScrollToPosition(position)。
// 滚动到指定位置,如果该项可以置顶就将其置顶显示
manager.scrollToPositionWithOffset(3, 0);1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Adapter跟Activity的交互
有时候 adapter 里面的控件点击事件需要回调给 Activity,这个时候就需要它们进行交互,通常的做法是,定义一个接口类,定义实现的方法,adapter中创建对应的接口属性, 当需要回调时, adapter 通过接口方法回调给 Activity, Activity需要实现对应的接口方法
adapter中的类
kotlin
class PlayAdapter(list: MutableList<Video>, listener: OnItemClickListener): RecyclerView.Adapter<PlayAdapter.ViewHolder>() {
var videoList = list
private val clickListener = listener // 接口属性
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlayAdapter.ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.play_recycler_item,parent,false))
}
override fun getItemCount(): Int = videoList.size
override fun onBindViewHolder(holder: PlayAdapter.ViewHolder, position: Int) {
val video = videoList[position]
Glide.with(holder.bgImg).load(video.headImg).into(holder.bgImg)
holder.closeBtn.setOnClickListener { this.clickListener.onCloseView()}
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val bgImg: ImageView = itemView.findViewById(R.id.play_item_img)
val closeBtn: Button = itemView.findViewById(R.id.close_btn)
}
interface OnItemClickListener{
fun onCloseView()
}
}
OnItemClickListener: 是对应的接口类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Activity中的代码
kotlin
class PlayActivity : AppCompatActivity(),PlayAdapter.OnItemClickListener{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_play)
}
override fun onCloseView() {
finish()
}
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
获取当前显示的位置
| 方法 | 作用 |
|---|---|
| findFirstVisibleItemPosition() | 返回当前RecycelrView中 第一个可见的item 的adapter postion |
| findLastVisibleItemPosition() | 返回当前RecycelrView中 最后一个可见的item 的adapter postion |
| findFirstCompletelyVisibleItemPosition() | 返回当前RecycelrView中 第一个完整可见的item 的adapter postion |
| findLastCompletelyVisibleItemPosition() | 返回当前RecycelrView中 最后一个完整可见的item 的adapter postion |
调用方式
kotlin
val postion = (recyclerView.layoutManager as LinearLayoutManager ).findFirstCompletelyVisibleItemPosition()1
这几个方法的差别在于: 一个是可见,一个是要完全可见
注意点: 这4个方法,只有当 RecyclerView 在屏幕展示出来后,才能得到正常的返回值,否则都是
-1
一个Recyclerview中多个样式的View
主要是两个方法的配置,getItemViewType 和 onCreateViewHolder 方法
java
// 返回定义的类型
public int getItemViewType(int position){
ItemModel model = mData.get(position);
if (model.getType() == 0){
return TYPE_FULL_IMAGE;
}else if (model.getType() == 1){
return TYPE_RIGHT_IMAGE;
}else {
return TYPE_THREE_IMAGES;
}
}
// 定义三个常量标识,三种item类型
public static final int TYPE_FULL_IMAGE = 0;
public static final int TYPE_RIGHT_IMAGE = 1;
public static final int TYPE_THREE_IMAGES = 2;
// 根据类型返回对应的view
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view;
if (viewType == TYPE_FULL_IMAGE){
view = View.inflate(parent.getContext(),R.layout.item_type_full_image,null);
}else if (viewType == TYPE_RIGHT_IMAGE){
view = View.inflate(parent.getContext(),R.layout.item_type_left_title,null);
}else {
view = View.inflate(parent.getContext(),R.layout.item_type_right_title,null);
}
return new ViewHolder(view);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
设置每个item边距
java
// 设置每个item的边距
mContentList.addItemDecoration(new RecyclerView.ItemDecoration(){
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
outRect.top = SizeUtils.dip2px(getContext(),2.5f);
outRect.bottom = SizeUtils.dip2px(getContext(),2.5f);
outRect.left = SizeUtils.dip2px(getContext(),2.5f);
outRect.right = SizeUtils.dip2px(getContext(),2.5f);
}
});1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10