🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 一睹为快       ![这里写图片描述](https://box.kancloud.cn/2016-03-17_56ea50d2d44cd.jpg "") ### 需求 1.动态加载属性,如尺码,颜色,款式等 由于每件商品的属性是不确定的,有的商品的属性是颜色和尺码,有的是口味,有的是大小,所以这些属性不能直接写死到页面上。 2.动态加载属性下的标签 每个属性下的标签个数也不是一定的,比如有的商品的尺码是是S,M,XL,有的是均码,也就是每种属性的具体的内容是不一定的。 ### 技术点 自定义ViewGroup,使其中的TextView可以依据内容长短自动换行,如下图所示      ![这里写图片描述](https://box.kancloud.cn/2016-03-17_56ea50d30e800.jpg "") ### 实现 ### 布局 通过ListView来显示商品所有属性,每种属性作为ListView的Item。 ~~~ <!-- 商品规格列表 --> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFFFF" > <ListView android:id="@+id/lv_property" android:layout_width="fill_parent" android:layout_height="match_parent" android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:divider="#C0C0C0" android:dividerHeight="0.5px" android:listSelector="#00000000"> </ListView> </LinearLayout> ~~~ ### 自定义ViewGroup > 普通的LinearLayout只能横向和纵向显示控件,但是当一行显示不够时,无法自动换行,需要我们自定义布局容器。 ~~~ package jczb.shoping.common; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; public class MyViewGroup extends ViewGroup { private final static int VIEW_MARGIN=15; public MyViewGroup(Context context, AttributeSet attrs){ super(context, attrs); } public MyViewGroup(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int stages = 1; int stageHeight = 0; int stageWidth = 0; int wholeWidth = MeasureSpec.getSize(widthMeasureSpec); for (int i = 0; i < getChildCount(); i++) { final View child = getChildAt(i); // measure measureChild(child, widthMeasureSpec, heightMeasureSpec); stageWidth += (child.getMeasuredWidth() + VIEW_MARGIN); stageHeight = child.getMeasuredHeight(); if (stageWidth >= wholeWidth) { stages++; //reset stageWidth stageWidth = child.getMeasuredWidth(); } } int wholeHeight = (stageHeight + VIEW_MARGIN) * stages; // report this final dimension setMeasuredDimension(resolveSize(wholeWidth, widthMeasureSpec), resolveSize(wholeHeight, heightMeasureSpec)); } private int jiange = 10;//按钮之间的间隔 @Override protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) { final int count = getChildCount(); int row=0;// which row lay you view relative to parent int lengthX=arg1 ; // right position of child relative to parent int lengthY=arg2; // bottom position of child relative to parent for(int i=0;i<count;i++){ final View child = this.getChildAt(i); int width = child.getMeasuredWidth(); int height = child.getMeasuredHeight(); if(i == 0){ lengthX+=width+VIEW_MARGIN;//第一个的时候不需要加 }else{ lengthX+=width+VIEW_MARGIN +jiange;//按钮之间的间隔 } lengthY=row*(height+VIEW_MARGIN)+VIEW_MARGIN+height+arg2; //if it can't drawing on a same line , skip to next line if(lengthX>arg3){ lengthX=width+VIEW_MARGIN+arg1; row++; lengthY=row*(height+VIEW_MARGIN)+VIEW_MARGIN+height+arg2; } child.layout(lengthX-width, lengthY-height, lengthX, lengthY); } } } ~~~ ### ListView的Adapter ~~~ package jczb.shoping.adapter; import java.util.ArrayList; import java.util.HashMap; import jczb.shoping.common.MyViewGroup; import jczb.shoping.ui.R; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.TableLayout; import android.widget.TextView; public class PropertyAdapter extends BaseAdapter { private Context mContext; private ArrayList<HashMap<String,Object>> mList; private ArrayList<HashMap<String,TextView[]>> mViewList; private Drawable drawableNormal ; private Drawable drawablePressed; private Handler mHandler; //用于保存用户的属性集合 private HashMap<String,String> selectProMap=new HashMap<String, String>(); /** * 返回选中的属性 * @return */ public HashMap<String, String> getSelectProMap() { return selectProMap; } public void setSelectProMap(HashMap<String, String> selectProMap) { this.selectProMap = selectProMap; } public PropertyAdapter(Handler handler,Context context,ArrayList<HashMap<String,Object>> list){ super(); this.mHandler=handler; this.mContext=context; this.mList=list; mViewList=new ArrayList<HashMap<String,TextView[]>>(); drawableNormal=mContext.getResources().getDrawable(R.drawable.tv_property_label); } @Override public int getCount() { // TODO Auto-generated method stub return mList.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return mList.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { // 获取list_item布局文件的视图 convertView = LayoutInflater.from(this.mContext).inflate(R.layout.lv_property_item, null,true); holder = new ViewHolder(); // 获取控件对象 holder.tvPropName= (TextView) convertView .findViewById(R.id.tv_property_name); //holder.llPropContents=(LinearLayout)convertView.findViewById(R.id.ll_property_content); //holder.tlPropContents=(TableLayout)convertView.findViewById(R.id.ll_property_content); // 设置控件集到convertView holder.vgPropContents= (MyViewGroup) convertView.findViewById(R.id.myviewgroup); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } if (this.mList != null) { //HashMap<String,TextView[]> mapView=new HashMap<String, TextView[]>(); ArrayList<String> lables = (ArrayList<String>) this.mList.get(position).get("lable"); String type = (String) this.mList.get(position).get( "type"); holder.tvPropName.setText(type);//规格名称 //动态加载标签 //判断布局中的子控件是否为0,如果不为0,就不添加了,防止ListView滚动时重复添加 if(holder.vgPropContents.getChildCount()==0){ TextView[] textViews = new TextView[lables.size()]; //设置每个标签的文本和布局 //TableRow tr=new TableRow(mContext); for (int i = 0; i < lables.size(); i++) { TextView textView = new TextView(mContext); textView.setGravity(17); textView.setPadding(25,15,25,15); textViews[i] = textView; textViews[i].setBackgroundResource(R.drawable.tv_property_label); textViews[i].setText(lables.get(i)); textViews[i].setTag(i); //textViews[i].setBackgroundColor(Color.parseColor("#EE5500")); //tr.addView(textViews[i]); // holder.llPropContents.addView(textViews[i]); holder.vgPropContents.addView(textViews[i]); } //holder.tlPropContents.addView(tr); //绑定标签的Click事件 for(int j=0;j<textViews.length;j++){ textViews[j].setTag(textViews); textViews[j].setOnClickListener(new LableClickListener(type)); } //把控件存起来 // mapView.put(type, textViews); // mViewList.add(mapView); } /**判断之前是否已选中标签*/ if(selectProMap.get(type)!=null){ for(int h=0;h<holder.vgPropContents.getChildCount();h++){ TextView v=(TextView) holder.vgPropContents.getChildAt(h); if(selectProMap.get(type).equals(v.getText().toString())){ v.setBackgroundColor(Color.parseColor("#EE5500")); v.setTextColor(Color.parseColor("#FFFFFF")); selectProMap.put(type, v.getText().toString()); } } } } return convertView; } /*定义item对象*/ public class ViewHolder { TextView tvPropName; LinearLayout llPropContents; MyViewGroup vgPropContents; TableLayout tlPropContents; } class LableClickListener implements OnClickListener{ private String type; public LableClickListener(String type){ this.type=type; } @Override public void onClick(View v) { TextView[] textViews=(TextView[])v.getTag(); TextView tv=(TextView)v; for(int i=0;i<textViews.length;i++){ //让点击的标签背景变成橙色,字体颜色变为白色 if(tv.equals(textViews[i])){ textViews[i].setBackgroundColor(Color.parseColor("#EE5500")); textViews[i].setTextColor(Color.parseColor("#FFFFFF")); selectProMap.put(type, textViews[i].getText().toString()); }else{ //其他标签背景变成白色,字体颜色为黑色 //textViews[i].setBackgroundDrawable(drawableNormal); textViews[i].setBackgroundResource(R.drawable.tv_property_label); textViews[i].setTextColor(Color.parseColor("#000000")); } } } } } ~~~ ### 总结 >   这里关键就是实现自定义的ViewGroup,重写onMeasure和onLayout方法,判断新添加的控件有没有超出屏幕的宽度来决定是否要换行。