Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

RV LayoutManager 流式布局


目录

流式布局

使用

.setLayoutManager(new FlowLayoutManager());

测试代码

public static List<TemplateTag> getTemplateTagList() {
List<TemplateTag> list = new ArrayList<>();
int colors[] = new int[10]; for (int i = 0, j = colors.length; i < j; i++) {
colors[i] = 0xFF000000 + new Random().nextInt(0xFFFFFF);
//注意,Integer.MAX_VALUE= 0x7fffffff,超过这个值就是负数了,所以通过随机数获取int值时,传入的值要小于0x7fffffff
}
list.add(new TemplateTag("白乾涛", colors[0]));
list.add(new TemplateTag("tag", colors[1]));
list.add(new TemplateTag("标签 tag", colors[2]));
list.add(new TemplateTag("1", colors[3]));
list.add(new TemplateTag("颜色 " + colors[4], colors[4]));
list.add(new TemplateTag("包青天", colors[5]));
list.add(new TemplateTag("包青天包青天", colors[6]));
list.add(new TemplateTag("包青天包青天包青天包青天包青天包青天包青天", colors[7]));
list.add(new TemplateTag("包青天天包青天", colors[8]));
list.add(new TemplateTag("包青天123456789123456789123456789123456789123456789", colors[9]));
return list;
}

根据颜色设置背景

tvContent.setBackground(getDrawable(mTemplateTag.color));
private GradientDrawable getDrawable(int bgColor) {
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setColor(bgColor);
gradientDrawable.setCornerRadius(DisplayUtil.dip2px(ApplicationContext.getContext(), 4));
return gradientDrawable;
}

FlowLayoutManager

