Android 折疊式流式布局

1631254796076.gif

最近有個(gè)需求察迟,搜索的歷史記錄霉赡,超過(guò)兩行需要折疊变隔,后面有個(gè)展示展開的按鈕磁椒,點(diǎn)擊按鈕展示4行數(shù)據(jù)点额。和京東的一樣狈癞,只是京東會(huì)展示全部夯到,但是我們是只會(huì)展示4行枣申。本以為會(huì)簡(jiǎn)單售葡,所以在網(wǎng)上找到了對(duì)應(yīng)的代碼http://www.reibang.com/p/530e9993f8bc,但是他的這個(gè)例子有個(gè)問(wèn)題,就是添加一個(gè)新數(shù)據(jù)忠藤,折疊和收縮的按鈕丟失了挟伙。于是,基于他的代碼模孩,砸門修改一下尖阔。
先來(lái)一個(gè)基本的自定義流式布局

public class FlowLayout extends ViewGroup {
    /**
     * 默認(rèn)折疊狀態(tài)
     */
    private static final boolean DEFAULT_FOLD = false;
    /**
     * 折疊的行數(shù)
     */
    private static final int DEFAULT_FOLD_LINES = 1;
    /**
     * 左對(duì)齊
     */
    private static final int DEFAULT_GRAVITY_LEFT = 0;
    /**
     * 右對(duì)齊
     */
    private static final int DEFAULT_GRAVITY_RIGHT = 1;

    /**
     * 是否折疊,默認(rèn)false不折疊
     */
    protected boolean mFold;
    /**
     * 折疊行數(shù)
     */
    private int mFoldLines = DEFAULT_FOLD_LINES;
    /**
     * 對(duì)齊 默認(rèn)左對(duì)齊
     */
    private int mGravity = DEFAULT_GRAVITY_LEFT;
    /**
     * 折疊狀態(tài)
     */
    private Boolean mFoldState;
    /**
     * 是否平均
     */
    private boolean mEqually;
    /**
     * 一行平局?jǐn)?shù)量
     */
    private int mEquallyCount;
    /**
     * 水平距離
     */
    private int mHorizontalSpacing;
    /**
     * 豎直距離
     */
    private int mVerticalSpacing;

    private static final int MAX_LINE = 3;//從0開始計(jì)數(shù)

    public int maxSize = 9;


    private OnFoldChangedListener mOnFoldChangedListener;

    public FlowLayout(Context context) {
        this(context, null);
    }

    public FlowLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
        mFold = a.getBoolean(R.styleable.FlowLayout_flow_fold, DEFAULT_FOLD);
        mFoldLines = a.getInt(R.styleable.FlowLayout_flow_foldLines, DEFAULT_FOLD_LINES);
        mGravity = a.getInt(R.styleable.FlowLayout_flow_gravity, DEFAULT_GRAVITY_LEFT);
        mEqually = a.getBoolean(R.styleable.FlowLayout_flow_equally, true);
        mEquallyCount = a.getInt(R.styleable.FlowLayout_flow_equally_count, 0);
        mHorizontalSpacing = a.getDimensionPixelOffset(R.styleable.FlowLayout_flow_horizontalSpacing, dp2px(8));
        mVerticalSpacing = a.getDimensionPixelOffset(R.styleable.FlowLayout_flow_verticalSpacing, dp2px(8));
        a.recycle();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //當(dāng)設(shè)置折疊 折疊數(shù)設(shè)置小于0直接隱藏布局
        if (mFold && mFoldLines <= 0) {
            setVisibility(GONE);
            changeFold(true, true, 0,0, 0);
            return;
        }
        //獲取mode 和 size
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int maxIndex = 0;

        final int layoutWidth = widthSize - getPaddingLeft() - getPaddingRight();
        //判斷如果布局寬度拋去左右padding小于0瓜贾,也不能處理了
        if (layoutWidth <= 0) {
            return;
        }

        //這里默認(rèn)寬高默認(rèn)值默認(rèn)把左右诺祸,上下padding加上
        int width = getPaddingLeft() + getPaddingRight();
        int height = getPaddingTop() + getPaddingBottom();

