android 实现伸缩布局效果

最近项目实现下面的图示的效果,本来想用listview+gridview实现,但是貌似挺麻烦的于是就用flowlayout 来addview实现添加伸缩的效果,实现也比较简单。这里写图片描述


mainActivity 布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    >
    <RelativeLayout 
         android:id="@+id/rl_category_title_bar_layout"
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
         >
         <RelativeLayout 
             android:layout_height="50dp"
             android:layout_width="match_parent"
             >
         <TextView 
             android:id="@+id/tv_category_title"
             android:layout_height="50dp"
             android:layout_width="wrap_content"
             android:text="分类"
             android:textSize="18sp"
             android:layout_centerInParent="true"
             android:gravity="center"
             />
          </RelativeLayout>
     </RelativeLayout>

        <ListView
             android:id="@+id/lv_category_menu"
             android:layout_height="match_parent"
             android:layout_width="match_parent"
             />

</LinearLayout>

自定义布局flowlayout

package comskyball.addflowlayout;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;


public class FlowLayout extends ViewGroup {

    private Context mContext;
    private int usefulWidth; // the space of a line we can use(line's width minus the sum of left and right padding
    private int lineSpacing = 0; // the spacing between lines in flowlayout
    List<View> childList = new ArrayList();
    List<Integer> lineNumList = new ArrayList();

    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);
        mContext = context;
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
                R.styleable.FlowLayout);
        lineSpacing = mTypedArray.getDimensionPixelSize(
                R.styleable.FlowLayout_lineSpacing, 0);
        mTypedArray.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int mPaddingLeft = getPaddingLeft();
        int mPaddingRight = getPaddingRight();
        int mPaddingTop = getPaddingTop();
        int mPaddingBottom = getPaddingBottom();

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int lineUsed = mPaddingLeft + mPaddingRight;
        int lineY = mPaddingTop;
        int lineHeight = 0;
        for (int i = 0; i < this.getChildCount(); i++) {
            View child = this.getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            int spaceWidth = 0;
            int spaceHeight = 0;
            LayoutParams childLp = child.getLayoutParams();
            if (childLp instanceof MarginLayoutParams) {
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, lineY);
                MarginLayoutParams mlp = (MarginLayoutParams) childLp;
                spaceWidth = mlp.leftMargin + mlp.rightMargin;
                spaceHeight = mlp.topMargin + mlp.bottomMargin;
            } else {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }

            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();
            spaceWidth += childWidth;
            spaceHeight += childHeight;

            if (lineUsed + spaceWidth > widthSize) {
                //approach the limit of width and move to next line
                lineY += lineHeight + lineSpacing;
                lineUsed = mPaddingLeft + mPaddingRight;
                lineHeight = 0;
            }
            if (spaceHeight > lineHeight) {
                lineHeight = spaceHeight;
            }
            lineUsed += spaceWidth;
        }
        setMeasuredDimension(
                widthSize,
                heightMode == MeasureSpec.EXACTLY ? heightSize : lineY + lineHeight + mPaddingBottom
        );
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int mPaddingLeft = getPaddingLeft();
        int mPaddingRight = getPaddingRight();
        int mPaddingTop = getPaddingTop();

        int lineX = mPaddingLeft;
        int lineY = mPaddingTop;
        int lineWidth = r - l;
        usefulWidth = lineWidth - mPaddingLeft - mPaddingRight;
        int lineUsed = mPaddingLeft + mPaddingRight;
        int lineHeight = 0;
        int lineNum = 0;

        lineNumList.clear();
        for (int i = 0; i < this.getChildCount(); i++) {
            View child = this.getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            int spaceWidth = 0;
            int spaceHeight = 0;
            int left = 0;
            int top = 0;
            int right = 0;
            int bottom = 0;
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            LayoutParams childLp = child.getLayoutParams();
            if (childLp instanceof MarginLayoutParams) {
                MarginLayoutParams mlp = (MarginLayoutParams) childLp;
                spaceWidth = mlp.leftMargin + mlp.rightMargin;
                spaceHeight = mlp.topMargin + mlp.bottomMargin;
                left = lineX + mlp.leftMargin;
                top = lineY + mlp.topMargin;
                right = lineX + mlp.leftMargin + childWidth;
                bottom = lineY + mlp.topMargin + childHeight;
            } else {
                left = lineX;
                top = lineY;
                right = lineX + childWidth;
                bottom = lineY + childHeight;
            }
            spaceWidth += childWidth;
            spaceHeight += childHeight;

            if (lineUsed + spaceWidth > lineWidth) {
                //approach the limit of width and move to next line
                lineNumList.add(lineNum);
                lineY += lineHeight + lineSpacing;
                lineUsed = mPaddingLeft + mPaddingRight;
                lineX = mPaddingLeft;
                lineHeight = 0;
                lineNum = 0;
                if (childLp instanceof MarginLayoutParams) {
                    MarginLayoutParams mlp = (MarginLayoutParams) childLp;
                    left = lineX + mlp.leftMargin;
                    top = lineY + mlp.topMargin;
                    right = lineX + mlp.leftMargin + childWidth;
                    bottom = lineY + mlp.topMargin + childHeight;
                } else {
                    left = lineX;
                    top = lineY;
                    right = lineX + childWidth;
                    bottom = lineY + childHeight;
                }
            }
            child.layout(left, top, right, bottom);
            lineNum ++;
            if (spaceHeight > lineHeight) {
                lineHeight = spaceHeight;
            }
            lineUsed += spaceWidth;
            lineX += spaceWidth;
        }
        // add the num of last line
        lineNumList.add(lineNum);
    }

    /**
     * resort child elements to use lines as few as possible
     */
    public void relayoutToCompress() {
        int childCount = this.getChildCount();
        if (0 == childCount) {
            //no need to sort if flowlayout has no child view
            return;
        }
        int count = 0;
        for (int i = 0; i < childCount; i++) {
            View v = getChildAt(i);
            if (v instanceof BlankView) {
                //BlankView is just to make childs look in alignment, we should ignore them when we relayout
                continue;
            }
            count++;
        }
        View[] childs = new View[count];
        int[] spaces = new int[count];
        int n = 0;
        for (int i = 0; i < childCount; i++) {
            View v = getChildAt(i);
            if (v instanceof BlankView) {
                //BlankView is just to make childs look in alignment, we should ignore them when we relayout
                continue;
            }
            childs[n] = v;
            LayoutParams childLp = v.getLayoutParams();
            int childWidth = v.getMeasuredWidth();
            if (childLp instanceof MarginLayoutParams) {
                MarginLayoutParams mlp = (MarginLayoutParams) childLp ;
                spaces[n] = mlp.leftMargin + childWidth + mlp.rightMargin;
            } else {
                spaces[n] = childWidth;
            }
            n++;
        }
        int[] compressSpaces = new int[count];
        for (int i = 0; i < count; i++) {
            compressSpaces[i] = spaces[i] > usefulWidth ? usefulWidth : spaces[i];
        }
        sortToCompress(childs, compressSpaces);
        this.removeAllViews();
        for (View v : childList) {
            this.addView(v);
        }
        childList.clear();
    }

    private void sortToCompress(View[] childs, int[] spaces) {
        int childCount = childs.length;
        int[][] table = new int[childCount + 1][usefulWidth + 1];
        for (int i = 0; i < childCount +1; i++) {
            for (int j = 0; j < usefulWidth; j++) {
                table[i][j] = 0;
            }
        }
        boolean[] flag = new boolean[childCount];
        for (int i = 0; i < childCount; i++) {
            flag[i] = false;
        }
        for (int i = 1; i <= childCount; i++) {
            for (int j = spaces[i-1]; j <= usefulWidth; j++) {
                table[i][j] = (table[i-1][j] > table[i-1][j-spaces[i-1]] + spaces[i-1]) ? table[i-1][j] : table[i-1][j-spaces[i-1]] + spaces[i-1];
            }
        }
        int v = usefulWidth;
        for (int i = childCount ; i > 0 && v >= spaces[i-1]; i--) {
            if (table[i][v] == table[i-1][v-spaces[i-1]] + spaces[i-1]) {
                flag[i-1] =  true;
                v = v - spaces[i - 1];
            }
        }
        int rest = childCount;
        View[] restArray;
        int[] restSpaces;
        for (int i = 0; i < flag.length; i++) {
            if (flag[i] == true) {
                childList.add(childs[i]);
                rest--;
            }
        }

        if (0 == rest) {
            return;
        }
        restArray = new View[rest];
        restSpaces = new int[rest];
        int index = 0;
        for (int i = 0; i < flag.length; i++) {
            if (flag[i] == false) {
                restArray[index] = childs[i];
                restSpaces[index] = spaces[i];
                index++;
            }
        }
        table = null;
        childs = null;
        flag = null;
        sortToCompress(restArray, restSpaces);
    }

    /**
     * add some blank view to make child elements look in alignment
     */
    public void relayoutToAlign() {
        int childCount = this.getChildCount();
        if (0 == childCount) {
            //no need to sort if flowlayout has no child view
            return;
        }
        int count = 0;
        for (int i = 0; i < childCount; i++) {
            View v = getChildAt(i);
            if (v instanceof BlankView) {
                //BlankView is just to make childs look in alignment, we should ignore them when we relayout
                continue;
            }
            count++;
        }
        View[] childs = new View[count];
        int[] spaces = new int[count];
        int n = 0;
        for (int i = 0; i < childCount; i++) {
            View v = getChildAt(i);
            if (v instanceof BlankView) {
                //BlankView is just to make childs look in alignment, we should ignore them when we relayout
                continue;
            }
            childs[n] = v;
            LayoutParams childLp = v.getLayoutParams();
            int childWidth = v.getMeasuredWidth();
            if (childLp instanceof MarginLayoutParams) {
                MarginLayoutParams mlp = (MarginLayoutParams) childLp ;
                spaces[n] = mlp.leftMargin + childWidth + mlp.rightMargin;
            } else {
                spaces[n] = childWidth;
            }
            n++;
        }
        int lineTotal = 0;
        int start = 0;
        this.removeAllViews();
        for (int i = 0; i < count; i++) {
            if (lineTotal + spaces[i] > usefulWidth) {
                int blankWidth = usefulWidth - lineTotal;
                int end = i - 1;
                int blankCount = end - start;
                if (blankCount >= 0) {
                    if (blankCount > 0) {
                        int eachBlankWidth = blankWidth / blankCount;
                        MarginLayoutParams lp = new MarginLayoutParams(eachBlankWidth, 0);
                        for (int j = start; j < end; j++) {
                            this.addView(childs[j]);
                            BlankView blank = new BlankView(mContext);
                            this.addView(blank, lp);
                        }
                    }
                    this.addView(childs[end]);
                    start = i;
                    i --;
                    lineTotal = 0;
                } else {
                    this.addView(childs[i]);
                    start = i + 1;
                    lineTotal = 0;
                }
            } else {
                lineTotal += spaces[i];
            }
        }
        for (int i = start; i < count; i++) {
            this.addView(childs[i]);
        }
    }

    /**
     * use both of relayout methods together
     */
    public void relayoutToCompressAndAlign(){
        this.relayoutToCompress();
        this.relayoutToAlign();
    }

    /**
     * cut the flowlayout to the specified num of lines
     * @param line_num
     */
    public void specifyLines(int line_num) {
        int childNum = 0;
        if (line_num > lineNumList.size()) {
            line_num = lineNumList.size();
        }
        for (int i = 0; i < line_num; i++) {
            childNum += lineNumList.get(i);
        }
        List<View> viewList = new ArrayList<View>();
        for (int i = 0; i < childNum; i++) {
            viewList.add(getChildAt(i));
        }
        removeAllViews();
        for (View v : viewList) {
            addView(v);
        }
    }
    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return new MarginLayoutParams(p);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs)
    {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(super.generateDefaultLayoutParams());
    }

    class BlankView extends View {

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

adapter

package comskyball.addflowlayout;

import java.util.ArrayList;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class CategoryLvAdapter extends BaseAdapter {

    public Context context;
    public ArrayList<Category> list;
    public boolean isMore=true;

    public CategoryLvAdapter(Context context,ArrayList<Category> list) {
        this.context=context;
        this.list=list;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return 0;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder=null;
        if(convertView==null){
            convertView=View.inflate(context, R.layout.lv_category_item, null);
            viewHolder=new ViewHolder();
            viewHolder.iv_lv_category_img=(ImageView) convertView.findViewById(R.id.iv_lv_category_img);
            viewHolder.tv_lv_category=(TextView) convertView.findViewById(R.id.tv_lv_category);
            viewHolder.flow_layout_lv_category=(FlowLayout) convertView.findViewById(R.id.flow_layout_lv_category);
            viewHolder.ll_lv_category_add=(LinearLayout) convertView.findViewById(R.id.ll_lv_category_add);
            viewHolder.iv_lv_category_arrow=(ImageView) convertView.findViewById(R.id.iv_lv_category_arrow);
            convertView.setTag(viewHolder);
        }else{
            viewHolder=(ViewHolder) convertView.getTag();
        }
//      ImageLoader.getInstance().displayImage(AppConfig.APP_URL+list.get(position).getImg(),viewHolder.iv_lv_category_img,App.normalOption);
        viewHolder.tv_lv_category.setText(list.get(position).getCate_name());
        viewHolder.iv_lv_category_arrow.setBackgroundResource(R.drawable.arrow_down);
        viewHolder.flow_layout_lv_category.removeAllViews();
        Utils.addflow(context,6, list.get(position).getNext(),viewHolder.flow_layout_lv_category);

        final FlowLayout flowLayoutLvCategory = viewHolder.flow_layout_lv_category;
        final ImageView ivLvCategoryArrow = viewHolder.iv_lv_category_arrow;

        viewHolder.ll_lv_category_add.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

                if(isMore){
                    isMore=false;
                    flowLayoutLvCategory.removeAllViews();
                    Utils.addflow(context,list.get(position).getNext().size(), list.get(position).getNext(),flowLayoutLvCategory);
                    ivLvCategoryArrow.setBackgroundResource(R.drawable.arrow_up);
                }else{
                    isMore=true;
                    flowLayoutLvCategory.removeAllViews();
                    Utils.addflow(context,6, list.get(position).getNext(),flowLayoutLvCategory);
                    ivLvCategoryArrow.setBackgroundResource(R.drawable.arrow_down);
                }
            }
        }); 

        return convertView; 
    }

    public class ViewHolder{
        public ImageView iv_lv_category_img;
        public TextView tv_lv_category;
        public FlowLayout flow_layout_lv_category;
        public LinearLayout ll_lv_category_add;
        public ImageView iv_lv_category_arrow;
    }


}