/**
* 描述:一种流式布局的LayoutManager<p>
* 添加时间:2017/12/26 <p>
* 开发者:<a href="http://www.cnblogs.com/baiqiantao">白乾涛</a><p>
*/
public class FlowLayoutManager extends RecyclerView.LayoutManager { private static final String TAG = FlowLayoutManager.class.getSimpleName();
final FlowLayoutManager self = this; private int width, height;
private int left, top, right;
//最大容器的宽度
private int usedMaxWidth;
//竖直方向上的偏移量
private int verticalScrollOffset = 0; public int getTotalHeight() {
return totalHeight;
} //计算显示的内容的高度
private int totalHeight = 0;
private Row row = new Row();
private List<Row> lineRows = new ArrayList<>(); //保存所有的Item的上下左右的偏移量信息
private SparseArray<Rect> allItemFrames = new SparseArray<>(); public FlowLayoutManager() {
//设置主动测量规则,适应recyclerView高度为wrap_content
setAutoMeasureEnabled(true);
} //每个item的定义
public class Item {
int useHeight;
View view; public void setRect(Rect rect) {
this.rect = rect;
} Rect rect; public Item(int useHeight, View view, Rect rect) {
this.useHeight = useHeight;
this.view = view;
this.rect = rect;
}
} //行信息的定义
public class Row {
public void setCuTop(float cuTop) {
this.cuTop = cuTop;
} public void setMaxHeight(float maxHeight) {
this.maxHeight = maxHeight;
} //每一行的头部坐标
float cuTop;
//每一行需要占据的最大高度
float maxHeight;
//每一行存储的item
List<Item> views = new ArrayList<>(); public void addViews(Item view) {
views.add(view);
}
} @Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
} //该方法主要用来获取每一个item在屏幕上占据的位置
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
Log.d(TAG, "onLayoutChildren");
if (getItemCount() == 0) {
detachAndScrapAttachedViews(recycler);
verticalScrollOffset = 0;
return;
}
if (getChildCount() == 0 && state.isPreLayout()) {
return;
}
//onLayoutChildren方法在RecyclerView 初始化时 会执行两遍
detachAndScrapAttachedViews(recycler);
if (getChildCount() == 0) {
width = getWidth();
height = getHeight();
left = getPaddingLeft();
right = getPaddingRight();
top = getPaddingTop();
usedMaxWidth = width - left - right;
}
totalHeight = 0;
int cuLineTop = top;
//当前行使用的宽度
int cuLineWidth = 0;
int itemLeft;
int itemTop;
int maxHeightItem = 0;
row = new Row();
lineRows.clear();
allItemFrames.clear();
removeAllViews();
for (int i = 0; i < getItemCount(); i++) {
Log.d(TAG, "index:" + i);
View childAt = recycler.getViewForPosition(i);
if (View.GONE == childAt.getVisibility()) {
continue;
}
measureChildWithMargins(childAt, 0, 0);
int childWidth = getDecoratedMeasuredWidth(childAt);
int childHeight = getDecoratedMeasuredHeight(childAt);
int childUseWidth = childWidth;
int childUseHeight = childHeight;
//如果加上当前的item还小于最大的宽度的话
if (cuLineWidth + childUseWidth <= usedMaxWidth) {
itemLeft = left + cuLineWidth;
itemTop = cuLineTop;
Rect frame = allItemFrames.get(i);
if (frame == null) {
frame = new Rect();
}
frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);
allItemFrames.put(i, frame);
cuLineWidth += childUseWidth;
maxHeightItem = Math.max(maxHeightItem, childUseHeight);
row.addViews(new Item(childUseHeight, childAt, frame));
row.setCuTop(cuLineTop);
row.setMaxHeight(maxHeightItem);
} else {
//换行
formatAboveRow();
cuLineTop += maxHeightItem;
totalHeight += maxHeightItem;
itemTop = cuLineTop;
itemLeft = left;
Rect frame = allItemFrames.get(i);
if (frame == null) {
frame = new Rect();
}
frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);
allItemFrames.put(i, frame);
cuLineWidth = childUseWidth;
maxHeightItem = childUseHeight;
row.addViews(new Item(childUseHeight, childAt, frame));
row.setCuTop(cuLineTop);
row.setMaxHeight(maxHeightItem);
}
//不要忘了最后一行进行刷新下布局
if (i == getItemCount() - 1) {
formatAboveRow();
totalHeight += maxHeightItem;
} }
totalHeight = Math.max(totalHeight, getVerticalSpace());
fillLayout(recycler, state);
} //对出现在屏幕上的item进行展示,超出屏幕的item回收到缓存中
private void fillLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.isPreLayout()) { // 跳过preLayout,preLayout主要用于支持动画
return;
} // 当前scroll offset状态下的显示区域
Rect displayFrame = new Rect(getPaddingLeft(), getPaddingTop() + verticalScrollOffset,
getWidth() - getPaddingRight(), verticalScrollOffset + (getHeight() - getPaddingBottom())); //对所有的行信息进行遍历
for (int j = 0; j < lineRows.size(); j++) {
Row row = lineRows.get(j);
float lineTop = row.cuTop;
float lineBottom = lineTop + row.maxHeight;
//如果该行在屏幕中,进行放置item
if (lineTop < displayFrame.bottom && displayFrame.top < lineBottom) {
List<Item> views = row.views;
for (int i = 0; i < views.size(); i++) {
View scrap = views.get(i).view;
measureChildWithMargins(scrap, 0, 0);
addView(scrap);
Rect frame = views.get(i).rect;
//将这个item布局出来
layoutDecoratedWithMargins(scrap,
frame.left,
frame.top - verticalScrollOffset,
frame.right,
frame.bottom - verticalScrollOffset);
}
} else {
//将不在屏幕中的item放到缓存中
List<Item> views = row.views;
for (int i = 0; i < views.size(); i++) {
View scrap = views.get(i).view;
removeAndRecycleView(scrap, recycler);
}
}
}
} /**
* 计算每一行没有居中的viewgroup,让居中显示
*/
private void formatAboveRow() {
List<Item> views = row.views;
for (int i = 0; i < views.size(); i++) {
Item item = views.get(i);
View view = item.view;
int position = getPosition(view);
//如果该item的位置不在该行中间位置的话,进行重新放置
if (allItemFrames.get(position).top < row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2) {
Rect frame = allItemFrames.get(position);
if (frame == null) {
frame = new Rect();
}
frame.set(allItemFrames.get(position).left, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2),
allItemFrames.get(position).right, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2 + getDecoratedMeasuredHeight(view)));
allItemFrames.put(position, frame);
item.setRect(frame);
views.set(i, item);
}
}
row.views = views;
lineRows.add(row);
row = new Row();
} /**
* 竖直方向需要滑动的条件
*
* @return
*/
@Override
public boolean canScrollVertically() {
return true;
} //监听竖直方向滑动的偏移量
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
RecyclerView.State state) { Log.d("TAG", "totalHeight:" + totalHeight);
//实际要滑动的距离
int travel = dy; //如果滑动到最顶部
if (verticalScrollOffset + dy < 0) {//限制滑动到顶部之后,不让继续向上滑动了
travel = -verticalScrollOffset;//verticalScrollOffset=0
} else if (verticalScrollOffset + dy > totalHeight - getVerticalSpace()) {//如果滑动到最底部
travel = totalHeight - getVerticalSpace() - verticalScrollOffset;//verticalScrollOffset=totalHeight - getVerticalSpace()
} //将竖直方向的偏移量+travel
verticalScrollOffset += travel; // 平移容器内的item
offsetChildrenVertical(-travel);
fillLayout(recycler, state);
return travel;
} private int getVerticalSpace() {
return self.getHeight() - self.getPaddingBottom() - self.getPaddingTop();
} public int getHorizontalSpace() {
return self.getWidth() - self.getPaddingLeft() - self.getPaddingRight();
} }

