recyclerView瀑布流

  最近在开发一个小软件,在需要用到图片列表的时候,突然觉得,我好像从来都没有用过recyclerView的瀑布流,于是记下了这篇使用方法

布局

主布局

  activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

item布局

  item.xml,在item布局中,设置RelativeLayout完全适应ImageView的布局大小,而ImageView的布局大小,将会在显示的过程中动态的计算出来

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/transparent">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>

创建图片实体类

  file用来储存本地图片的文件对象,不用多说;
  imgWidth和imgHeight用来储存这个图片的px宽度和高度,在后面适配器中需要结合这两个值去计算图片显示在屏幕上的px高度;
  showHeight用来储存图片显示在屏幕上的高度,由屏幕宽度、图片真实高度、图片真实宽度计算得出,当它的值为-1时,说明该对象的显示高度已经计算过了(如果我们的列表是纵向的,那么它显示的宽度是直接通过屏幕宽度计算的,所以不需要在实体类中再对它的显示宽度做记录);

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
public class ImgEntity {
private File file; //文件对象
private int imgWidth; //图片宽度
private int imgHeight; //图片高度
private int showHeight = -1; //显示的高度

public void setFile(File file) {
this.file = file;
}

public void setImgWidth(int imgWidth) {
this.imgWidth = imgWidth;
}

public void setImgHeight(int imgHeight) {
this.imgHeight = imgHeight;
}

public void setShowHeight(int showHeight) {
this.showHeight = showHeight;
}

public File getFile() {
return file;
}

public int getImgWidth() {
return imgWidth;
}

public int getImgHeight() {
return imgHeight;
}

public int getShowHeight() {
return showHeight;
}
}

适配器

  图片显示在屏幕的宽度和高度都是在适配器中动态计算的,由于例子中是垂直方向的瀑布流,所以宽度应该是一个固定值,故没有在图片实体中对其属性定义

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
58
59
60
61
62
63
64
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
private Context context;
private List<ImgEntity> photoList; //图片实体对象
private int spanCount; //列数
private int itemRound; //item的圆角度
private int marginLeft; //item左边的外边距
private int marginTop; //item顶部的外边距
private int marginRight; //item右边的外边距
private int marginBottom; //item底部的外边距

private int showWidth; //图片显示在屏幕时的宽度

public RecyclerAdapter(Context context, List<ImgEntity> photoList, int spanCount, int itemRound, int marginLeft, int marginTop, int marginRight, int marginBottom) {
this.context = context;
this.photoList = photoList;
this.spanCount = spanCount;
this.itemRound = itemRound;
this.marginLeft = marginLeft;
this.marginTop = marginTop;
this.marginRight = marginRight;
this.marginBottom = marginBottom;

//屏幕宽度px值
int screenWidthPx = context.getResources().getDisplayMetrics().widthPixels;
//屏幕宽度 减去 (左右两边的宽度乘item列数),再除以列数就得到了图片显示在屏幕时的宽度
showWidth = (screenWidthPx - (marginLeft + marginRight) * spanCount) / spanCount;
}

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

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ImgEntity imgEntity = photoList.get(position);
if (imgEntity.getShowHeight() == -1){ //如果等于-1说明是第一次加载,计算它的显示在屏幕时的高度
int showHeight = (int) (imgEntity.getImgHeight() * (1.0f * showWidth / imgEntity.getImgWidth()));
imgEntity.setShowHeight(showHeight);
}

holder.imageView.getLayoutParams().height = imgEntity.getShowHeight(); //设置imageView高度

//使imageView圆角
RoundedCorners roundedCorners = new RoundedCorners(itemRound);
RequestOptions override = RequestOptions.bitmapTransform(roundedCorners).override(300, 300);
Glide.with(context).load(photoList.get(position).getFile()).apply(override).into(holder.imageView);
}

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

public class ViewHolder extends RecyclerView.ViewHolder {
private final ImageView imageView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.imageView);
}
}
}

使用时

  代码中用到的FileUtils.getPathBitmap(path)是封装的一个方法,具体使用为通过图片文件的地址,获取图片的bitmap

1
2
3
4
5
6
7
8
9
10
11
12
public class FileUtils{
public static Bitmap getPathBitmap(String filePath){
File file = new File(filePath);
try {
FileInputStream fileInputStream = new FileInputStream(file);
return BitmapFactory.decodeStream(fileInputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
}

  实战开发中图片的长宽大都由服务器提供

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
58
59
60
61
public class MainActivity extends AppCompatActivity {
private int spanCount = 2; //列数
private int itemRound = 6; //item的圆角度
private int marginLeft = 10; //item左边的外边距
private int marginTop = 10; //item顶部的外边距
private int marginRight = 10; //item右边的外边距
private int marginBottom = 10; //item底部的外边距

private RecyclerView recyclerView;
private List<ImgEntity> photoList = new ArrarList<>();

@Override
protect void onCreate(Bundle bundle) {
super.onCreate(bundle);
recyclerView = findViewById(R.id.recyclerView);

File folder = new File("图片文件所在的文件夹地址");
File[] files = folder.listFiles();
if (files != null && files.length > 0){
for (File file : files){
// 仅添加PNG图片
if (FileUtils.getFileType(file).equals("png")){
ImgEntity imgEntity = new ImgEntity(); // 闪照实体

imgEntity.setFile(file); //设置文件对象

Bitmap pathBitmap = FileUtils.getPathBitmap(file.getPath());
int imgWidth = pathBitmap == null ? 0 : pathBitmap.getWidth();
int imgHeight = pathBitmap == null ? 0 : pathBitmap.getHeight();
imgEntity.setImgWidth(imgWidth); //设置图片文件宽度
imgEntity.setImgHeight(imgHeight); //设置图片高度

photoList.add(imgEntity);
}
}
}

// 瀑布流布局,第一个参数控制行数/列数,第二个参数控制布局方向
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL);
// 创建适配器
FlashRecyclerAdapter flashRecyclerAdapter = new FlashRecyclerAdapter(
this,
photoList,
spanCount,
itemRound,
marginLeft, marginTop, marginRight, marginBottom
);

// 给item添加边距
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.set(marginLeft, marginTop, marginRight, marginBottom); //给item添加左上右下外边距
}
});

recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(flashRecyclerAdapter);
}
}

效果

  不要在意其它细节,实现完成后大概就是这个样子了