        //初始一行的寬度
        int lineWidth = 0;
        //初始一行的高度
        int lineHeight = 0;

        //測(cè)量子View
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int[] wh = null;
        int childWidth, childHeight;
        int childWidthMeasureSpec = 0, childHeightMeasureSpec = 0;
        //行數(shù)
        int line = 0;
        //折疊的狀態(tài)
        boolean newFoldState = false;
        //發(fā)生折疊的索引
        int foldIndex = 0;
        //剩余空間
        int surplusWidth = widthSize;
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            final View view = getChildAt(i);
            //這里需要先判斷子view是否被設(shè)置了GONE
            if (view.getVisibility() == GONE) {
                continue;
            }
            //如果設(shè)置是平局顯示
            if (mEqually) {
                //這里只要計(jì)算一次就可以了
                if (wh == null) {
                    //取子view最大的寬高
                    wh = getMaxWidthHeight();
                    //求一行能顯示多少個(gè)
                    int oneRowItemCount = (layoutWidth + mHorizontalSpacing) / (mHorizontalSpacing + wh[0]);
                    //當(dāng)你設(shè)置了一行平局顯示多少個(gè)
                    if (mEquallyCount > 0) {
                        //判斷當(dāng)你設(shè)定的數(shù)量小于計(jì)算的數(shù)量時(shí),使用設(shè)置的祭芦,所以說(shuō)當(dāng)我們計(jì)算的豎直小于設(shè)置的值的時(shí)候這里并沒(méi)有強(qiáng)制設(shè)置設(shè)定的值
                        //如果需求要求必須按照設(shè)定的來(lái)筷笨,這里就不要做if判斷,直接使用設(shè)定的值龟劲,但是布局顯示會(huì)出現(xiàn)顯示不全或者...的情況胃夏。
                        //if (oneRowItemCount > mEquallyCount) {
                        /**
                         * 這里使用固定,設(shè)置了一行幾個(gè)就是集合 顯示不全,顯示"..."
                         */
                        oneRowItemCount = mEquallyCount;
                        //}
                    }
                    // 根據(jù)上面計(jì)算的一行顯示的數(shù)量來(lái)計(jì)算一個(gè)的寬度
                    int newWidth = (layoutWidth - (oneRowItemCount - 1) * mHorizontalSpacing) / oneRowItemCount;
                    wh[0] = newWidth;
                    //重新獲取子view的MeasureSpec
                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(wh[0], MeasureSpec.EXACTLY);
                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(wh[1], MeasureSpec.EXACTLY);
                }
                childWidth = wh[0];
                childHeight = wh[1];
                //重新測(cè)量子view的大小
                getChildAt(i).measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
            // 自適顯示
            else {
                childWidth = view.getMeasuredWidth();
                childHeight = view.getMeasuredHeight();
                if(!mFold && line >= 4){
                    String tag = (String) view.getTag();
                    if(TextUtils.isEmpty(tag)){
                        tag = " ";
                    }
                    view.setVisibility(GONE);
                }
            }

            //第一行
            if (i == 0) {
                lineWidth = getPaddingLeft() + getPaddingRight() + childWidth;
                lineHeight = childHeight;
            } else {
                //判斷是否需要換行
                //換行
                if (lineWidth + mHorizontalSpacing + childWidth > widthSize) {
                    line++;//行數(shù)增加
                    // 取最大的寬度
                    width = Math.max(lineWidth, width);
                    //這里判斷是否設(shè)置折疊及行數(shù)是否超過(guò)了設(shè)定值
                    if (mFold && line >= mFoldLines) {
                        line++;
                        height += lineHeight;
                        newFoldState = true;
                        surplusWidth = widthSize - lineWidth - mHorizontalSpacing;
                        break;
                    }
                    //重新開啟新行昌跌,開始記錄
                    lineWidth = getPaddingLeft() + getPaddingRight() + childWidth;
                    //疊加當(dāng)前高度仰禀,
                    height += mVerticalSpacing + lineHeight;
                    //開啟記錄下一行的高度
                    lineHeight = childHeight;
                    if(!mFold && line > MAX_LINE && maxIndex==0){
                        //如果不是折疊狀態(tài),并且數(shù)目大于4行蚕愤,并且maxIndex沒(méi)有付過(guò)值
                        maxIndex = i-1;
                    }
                }
                //不換行
                else {
                    lineWidth = lineWidth + mHorizontalSpacing + childWidth;
                    lineHeight = Math.max(lineHeight, childHeight);
                    if(!mFold && line == MAX_LINE && i >= maxSize && maxIndex==0){
                        //如果不是折疊狀態(tài)答恶,并且剛好為4行,并且maxIndex沒(méi)有付過(guò)值萍诱,并且條目剛好等于9
                        maxIndex = i;
                    }
                }
            }
            // 如果是最后一個(gè)悬嗓,則將當(dāng)前記錄的最大寬度和當(dāng)前l(fā)ineWidth做比較
            if (i == count - 1) {
                line++;
                width = Math.max(width, lineWidth);
                height += lineHeight;
            }
            foldIndex = i;
        }
        //根據(jù)計(jì)算的值重新設(shè)置
        setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : width,
                heightMode == MeasureSpec.EXACTLY ? heightSize : height);
        //折疊狀態(tài)
        changeFold(line > mFoldLines, newFoldState, foldIndex,maxIndex, surplusWidth);
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int layoutWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
        if (layoutWidth <= 0) {
            return;
        }
        int childWidth, childHeight;
        //需要加上top padding
        int top = getPaddingTop();
        final int[] wh = getMaxWidthHeight();
        int lineHeight = 0;
        int line = 0;
        //左對(duì)齊
        if (mGravity == DEFAULT_GRAVITY_LEFT) {
            //左側(cè)需要先加上左邊的padding
            int left = getPaddingLeft();
            for (int i = 0, count = getChildCount(); i < count; i++) {
                final View view = getChildAt(i);
                //這里一樣判斷下顯示狀態(tài)
                if (view.getVisibility() == GONE) {
                    continue;
                }
                //如果設(shè)置的平均 就使用最大的寬度和高度 否則直接自適寬高
                if (mEqually) {
                    childWidth = wh[0];
                    childHeight = wh[1];
                } else {
                    childWidth = view.getMeasuredWidth();
                    childHeight = view.getMeasuredHeight();
                }
                //第一行開始擺放
                if (i == 0) {
                    view.layout(left, top, left + childWidth, top + childHeight);
                    lineHeight = childHeight;
                } else {
                    //判斷是否需要換行
                    if (left + mHorizontalSpacing + childWidth > layoutWidth + getPaddingLeft()) {
                        line++;
                        if (mFold && line >= mFoldLines) {
                            line++;
                            break;
                        }
                        //重新起行
                        left = getPaddingLeft();
                        top = top + mVerticalSpacing + lineHeight;
                        lineHeight = childHeight;
                    } else {
                        left = left + mHorizontalSpacing;
                        lineHeight = Math.max(lineHeight, childHeight);
                    }
                    view.layout(left, top, left + childWidth, top + childHeight);
                }
                //累加left
                left += childWidth;
            }
        }
        //右對(duì)齊
        else {
            int paddingLeft = getPaddingLeft();
            // 相當(dāng)于getMeasuredWidth() -  getPaddingRight();
            int right = layoutWidth + paddingLeft;

            for (int i = 0, count = getChildCount(); i < count; i++) {
                final View view = getChildAt(i);
                if (view.getVisibility() == GONE) {
                    continue;
                }
                //如果設(shè)置的平均 就使用最大的寬度和高度 否則直接自適寬高
                if (mEqually) {
                    childWidth = wh[0];
                    childHeight = wh[1];
                } else {
                    childWidth = view.getMeasuredWidth();
                    childHeight = view.getMeasuredHeight();
                }
                if (i == 0) {
                    view.layout(right - childWidth, top, right, top + childHeight);
                    lineHeight = childHeight;
                } else {
                    //判斷是否需要換行
                    if (right - childWidth - mHorizontalSpacing < paddingLeft) {
                        line++;
                        if (mFold && line >= mFoldLines) {
                            line++;
                            break;
                        }
                        //重新起行
                        right = layoutWidth + paddingLeft;
                        top = top + mVerticalSpacing + lineHeight;
                        lineHeight = childHeight;
                    } else {
                        right = right - mHorizontalSpacing;
                        lineHeight = Math.max(lineHeight, childHeight);
                    }
                    view.layout(right - childWidth, top, right, top + childHeight);
                }
                right -= childWidth;
            }
        }
    }

    /**
     * 取最大的子view的寬度和高度
     *
     * @return
     */
    private int[] getMaxWidthHeight() {
        int maxWidth = 0;
        int maxHeight = 0;
        for (int i = 0, count = getChildCount(); i < count; i++) {
            final View view = getChildAt(i);
            if (view.getVisibility() == GONE) {
                continue;
            }
            maxWidth = Math.max(maxWidth, view.getMeasuredWidth());
            maxHeight = Math.max(maxHeight, view.getMeasuredHeight());
        }
        return new int[]{maxWidth, maxHeight};
    }

    /**
     * 折疊狀態(tài)改變回調(diào)
     *
     * @param canFold
     * @param newFoldState
     */
    public void changeFold(boolean canFold, boolean newFoldState, int index,int maxIndex, int surplusWidth) {
        if (mFoldState == null || mFoldState != newFoldState) {
            if (canFold) {
                mFoldState = newFoldState;
            }
            if (mOnFoldChangedListener != null) {
                mOnFoldChangedListener.onFoldChange(canFold, newFoldState, index,maxIndex, surplusWidth);
            }
        }
    }

    /**
     * 設(shè)置是否折疊
     *
     * @param fold
     */
    public void setFold(boolean fold) {
        mFold = fold;
        if (mFoldLines <= 0) {
            setVisibility(fold ? GONE : VISIBLE);
            changeFold(true, fold, 0,0, 0);
        } else {
            requestLayout();
        }
    }

    public void setFoldLines(int mFoldLines) {
        this.mFoldLines = mFoldLines;
    }

    public void setEqually(boolean mEqually) {
        this.mEqually = mEqually;
    }

    /**
     * 折疊切換,如果之前是折疊狀態(tài)就切換為未折疊狀態(tài)裕坊,否則相反
     */
    public void toggleFold() {
        setFold(!mFold);
    }


    /**
     * dp->px
     *
     * @param dp
     * @return
     */
    private int dp2px(int dp) {
        return (int) (getContext().getResources().getDisplayMetrics().density * dp);
    }


    /**
     * 設(shè)置折疊狀態(tài)回調(diào)
     *
     * @param listener
     */
    public void setOnFoldChangedListener(OnFoldChangedListener listener) {
        mOnFoldChangedListener = listener;
    }

    public interface OnFoldChangedListener {


        /**
         * 折疊狀態(tài)時(shí)時(shí)回調(diào)
         *
         * @param canFold      是否可以折疊包竹,true為可以折疊,false為不可以折疊
         * @param fold         當(dāng)前折疊狀態(tài),true為折疊周瞎,false為未折疊
         * @param index        當(dāng)前顯示的view索引數(shù)量
         * @param surplusWidth 折疊狀態(tài)下 剩余空間
         */
        void onFoldChange(boolean canFold, boolean fold, int index,int maxIndex, int surplusWidth);
    }
}