2017-12-27

RV LayoutManager 流式布局 MD的更多相关文章

  1. 流式布局&固定宽度&响应式&rem

    我们现在在切页面布局的使用常用的单位是px,这是一个绝对单位,web app的屏幕适配有很多中做法,例如:流式布局.限死宽度,还有就是通过响应式来做,但是这些方案都不是最佳的解决方法. 1.流式布局: ...

  2. webapp,liveapp: 流式布局和rem布局

    liveapp场景应用,一般针对的是移动端,近来也是很火,颇有一些感受,拿来分享一下. 页面宽度范围: 一般移动端页面我们的像素范围是320px-640px,最大640px,最小320px,所以设计稿 ...

  3. JAVA 流式布局管理器

    //流式布局管理器 import java.awt.*; import javax.swing.*; public class Jiemian2 extends JFrame{ //定义组件 JBut ...

  4. 自定义View(三)--实现一个简单地流式布局

    Android中的流式布局也就是常说的瀑布流很是常见,不仅在很多项目中都能见到,而且面试中也有很多面试官问道,那么什么是流式布局呢?简单来说就是如果当前行的剩余宽度不足以摆放下一个控件的时候,则自动将 ...

  5. Android 自动换行流式布局的RadioGroup

    效果图 用法 使用FlowRadioGroup代替RadioGroup 代码 import android.content.Context; import android.util.Attribute ...

  6. Android 自定义View修炼-Android中常见的热门标签的流式布局的实现

    一.概述:在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧(源码下载在下面最后给出哈) 类似的 ...

  7. 转:Java图形化界面设计——布局管理器之FlowLayout(流式布局)其他请参考转载出处网址

    http://blog.csdn.net/liujun13579/article/details/7771191 前文讲解了JFrame.JPanel,其中已经涉及到了空布局的使用.Java虽然可以以 ...

  8. 自定义ViewGroup 流式布局

    使用 public class MainActivity extends Activity {     @Override     protected void onCreate(Bundle sav ...

  9. Android流式布局实现

    查看我的所有开源项目[开源实验室] 欢迎增加我的QQ群:[201055521],本博客client下载[请点击] 摘要 新项目用到了一种全新布局----Android标签流式布局的功能,正好一直说给大 ...

随机推荐

  1. EditText 数字范围 检查string 是不是数字

    public static boolean isNumeric00(String str){ try{ Integer.parseInt(str); return true; }catch(Numbe ...

  2. hadoop 视频教程3之实战教程

    一,视频内容: 海量数据处理平台框架 hadoop介绍 hadoop 生态系统介绍 hdfs 设计原则 hdfs 系统架构 namenode datanode secondarynamenode hd ...

  3. 负载均衡介绍及Nginx简单实现

    负载均衡介绍及Nginx简单实现 负载均衡 负载均衡介绍及Nginx简单实现 1. 介绍 2. 常用的开源软件 2.1 LVS 优点 缺点 2.2 Nginx 优点 缺点 3. 常用的开源反向代理软件 ...

  4. Xtreme8.0 - Magic Square 水题

    Xtreme8.0 - Magic Square 题目连接: https://www.hackerrank.com/contests/ieeextreme-challenges/challenges/ ...

  5. 解决jQueryUi AutoComplete在某些浏览器下无法出现候选项问题

    在某些浏览器(如火狐),在使用AutoComplete进行绑定的时候,无法出现与关键字相似的候选项.其原因这里有描述: 解决方法可以采用下面方式: $('#bindInputId).bind(&quo ...

  6. java 虚拟机启动参数 (转)

    在Java.J2EE大型应用中,JVM非标准参数的配置直接关系到整个系统的性能. JVM非标准参数指的是JVM底层的一些配置参数,这些参数在一般开发中默认即可,不需要任何配置.但是在生产环境中,为了提 ...

  7. deeplearningbook-chinese

    https://exacity.github.io/deeplearningbook-chinese/

  8. WebLogic使用总结(二)——WebLogic卸载

    一.WebLogic 12c的卸载 WebLogic的卸载是非常容易的,找到WebLogic的卸载程序,如下图所示: 启动卸载程序,如下图所示:

  9. (转)java并发编程--Executor框架

    本文转自https://www.cnblogs.com/MOBIN/p/5436482.html java并发编程--Executor框架 只要用到线程,就可以使用executor.,在开发中如果需要 ...

  10. .NET:不要使用扩展方法扩展Object对象。

    C#的扩展方法算是一种Minin(掺入)机制,掺入方法有其合理的使用场景,这里说说一种不好的使用场景(个人意见):不要使用扩展方法扩展Object对象.扩展Object会对所有类型的示例有侵入,特别是 ...