可以滑動(dòng)流式布局

網(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的用法一樣了


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容