這里的 MAX_LINE 和 maxSize是我加的苗缩,MAX_LINE 記錄我最多只能多少行,行數(shù)從0開始声诸,所以我這里設(shè)置3酱讶,maxSize是為了防止一種場(chǎng)景bug,比如:我設(shè)置最多4行双絮,然后后臺(tái)返回的歷史記錄剛好4行浴麻,排滿了得问,但是他不會(huì)執(zhí)行換行代碼囤攀,所以我就在不換行的時(shí)候還判斷了一下當(dāng)前排的子view是不是數(shù)據(jù)中的最后一個(gè),如果是的宫纬,那么也要記錄一下index焚挠。
接下來(lái)就是將它介入適配器

public class FlowListView extends FlowLayout implements FlowAdapter.OnDataChangedListener {

    protected FlowAdapter flowAdapter;

    public FlowListView(Context context) {
        super(context);
    }

    public FlowListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FlowListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    public void setAdapter(FlowAdapter tagAdapter) {
        this.flowAdapter = tagAdapter;
        this.flowAdapter.setOnDataChangedListener(this);
        updateView();
    }

    @Override
    public void onChanged() {
        updateView();
    }

    private void updateView() {
        removeAllViews();
//        int count1 = getChildCount();
//        for(int x = 0; x < count1;x++){
//            Log.d("yanjin","x = "+getChildAt(x).getTag());
//        }
        if (null == flowAdapter) {
            throw new RuntimeException("adapter cannot be empty");
        }
        int count = flowAdapter.getCount();

        for (int i = 0; i < count; i++) {
            View tagView = flowAdapter.getView(this, flowAdapter.getItem(i), i);
            tagView.setTag(flowAdapter.getItem(i));
            flowAdapter.initView(tagView, flowAdapter.getItem(i), i);
            addView(tagView);
        }
    }

}
public abstract class FlowAdapter<T> {

