生活不止有眼前的苟且,还有诗和远方。不,还有未来的苟且和将就。不停留于目前的苟且,为了不在当前做一条咸鱼,那么需要翻个身,一点点努力,总有咸鱼翻身之时。
前不久公司要实现类似下图的效果
一看到效果图,首先就想到应该获取宽和高,然后一行动态拼接两张图,那么如何使两张图搞好拼接成一行,并且高度一样呢。
我们来计算一下: 一行有两张图,第一张图的宽w1、高h1,第二张图的宽w2,高h2,屏幕的宽为sw(screen width)。 假设算出来第一张图为w,第二张图宽度为sw-w,而且两张图高度一致,所以有以下公式:
w*h1/w1 = h2/w2*(sw-w)
一个公式只有一个未知变量,所以可以求解出w的值
接下来就是代码实现过程,我们考虑用RecyclerView 去实现,一开始打算用瀑布流去实现。但是考虑用瀑布流去实现,但是后来发觉瀑布流支持 item 占据总列数的宽度,不支持只占据其中几列。假如图片一张宽度很小,一张很大如何去实现呢(如果有大神知道如何实现,请不吝指导!)
后来去星球提问,有网友推荐用Google的flex布局管理器,咋一看,这不是前端的flex布局吗?还真别说,就是就是前端的流式布局,连用法都很像。这里就不介绍用法了,想学习的话推荐其他网友的博客!Android 流式布局-FlexboxLayout与RecyclerView
一、FlexboxLayout实现 错落有致布局
效果如下: !源代码地址
- 首先引入流式布局
implementation 'com.google.android:flexbox:0.3.0-alpha2'//流式布局
- 初始化布局
private void initView() {
gallleryRecyclerView = findViewById(R.id.gallery_recyclerview);
mFlexboxLayoutManager = new FlexboxLayoutManager();
mFlexboxLayoutManager.setFlexDirection(FlexDirection.ROW);
mFlexboxLayoutManager.setJustifyContent(JustifyContent.SPACE_BETWEEN);
mFlexboxLayoutManager.setAlignItems(AlignItems.FLEX_START);
gallleryRecyclerView.setLayoutManager(mFlexboxLayoutManager);
}
gallleryRecyclerView 定义Recyclew,mFlexboxLayoutManager为流式布局管理类,mFlexboxLayoutManager.setFlexDirection(FlexDirection.ROW) 设置主轴为水平方向,起点在左端,从左到右; mFlexboxLayoutManager.setAlignItems(AlignItems.FLEX_START)顶端对齐;gallleryRecyclerView.setLayoutManager(mFlexboxLayoutManager);把管理类赋值给Recyclerview.
- 定义一组美女图片
private void initData() {
imageList = new ArrayList<>(Arrays.asList(R.mipmap.girl1,R.mipmap.girl2,R.mipmap.girl3,R.mipmap.girl4,R.mipmap.girl5
,R.mipmap.girl6,R.mipmap.girl7,R.mipmap.girl8,R.mipmap.girl9));
mImageBeanList = new ArrayList<>();
for (int i=0;i<imageList.size();i++){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(),imageList.get(i),options);
int width = options.outWidth;
int height = options.outHeight;
ImageBean imageBean = new ImageBean();
imageBean.setResourceId(imageList.get(i));
imageBean.setWidth(width);
imageBean.setHeight(height);
mImageBeanList.add(imageBean);
}
mFlexBoxAdapter = new FlexBoxAdapter(FlexboxActivity.this,mImageBeanList);
gallleryRecyclerView.setAdapter(mFlexBoxAdapter);
gallleryRecyclerView.setHasFixedSize(false);
}
赋值给FlexBoxAdapter
- 定义Adaper
public class FlexBoxAdapter extends RecyclerView.Adapter<FlexBoxAdapter.ViewHolder> {
private List<ImageBean> mPicturesBeanList;
private Context mContext;
private float screenWidth;
public FlexBoxAdapter(Context context, List<ImageBean> imageBeans) {
this.mPicturesBeanList = imageBeans;
this.mContext = context;
filterData();
}
private void filterData() {
screenWidth = (float) ScreenUtils.getScreenWidth(mContext);
int i = 0;
while (i < this.mPicturesBeanList.size()) {
if (i % 2 == 0 && i + 1 < this.mPicturesBeanList.size()) {
long divisor = (long) (screenWidth * (this.mPicturesBeanList.get(i + 1).getHeight()) * (this.mPicturesBeanList.get(i).getWidth()));
long beDivisor = this.mPicturesBeanList.get(i + 1).getWidth() * this.mPicturesBeanList.get(i).getHeight() + this.mPicturesBeanList.get(i).getWidth() * this.mPicturesBeanList.get(i + 1).getHeight();
int w = (int) (divisor / beDivisor);
int h = w * this.mPicturesBeanList.get(i).getHeight() / this.mPicturesBeanList.get(i).getWidth();
this.mPicturesBeanList.get(i).setChangeWidth(w);
this.mPicturesBeanList.get(i).setChangeHeight(h);
int tW = (int) (screenWidth - screenWidth * this.mPicturesBeanList.get(i + 1).getHeight() * this.mPicturesBeanList.get(i).getWidth() / (this.mPicturesBeanList.get(i + 1).getWidth() * this.mPicturesBeanList.get(i).getHeight() + this.mPicturesBeanList.get(i).getWidth() * this.mPicturesBeanList.get(i + 1).getHeight()));
int tH = tW * this.mPicturesBeanList.get(i + 1).getHeight() / this.mPicturesBeanList.get(i + 1).getWidth();
this.mPicturesBeanList.get(i + 1).setChangeWidth(tW);
this.mPicturesBeanList.get(i + 1).setChangeHeight(tH);
}
if (i == mPicturesBeanList.size() - 1 && mPicturesBeanList.size() % 2 == 1) {
int w = (int) screenWidth;
int h = w * mPicturesBeanList.get(i).getHeight() / mPicturesBeanList.get(i).getWidth();
this.mPicturesBeanList.get(i).setChangeWidth(w);
this.mPicturesBeanList.get(i).setChangeHeight(h);
}
i++;
}
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
......
}
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
......
}
@Override
public int getItemCount() {
return this.mPicturesBeanList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView mImageView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
mImageView = itemView.findViewById(R.id.mine_photo_iv);
}
}
}
filterData()这个方法动态计算图片适合的宽高,使两张图片刚好铺满一行。
想看完整代码可以到源代码地址
完美的实现出来了,打开wegame,准备召唤师峡谷见。但是产品一个电话突然打过来,要加头部和底部布局,这。。。flex布局如何自定义头部和底部呢,而且头部要占一行,底部也要占一行,苦思冥想,而且flex布局用的也不多。要是是网格布局多好啊,直接自定义头部和底部,轻松搞定召唤师峡谷搞起。
网格布局 GridLayoutManager 有个setSpanSizeLookup()方法,可以指定item占几行,同一行的高度也是一样。和我现在的需求就是宽度要如何设置。
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3, LinearLayoutManager.HORIZONTAL, false);
//自定义item占据的小格大小时需要重写 getSpanSize(),返回值就是占据的小格数量
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
//以下代码仅为上图示例为写,具体场景中应该根据需求具体编写
if (position == 1) {
return 2;
}
if (position == 2) {
return 3;
}
return 1;
}
像这样就能指定一行3个Item,根据position来指定item所占宽度。
二、GirdLayoutt实现 错落有致布局
不得不说,灵感这种东西,不止诗人、歌手需要,有时候代码世界也需要灵光一现。那我为何不设置屏幕宽度为总的个数呢?图片宽度为多少,就是多少格呢?
mGirdLayoutManager = new GridLayoutManager(this, ScreenUtils.getScreenWidth(GridLayoutActivity.this));
gallleryRecyclerView.setLayoutManager(mGirdLayoutManager);
- 看下GridLayoutManager的定义
/**
* Creates a vertical GridLayoutManager
*
* @param context Current context, will be used to access resources.
* @param spanCount The number of columns in the grid
*/
public GridLayoutManager(Context context, int spanCount) {
super(context);
setSpanCount(spanCount);
}
spanCount就是个数多少,现在定义为ScreenUtils.getScreenWidth(GridLayoutActivity.this),即屏幕的宽度。
- 网格布局的adater
package com.lqg.Image;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import java.util.List;
public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.ViewHolder> {
private List<ImageBean> mPicturesBeanList;
private Context mContext;
public static final int TYPE_HEADER = 0;
public static final int TYPE_NORMAL = 1;
public static final int TYPE_BOTTOM = 2;
private View mHeaderView;
private View mBottomView;
private float screenWidth;
public GalleryAdapter(Context context, List<ImageBean> imageBeans) {
this.mContext = context;
this.mPicturesBeanList = imageBeans;
filterData();
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if (manager instanceof GridLayoutManager) {
final GridLayoutManager gridManager = ((GridLayoutManager) manager);
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return getItemViewType(position) == TYPE_HEADER || getItemViewType(position) == TYPE_BOTTOM
? gridManager.getSpanCount() : mPicturesBeanList.get(position - 1).getChangeWidth();
}
});
}
}
@Override
public int getItemViewType(int position) {
if (mHeaderView == null) return TYPE_NORMAL;
if (mBottomView == null) return TYPE_NORMAL;
if (position == 0) return TYPE_HEADER;
if (position == this.getItemCount() - 1) return TYPE_BOTTOM;
return TYPE_NORMAL;
}
private void filterData() {
......
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
if (mHeaderView != null && i == TYPE_HEADER) return new ViewHolder(mHeaderView);
if (mBottomView != null && i == TYPE_BOTTOM) return new ViewHolder(mBottomView);
View view = LayoutInflater.from(mContext).inflate(R.layout.adater_details_works_item, null, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, final int i) {
......
}
public int getRealPosition(RecyclerView.ViewHolder holder) {
int position = holder.getLayoutPosition();
return mHeaderView == null ? position : position - 1;
}
@Override
public int getItemCount() {
return mPicturesBeanList.size() + 2;
}
public class ViewHolder extends RecyclerView.ViewHolder {
......
}
}
关键看
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return getItemViewType(position) == TYPE_HEADER || getItemViewType(position) == TYPE_BOTTOM
? gridManager.getSpanCount() : mPicturesBeanList.get(position - 1).getChangeWidth();
}
});
根据item类型,获取item的格数,头部和底部各占一行。正常图片的item根据计算好的图片宽度获取个数。 最终效果如下:
想看完整代码可以到源代码地址
做的过程中,学习了别人的知识点,果然站在巨人的肩膀才能看的远!