Android实现伸缩布局效果的实例代码

作者:简简单单 2017-01-07

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

mainActivity 布局

 
 代码如下 复制代码
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  >
  
     android:id="@+id/rl_category_title_bar_layout"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     >
     
       android:layout_height="50dp"
       android:layout_width="match_parent"
       >
     
       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"
       />
     
   
    
       android:id="@+id/lv_category_menu"
       android:layout_height="match_parent"
       android:layout_width="match_parent"
       />
 

自定义布局flowlayout

 
 代码如下 复制代码
packagecomskyball.addflowlayout;
importandroid.content.Context;
importandroid.content.res.TypedArray;
importandroid.util.AttributeSet;
importandroid.view.View;
importandroid.view.ViewGroup;
importjava.util.ArrayList;
importjava.util.List;
publicclassFlowLayoutextendsViewGroup {
  privateContext mContext;
  privateintusefulWidth;// the space of a line we can use(line's width minus the sum of left and right padding
  privateintlineSpacing =0;// the spacing between lines in flowlayout
  List childList =newArrayList();
  List lineNumList =newArrayList();
  publicFlowLayout(Context context) {
    this(context,null);
  }
  publicFlowLayout(Context context, AttributeSet attrs) {
    this(context, attrs,0);
  }
  publicFlowLayout(Context context, AttributeSet attrs,intdefStyleAttr) {
    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
  protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec) {
    intmPaddingLeft = getPaddingLeft();
    intmPaddingRight = getPaddingRight();
    intmPaddingTop = getPaddingTop();
    intmPaddingBottom = getPaddingBottom();
    intwidthSize = MeasureSpec.getSize(widthMeasureSpec);
    intheightMode = MeasureSpec.getMode(heightMeasureSpec);
    intheightSize = MeasureSpec.getSize(heightMeasureSpec);
    intlineUsed = mPaddingLeft + mPaddingRight;
    intlineY = mPaddingTop;
    intlineHeight =0;
    for(inti =0; i
      View child =this.getChildAt(i);
      if(child.getVisibility() == GONE) {
        continue;
      }
      intspaceWidth =0;
      intspaceHeight =0;
      LayoutParams childLp = child.getLayoutParams();
      if(childLpinstanceofMarginLayoutParams) {
        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);
      }
      intchildWidth = child.getMeasuredWidth();
      intchildHeight = 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
  protectedvoidonLayout(booleanchanged,intl,intt,intr,intb) {
    intmPaddingLeft = getPaddingLeft();
    intmPaddingRight = getPaddingRight();
    intmPaddingTop = getPaddingTop();
    intlineX = mPaddingLeft;
    intlineY = mPaddingTop;
    intlineWidth = r - l;
    usefulWidth = lineWidth - mPaddingLeft - mPaddingRight;
    intlineUsed = mPaddingLeft + mPaddingRight;
    intlineHeight =0;
    intlineNum =0;
    lineNumList.clear();
    for(inti =0; i
      View child =this.getChildAt(i);
      if(child.getVisibility() == GONE) {
        continue;
      }
      intspaceWidth =0;
      intspaceHeight =0;
      intleft =0;
      inttop =0;
      intright =0;
      intbottom =0;
      intchildWidth = child.getMeasuredWidth();
      intchildHeight = child.getMeasuredHeight();
      LayoutParams childLp = child.getLayoutParams();
      if(childLpinstanceofMarginLayoutParams) {
        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(childLpinstanceofMarginLayoutParams) {
          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
   */
  publicvoidrelayoutToCompress() {
    intchildCount =this.getChildCount();
    if(0== childCount) {
      //no need to sort if flowlayout has no child view
      return;
    }
    intcount =0;
    for(inti =0; i < childCount; i++) {
      View v = getChildAt(i);
      if(vinstanceofBlankView) {
        //BlankView is just to make childs look in alignment, we should ignore them when we relayout
        continue;
      }
      count++;
    }
    View[] childs =newView[count];
    int[] spaces =newint[count];
    intn =0;
    for(inti =0; i < childCount; i++) {
      View v = getChildAt(i);
      if(vinstanceofBlankView) {
        //BlankView is just to make childs look in alignment, we should ignore them when we relayout
        continue;
      }
      childs[n] = v;
      LayoutParams childLp = v.getLayoutParams();
      intchildWidth = v.getMeasuredWidth();
      if(childLpinstanceofMarginLayoutParams) {
        MarginLayoutParams mlp = (MarginLayoutParams) childLp ;
        spaces[n] = mlp.leftMargin + childWidth + mlp.rightMargin;
      }else{
        spaces[n] = childWidth;
      }
      n++;
    }
    int[] compressSpaces =newint[count];
    for(inti =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();
  }
  privatevoidsortToCompress(View[] childs,int[] spaces) {
    intchildCount = childs.length;
    int[][] table =newint[childCount +1][usefulWidth +1];
    for(inti =0; i < childCount +1; i++) {
      for(intj =0; j < usefulWidth; j++) {
        table[i][j] =0;
      }
    }
    boolean[] flag =newboolean[childCount];
    for(inti =0; i < childCount; i++) {
      flag[i] =false;
    }
    for(inti =1; i <= childCount; i++) {
      for(intj = 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];
      }
    }
    intv = usefulWidth;
    for(inti = 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];
      }
    }
    intrest = childCount;
    View[] restArray;
    int[] restSpaces;
    for(inti =0; i < flag.length; i++) {
      if(flag[i] ==true) {
        childList.add(childs[i]);
        rest--;
      }
    }
    if(0== rest) {
      return;
    }
    restArray =newView[rest];
    restSpaces =newint[rest];
    intindex =0;
    for(inti =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
   */
  publicvoidrelayoutToAlign() {
    intchildCount =this.getChildCount();
    if(0== childCount) {
      //no need to sort if flowlayout has no child view
      return;
    }
    intcount =0;
    for(inti =0; i < childCount; i++) {
      View v = getChildAt(i);
      if(vinstanceofBlankView) {
        //BlankView is just to make childs look in alignment, we should ignore them when we relayout
        continue;
      }
      count++;
    }
    View[] childs =newView[count];
    int[] spaces =newint[count];
    intn =0;
    for(inti =0; i < childCount; i++) {
      View v = getChildAt(i);
      if(vinstanceofBlankView) {
        //BlankView is just to make childs look in alignment, we should ignore them when we relayout
        continue;
      }
      childs[n] = v;
      LayoutParams childLp = v.getLayoutParams();
      intchildWidth = v.getMeasuredWidth();
      if(childLpinstanceofMarginLayoutParams) {
        MarginLayoutParams mlp = (MarginLayoutParams) childLp ;
        spaces[n] = mlp.leftMargin + childWidth + mlp.rightMargin;
      }else{
        spaces[n] = childWidth;
      }
      n++;
    }
    intlineTotal =0;
    intstart =0;
    this.removeAllViews();
    for(inti =0; i < count; i++) {
      if(lineTotal + spaces[i] > usefulWidth) {
        intblankWidth = usefulWidth - lineTotal;
        intend = i -1;
        intblankCount = end - start;
        if(blankCount >=0) {
          if(blankCount >0) {
            inteachBlankWidth = blankWidth / blankCount;
            MarginLayoutParams lp =newMarginLayoutParams(eachBlankWidth,0);
            for(intj = start; j < end; j++) {
              this.addView(childs[j]);
              BlankView blank =newBlankView(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(inti = start; i < count; i++) {
      this.addView(childs[i]);
    }
  }
  /**
   * use both of relayout methods together
   */
  publicvoidrelayoutToCompressAndAlign(){
    this.relayoutToCompress();
    this.relayoutToAlign();
  }
  /**
   * cut the flowlayout to the specified num of lines
   * @param line_num
   */
  publicvoidspecifyLines(intline_num) {
    intchildNum =0;
    if(line_num > lineNumList.size()) {
      line_num = lineNumList.size();
    }
    for(inti =0; i < line_num; i++) {
      childNum += lineNumList.get(i);
    }
    List viewList =newArrayList();
    for(inti =0; i < childNum; i++) {
      viewList.add(getChildAt(i));
    }
    removeAllViews();
    for(View v : viewList) {
      addView(v);
    }
  }
  @Override
  protectedLayoutParams generateLayoutParams(LayoutParams p) {
    returnnewMarginLayoutParams(p);
  }
  @Override
  publicLayoutParams generateLayoutParams(AttributeSet attrs)
  {
    returnnewMarginLayoutParams(getContext(), attrs);
  }
  @Override
  protectedLayoutParams generateDefaultLayoutParams() {
    returnnewMarginLayoutParams(super.generateDefaultLayoutParams());
  }
  classBlankViewextendsView {
    publicBlankView(Context context) {
      super(context);
    }
  }
}
 

adapter

 
 代码如下 复制代码
packagecomskyball.addflowlayout;
importjava.util.ArrayList;
importandroid.content.Context;
importandroid.os.Handler;
importandroid.os.Message;
importandroid.util.Log;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.view.ViewGroup;
importandroid.widget.BaseAdapter;
importandroid.widget.ImageView;
importandroid.widget.LinearLayout;
importandroid.widget.TextView;
importandroid.widget.Toast;
publicclassCategoryLvAdapterextendsBaseAdapter {
  publicContext context;
  publicArrayList list;
  publicbooleanisMore=true;
  publicCategoryLvAdapter(Context context,ArrayList list) {
    this.context=context;
    this.list=list;
  }
  @Override
  publicintgetCount() {
    returnlist.size();
  }
  @Override
  publicObject getItem(intposition) {
    return0;
  }
  @Override
  publiclonggetItemId(intposition) {
    return0;
  }
  @Override
  publicView getView(finalintposition, View convertView, ViewGroup parent) {
    ViewHolder viewHolder=null;
    if(convertView==null){
      convertView=View.inflate(context, R.layout.lv_category_item,null);
      viewHolder=newViewHolder();
      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);
    finalFlowLayout flowLayoutLvCategory = viewHolder.flow_layout_lv_category;
    finalImageView ivLvCategoryArrow = viewHolder.iv_lv_category_arrow;
    viewHolder.ll_lv_category_add.setOnClickListener(newOnClickListener() {
      @Override
      publicvoidonClick(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);
        }
      }
    });
    returnconvertView;
  }
  publicclassViewHolder{
    publicImageView iv_lv_category_img;
    publicTextView tv_lv_category;
    publicFlowLayout flow_layout_lv_category;
    publicLinearLayout ll_lv_category_add;
    publicImageView iv_lv_category_arrow;
  }
}

adapter item布局

  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"
  >
   
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:orientation="vertical"
     >
     
       android:layout_height="35dp"
       android:layout_width="match_parent"
       >
       
         android:id="@+id/iv_lv_category_img"
         style="@style/category_iv_left_style"
         />
       
         android:id="@+id/tv_lv_category"
         style="@style/category_tv_style"
         android:text="衣食"
         android:layout_toRightOf="@id/iv_lv_category_img"
         />
     
     
       style="@style/category_view_style"
       />
     
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       >
       
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        >
       
        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"
        />
       
         android:id="@+id/ll_lv_category_add"
         style="@style/category_ll_style"
         android:layout_height="35dp"
         android:layout_below="@id/flow_layout_lv_category"
         >
         
           android:id="@+id/iv_lv_category_arrow"
           style="@style/category_iv_style"
           android:background="@drawable/arrow_down"
           />
         
           style="@style/category_view_style"
           />
        
        
      
   
 

相关文章

精彩推荐