    private OnDataChangedListener onDataChangedListener;

    private List<T> data;

    /**
     * 子View創(chuàng)建
     *
     * @param parent
     * @param item
     * @param position
     * @return
     */
    public abstract View getView(ViewGroup parent, T item, int position);

    /**
     * 初始化View
     *
     * @param view
     * @param item
     * @param position
     * @return
     */
    public abstract void initView(View view, T item, int position);


    /**
     * 折疊View 默認(rèn)不設(shè)置
     *
     * @return
     */
    public View foldView() {
        return null;
    }


    /**
     * 數(shù)據(jù)的數(shù)量
     *
     * @return
     */
    public int getCount() {
        return this.data == null ? 0 : this.data.size();
    }

    /**
     * 獲取數(shù)據(jù)
     *
     * @return
     */
    public List<T> getData() {
        return data;
    }


    /**
     * 設(shè)置新數(shù)據(jù)
     *
     * @param data
     */
    public void setNewData(List<T> data) {
        this.data = data;
        notifyDataChanged();
    }

    /**
     * 添加數(shù)據(jù)
     *
     * @param data
     */
    public void addData(List<T> data) {
        if (this.data == null) {
            this.data = new ArrayList<>();
        }
        this.data.addAll(data);
        notifyDataChanged();
    }