adapter item布局

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
     <LinearLayout 
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
         android:orientation="vertical"
         >
         <RelativeLayout 
             android:layout_height="35dp"
             android:layout_width="match_parent"
             >
             <ImageView
                 android:id="@+id/iv_lv_category_img" 
                 style="@style/category_iv_left_style"
                 />
             <TextView
                 android:id="@+id/tv_lv_category"
                 style="@style/category_tv_style"
                 android:text="衣食"
                 android:layout_toRightOf="@id/iv_lv_category_img"
                 />
         </RelativeLayout>

          <View 
              style="@style/category_view_style"
              />
          <ScrollView
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              >
              <RelativeLayout
                android:layout_height="match_parent"
                android:layout_width="match_parent"
                >
              <comskyball.addflowlayout.FlowLayout
                android:id="@+id/flow_layout_lv_category"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:lineSpacing="10dp"
                app:maxLine="3"
                android:background="#F0F0F0"
                android:layout_marginTop="5dp"
                />
              <LinearLayout 
                  android:id="@+id/ll_lv_category_add"
                  style="@style/category_ll_style"
                  android:layout_height="35dp"
                  android:layout_below="@id/flow_layout_lv_category"
                  >
                  <ImageView 
                      android:id="@+id/iv_lv_category_arrow"
                      style="@style/category_iv_style"
                      android:background="@drawable/arrow_down"
                      />
                  <View 
                      style="@style/category_view_style"
                      />
               </LinearLayout>
               </RelativeLayout>
           </ScrollView>
      </LinearLayout>

</RelativeLayout>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值