最近项目实现下面的图示的效果,本来想用listview+gridview实现,但是貌似挺麻烦的于是就用flowlayout 来addview实现添加伸缩的效果,实现也比较简单。
mainActivity 布局
<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 childList = new ArrayList();
List 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 viewList = new ArrayList();
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 list;
public boolean isMore=true;
public CategoryLvAdapter(Context context,ArrayList 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布局
<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>