    /**
     * 添加數(shù)據(jù)
     *
     * @param index
     * @param data
     */
    public void addData(int index, List<T> data) {
        if (this.data == null) {
            this.data = new ArrayList<>();
        }
        this.data.addAll(index, data);
        notifyDataChanged();
    }


    /**
     * 添加數(shù)據(jù)
     *
     * @param data
     */
    public void addData(T data) {
        if (this.data == null) {
            this.data = new ArrayList<>();
        }
        this.data.add(data);
        notifyDataChanged();
    }


    /**
     * 獲取指定位置的數(shù)據(jù)
     *
     * @param position
     * @return
     */
    public T getItem(int position) {
        if (this.data != null && position >= 0 && position < this.data.size()) {
            return this.data.get(position);
        }
        return null;
    }


    /**
     * 刷新數(shù)據(jù)
     */
    public void notifyDataChanged() {
        if (this.onDataChangedListener != null) {
            this.onDataChangedListener.onChanged();
        }
    }

    void setOnDataChangedListener(OnDataChangedListener listener) {
        this.onDataChangedListener = listener;
    }

    interface OnDataChangedListener<T> {
        void onChanged();
    }


}

這個(gè)里面的代碼都是沒(méi)改的。照抄
最后再對(duì)流式布局封裝漓骚,加入展開和收縮按鈕

public class ExpansionFoldLayout extends FlowListView {
    private View upFoldView;
    private View downFoldView;
    private int mRootWidth = Utils.getScreenWidth();

    public ExpansionFoldLayout(Context context) {
        this(context, null);
    }

