網(wǎng)上很多流式布局不支持滑動(dòng),由于我們的需求要求多的時(shí)候必須能滑動(dòng),于是我就想到了recyclerview,改造他們manager
public class FlowLayoutManagerextends RecyclerView.LayoutManager {
private static final StringTAG = FlowLayoutManager.class.getSimpleName();
? ? final FlowLayoutManagerself =this;
? ? protected int width, height;
? ? private int left, top, right;
? ? //最大容器的寬度
? ? private int usedMaxWidth;
? ? //豎直方向上的偏移量
? ? private int verticalScrollOffset =0;
? ? public int getTotalHeight() {
return totalHeight;
? ? }
//計(jì)算顯示的內(nèi)容的高度
? ? protected int totalHeight =0;
? ? private Rowrow =new Row();
? ? private ListlineRows =new ArrayList<>();
? ? //保存所有的Item的上下左右的偏移量信息
? ? private SparseArrayallItemFrames =new SparseArray<>();
? ? public FlowLayoutManager() {
//設(shè)置主動(dòng)測(cè)量規(guī)則,適應(yīng)recyclerView高度為wrap_content
? ? ? ? setAutoMeasureEnabled(true);
? ? }
//每個(gè)item的定義
? ? public class Item {
int useHeight;
? ? ? ? Viewview;
? ? ? ? public void setRect(Rect rect) {
this.rect = rect;
? ? ? ? }
Rectrect;
? ? ? ? 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;
? ? ? ? }
//每一行的頭部坐標(biāo)
? ? ? ? float cuTop;
? ? ? ? //每一行需要占據(jù)的最大高度
? ? ? ? float maxHeight;
? ? ? ? //每一行存儲(chǔ)的item
? ? ? ? Listviews =new ArrayList<>();
? ? ? ? public void addViews(Item view) {
views.add(view);
? ? ? ? }
}
@Override
? ? public RecyclerView.LayoutParamsgenerateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
? ? }
//該方法主要用來(lái)獲取每一個(gè)item在屏幕上占據(jù)的位置
? ? @Override
? ? public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
Log.d(TAG, "onLayoutChildren");
? ? ? ? totalHeight =0;
? ? ? ? int cuLineTop =top;
? ? ? ? //當(dāng)前行使用的寬度
? ? ? ? int cuLineWidth =0;
? ? ? ? int itemLeft;
? ? ? ? int itemTop;
? ? ? ? int maxHeightItem =0;
? ? ? ? row =new Row();
? ? ? ? lineRows.clear();
? ? ? ? allItemFrames.clear();
? ? ? ? removeAllViews();
? ? ? ? if (getItemCount() ==0) {
detachAndScrapAttachedViews(recycler);
? ? ? ? ? ? verticalScrollOffset =0;
return;
? ? ? ? }
if (getChildCount() ==0 && state.isPreLayout()) {
return;
? ? ? ? }
//onLayoutChildren方法在RecyclerView 初始化時(shí) 會(huì)執(zhí)行兩遍
? ? ? ? detachAndScrapAttachedViews(recycler);
? ? ? ? if (getChildCount() ==0) {
width = getWidth();
? ? ? ? ? ? height = getHeight();
? ? ? ? ? ? left = getPaddingLeft();
? ? ? ? ? ? right = getPaddingRight();
? ? ? ? ? ? top = getPaddingTop();
? ? ? ? ? ? usedMaxWidth =width -left -right;
? ? ? ? }
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;
? ? ? ? ? ? //如果加上當(dāng)前的item還小于最大的寬度的話(huà)
? ? ? ? ? ? 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);
? ? ? ? ? ? }
//不要忘了最后一行進(jìn)行刷新下布局
? ? ? ? ? ? if (i == getItemCount() -1) {
formatAboveRow();
? ? ? ? ? ? ? ? totalHeight += maxHeightItem;
? ? ? ? ? ? }
}
totalHeight = Math.max(totalHeight, getVerticalSpace());
? ? ? ? Log.d(TAG, "onLayoutChildren totalHeight:" +totalHeight);
? ? ? ? fillLayout(recycler, state);
? ? }
//對(duì)出現(xiàn)在屏幕上的item進(jìn)行展示,超出屏幕的item回收到緩存中
? ? private void fillLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.isPreLayout() || getItemCount() ==0) {// 跳過(guò)preLayout,preLayout主要用于支持動(dòng)畫(huà)
? ? ? ? ? ? return;
? ? ? ? }
// 當(dāng)前scroll offset狀態(tài)下的顯示區(qū)域
? ? ? ? Rect displayFrame =new Rect(getPaddingLeft(), getPaddingTop() +verticalScrollOffset,
? ? ? ? ? ? ? ? getWidth() - getPaddingRight(), verticalScrollOffset + (getHeight() - getPaddingBottom()));
? ? ? ? //對(duì)所有的行信息進(jìn)行遍歷
? ? ? ? for (int j =0; j
Row row =lineRows.get(j);
? ? ? ? ? ? float lineTop = row.cuTop;
? ? ? ? ? ? float lineBottom = lineTop + row.maxHeight;
? ? ? ? ? ? //如果該行在屏幕中,進(jìn)行放置item
//? ? ? ? ? ? if (lineTop < displayFrame.bottom && displayFrame.top < lineBottom) {
? ? ? ? ? ? List 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;
? ? ? ? ? ? ? ? //將這個(gè)item布局出來(lái)
? ? ? ? ? ? ? ? layoutDecoratedWithMargins(scrap,
? ? ? ? ? ? ? ? ? ? ? ? frame.left,
? ? ? ? ? ? ? ? ? ? ? ? frame.top -verticalScrollOffset,
? ? ? ? ? ? ? ? ? ? ? ? frame.right,
? ? ? ? ? ? ? ? ? ? ? ? frame.bottom -verticalScrollOffset);
? ? ? ? ? ? }
//? ? ? ? ? ? } else {
//? ? ? ? ? ? ? ? //將不在屏幕中的item放到緩存中
//? ? ? ? ? ? ? ? List views = row.views;
//? ? ? ? ? ? ? ? for (int i = 0; i < views.size(); i++) {
//? ? ? ? ? ? ? ? ? ? View scrap = views.get(i).view;
//? ? ? ? ? ? ? ? ? ? removeAndRecycleView(scrap, recycler);
//? ? ? ? ? ? ? ? }
//? ? ? ? ? ? }
? ? ? ? }
}
/**
* 計(jì)算每一行沒(méi)有居中的viewgroup,讓居中顯示
*/
? ? private void formatAboveRow() {
List 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的位置不在該行中間位置的話(huà),進(jìn)行重新放置
? ? ? ? ? ? if (allItemFrames.get(position).top
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();
? ? }
/**
* 豎直方向需要滑動(dòng)的條件
*
? ? * @return
? ? */
? ? @Override
? ? public boolean canScrollVertically() {
return true;
? ? }
//監(jiān)聽(tīng)豎直方向滑動(dòng)的偏移量
? ? @Override
? ? public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? RecyclerView.State state) {
Log.d("TAG", "totalHeight:" +totalHeight);
? ? ? ? //實(shí)際要滑動(dòng)的距離
? ? ? ? int travel = dy;
? ? ? ? //如果滑動(dòng)到最頂部
? ? ? ? if (verticalScrollOffset + dy <0) {//限制滑動(dòng)到頂部之后,不讓繼續(xù)向上滑動(dòng)了
? ? ? ? ? ? travel = -verticalScrollOffset;//verticalScrollOffset=0
? ? ? ? }else if (verticalScrollOffset + dy >totalHeight - getVerticalSpace()) {//如果滑動(dòng)到最底部
? ? ? ? ? ? travel =totalHeight - getVerticalSpace() -verticalScrollOffset;//verticalScrollOffset=totalHeight - getVerticalSpace()
? ? ? ? }
//將豎直方向的偏移量+travel
? ? ? ? verticalScrollOffset += travel;
? ? ? ? // 平移容器內(nèi)的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();
? ? }
}
就這么一個(gè)類(lèi)搞定,接下來(lái)的用法和recyclerview的用法一樣了