    public ExpansionFoldLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ExpansionFoldLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        upFoldView = LayoutInflater.from(context).inflate(R.layout.view_item_fold_up, null);
        upFoldView.setTag("up");
        downFoldView = LayoutInflater.from(context).inflate(R.layout.view_item_fold_down, null);
        downFoldView.setTag("down");
        upFoldView.setOnClickListener(v -> {
            mFold = false;
            ((ExpansionAndContractionActivity)context).setFlag(false);
            flowAdapter.notifyDataChanged();
        });

        downFoldView.setOnClickListener(v -> {
            mFold = true;
            ((ExpansionAndContractionActivity)context).setFlag(true);
            flowAdapter.notifyDataChanged();
        });

        setOnFoldChangedListener((canFold, fold, index,maxIndex, surplusWidth) -> {
            try {
                if (canFold) {
                    Utils.removeFromParent(downFoldView);
                    addView(downFoldView);
                    android.util.Log.d("yanjin","maxIndex = "+maxIndex);
                    for (int x = 0; x < getChildCount(); x++){
                        View view = getChildAt(x);
                        if(view.getTag() != null){
                            Log.d("yanjin","maxIndex tag = "+view.getTag());
                        }
                    }
                    if (fold) {
                        Utils.removeFromParent(upFoldView);
                        int upIndex = index(index, surplusWidth);
                        addView(upFoldView, upIndex);
                        //標(biāo)簽的后面不能有其他標(biāo)簽蝌衔,所以好辦法就是在他的后面加一個(gè)空的標(biāo)簽寬度是屏幕寬度
                        View emptyView = new View(getContext());
                        addView(emptyView,upIndex+1,new FrameLayout.LayoutParams(mRootWidth, LayoutParams.MATCH_PARENT));
                    } else {
                        if(maxIndex != 0){
                            Utils.removeFromParent(downFoldView);
                            FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
                            addView(downFoldView,maxIndex,layoutParams);
                            //標(biāo)簽的后面不能有其他標(biāo)簽,所以好辦法就是在他的后面加一個(gè)空的標(biāo)簽寬度是屏幕寬度
                            View emptyView = new View(getContext());
                            addView(emptyView,maxIndex+1,new FrameLayout.LayoutParams(mRootWidth, LayoutParams.MATCH_PARENT));
                        }else{
                            Utils.removeFromParent(downFoldView);
                            addView(downFoldView);
                            //標(biāo)簽的后面不能有其他標(biāo)簽蝌蹂,所以好辦法就是在他的后面加一個(gè)空的標(biāo)簽寬度是屏幕寬度
                            View emptyView = new View(getContext());
                            addView(emptyView,new FrameLayout.LayoutParams(mRootWidth, LayoutParams.MATCH_PARENT));
                        }
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        });
    }

    private int index(int index, int surplusWidth) {
        int upIndex = index;
        int upWidth = Utils.getViewWidth(upFoldView);
        //當(dāng)剩余空間大于等于展開View寬度直接加入index+1
        if (surplusWidth >= upWidth) {
            upIndex = index + 1;
        } else { //找到對(duì)應(yīng)的位置
            for (int i = index; i >= 0; i--) {
                View view = getChildAt(index);
                int viewWidth = Utils.getViewWidth(view);
                upWidth -= viewWidth;
                if (upWidth <= 0) {
                    upIndex = i;
                    break;
                }
            }
        }
        return upIndex;
    }


}

這里要注意的就是setOnFoldChangedListener回調(diào)噩斟,這里加入展開或者收縮。這里會(huì)有一個(gè)bug也就是展開或者收縮按鈕后面可能會(huì)展示一些其他標(biāo)簽按鈕孤个,這里為了避免bug剃允,直接在后面加一個(gè)充滿布局的子view放在展開或收縮按鈕后面,這樣就不會(huì)有這個(gè)問(wèn)題了齐鲤。
view_item_fold_up.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/shape_fold_view">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="UP"
        android:layout_gravity="center"/>
</FrameLayout>

view_item_fold_down.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/shape_fold_view">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Do"
        android:layout_gravity="center"/>
</FrameLayout>
//標(biāo)簽的后面不能有其他標(biāo)簽斥废,所以好辦法就是在他的后面加一個(gè)空的標(biāo)簽寬度是屏幕寬度
                       View emptyView = new View(getContext());
                       addView(emptyView,upIndex+1,new FrameLayout.LayoutParams(mRootWidth, LayoutParams.MATCH_PARENT));

適配器

public class SearchHistoryAdapter extends FlowAdapter<String> {

    @Override
    public View getView(ViewGroup parent, String item, int position) {
        return LayoutInflater.from(parent.getContext()).inflate(R.layout.item_search_history, null);
    }

    @Override
    public void initView(View view, String item, int position) {
        AppCompatTextView textView = view.findViewById(R.id.item_tv);
        textView.setText(item);
        textView.setOnClickListener(v -> Toast.makeText(view.getContext(), item, Toast.LENGTH_SHORT).show());
    }
}

最后集成在activity中使用

class ExpansionAndContractionActivity : AppCompatActivity() {
   public var flag = true
   private val list by lazy {
       Utils.getHistoryList()
   }
   private val mAdapter by lazy {
       SearchHistoryAdapter()
   }
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_expansion_and_contraction)
       initView()
       initData()
   }

   /**
    * 設(shè)置值時(shí)每次都是重新初始化view
    * 解決它加載新數(shù)據(jù)上拉下拉按鈕消失的bug
    */
   private fun initData() {
       //放入布局中
       mRlTest.removeAllViews()
       var flowListView: ExpansionFoldLayout =
           ExpansionFoldLayout(
               this
           )
       flowListView.setEqually(false)
       flowListView.setFoldLines(MIN_LINE)
       if(flag){
           flowListView.setFold(true)
       }else{
           flowListView.setFold(false)
       }
       mAdapter.setNewData(list)
       //如果列表返回不為空,那么給流式布局加上列表的最大position
       if(list != null && list.size > 0){
           flowListView.maxSize = list.size-1
       }
       flowListView.setAdapter(mAdapter)
       mRlTest.addView(flowListView, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT))
   }

   private fun initView() {
       mBtnAdd?.setOnClickListener {
           list.add(0,"新增加的"+list.size)
           initData()
       }
   }

   companion object{
       public var MIN_LINE = 2
   }
}

activity_expansion_and_contraction给郊。xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    android:background="@color/ff333333"
    tools:context=".expansion_and_contraction.ExpansionAndContractionActivity">
    <RelativeLayout
        android:id="@+id/mRlTest"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/mBtnAdd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="添加一個(gè)"
        android:layout_alignParentBottom="true"/>
</RelativeLayout>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末牡肉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子淆九,更是在濱河造成了極大的恐慌统锤,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炭庙,死亡現(xiàn)場(chǎng)離奇詭異饲窿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)煤搜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門免绿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人擦盾,你說(shuō)我怎么就攤上這事嘲驾√视矗” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵辽故,是天一觀的道長(zhǎng)徒仓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)誊垢,這世上最難降的妖魔是什么掉弛? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮喂走,結(jié)果婚禮上殃饿,老公的妹妹穿的比我還像新娘。我一直安慰自己芋肠,他們只是感情好乎芳,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著帖池,像睡著了一般奈惑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上睡汹,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天肴甸,我揣著相機(jī)與錄音,去河邊找鬼囚巴。 笑死原在,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的文兢。 我是一名探鬼主播晤斩,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼姆坚!你這毒婦竟也來(lái)了澳泵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤兼呵,失蹤者是張志新(化名)和其女友劉穎兔辅,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體击喂,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡维苔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了懂昂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片介时。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沸柔,到底是詐尸還是另有隱情循衰,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布褐澎,位于F島的核電站会钝,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏工三。R本人自食惡果不足惜迁酸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俭正。 院中可真熱鬧奸鬓,春花似錦、人聲如沸段审。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)寺枉。三九已至,卻和暖如春绷落,著一層夾襖步出監(jiān)牢的瞬間姥闪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工砌烁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留筐喳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓函喉,卻偏偏與公主長(zhǎng)得像避归,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子管呵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容