Android:ViewGroup 实现淘宝中商品规格选择
一、原理
使用 ViewGroup 控件,加入按钮等控件进去。需要重写里面的一些方法,如 onMeasure(),onLayout()等。
二、实现
public class GoodsViewGroup<X extends TextView>
extends ViewGroup {
public static final String BTN_MODE = "BTNMODE"; //按钮模式
public static final String TEV_MODE = "TEVMODE"; //文本模式
private static final String TAG = "IViewGroup";
private final int HorInterval = 10; //水平间隔
private final int VerInterval = 10; //垂直间隔
private int viewWidth; //控件的宽度
private int viewHeight; //控件的高度
private ArrayList<String> mTexts = new ArrayList<>();
private Context mContext;
private int textModePadding = 15;
//正常样式
private float itemTextSize = 18;
private int itemBGResNor = R.drawable.goods_item_btn_normal;
private int itemTextColorNor = Color.parseColor("#000000");
//选中的样式
private int itemBGResPre = R.drawable.goods_item_btn_selected;
private int itemTextColorPre = Color.parseColor("#ffffff");
public GoodsViewGroup(Context context) {
this(context, null);
}
public GoodsViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
/**
* 计算控件的大小
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = measureWidth(widthMeasureSpec);
viewHeight = measureHeight(heightMeasureSpec);
Log.e(TAG, "onMeasure:" + viewWidth + ":" + viewHeight);
// 计算自定义的ViewGroup中所有子控件的大小
measureChildren(widthMeasureSpec, heightMeasureSpec);
// 设置自定义的控件MyViewGroup的大小
setMeasuredDimension(viewWidth, getViewHeight());
}
private int measureWidth(int pWidthMeasureSpec) {
int result = 0;
int widthMode = MeasureSpec.getMode(pWidthMeasureSpec);
int widthSize = MeasureSpec.getSize(pWidthMeasureSpec);
switch (widthMode) {
/**
* mode共有三种情况,取值分别为MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY,
* MeasureSpec.AT_MOST。
*
*
* MeasureSpec.EXACTLY是精确尺寸,
* 当我们将控件的layout_width或layout_height指定为具体数值时如andorid
* :layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
*
*
* MeasureSpec.AT_MOST是最大尺寸,
* 当控件的layout_width或layout_height指定为WRAP_CONTENT时
* ,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可
* 。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
*
*
* MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,
* 通过measure方法传入的模式。
*/
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = widthSize;
break;
}
return result;
}
private int measureHeight(int pHeightMeasureSpec) {
int result = 0;
int heightMode = MeasureSpec.getMode(pHeightMeasureSpec);
int heightSize = MeasureSpec.getSize(pHeightMeasureSpec);
switch (heightMode) {
case MeasureSpec.UNSPECIFIED:
result = getSuggestedMinimumHeight();
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = heightSize;
break;
}
return result;
}
/**
* 覆写onLayout,其目的是为了指定视图的显示位置,方法执行的前后顺序是在onMeasure之后,因为视图肯定是只有知道大小的情况下,
* 才能确定怎么摆放
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 遍历所有子视图
int posLeft = HorInterval;
int posTop = VerInterval;
int posRight;
int posBottom;
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
// 获取在onMeasure中计算的视图尺寸
int measureHeight = childView.getMeasuredHeight();
int measuredWidth = childView.getMeasuredWidth();
if (posLeft + getNextHorLastPos(i) > viewWidth) {
posLeft = HorInterval;
posTop += (measureHeight + VerInterval);
}
posRight = posLeft + measuredWidth;
posBottom = posTop + measureHeight;
childView.layout(posLeft, posTop, posRight, posBottom);
posLeft += (measuredWidth + HorInterval);
}
}
//获取控件的自适应高度
private int getViewHeight() {
int viewwidth = HorInterval;
int viewheight = VerInterval;
if (getChildCount() > 0) {
viewheight = getChildAt(0).getMeasuredHeight() + VerInterval;
}
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
// 获取在onMeasure中计算的视图尺寸
int measureHeight = childView.getMeasuredHeight();
int measuredWidth = childView.getMeasuredWidth();
if (viewwidth + getNextHorLastPos(i) > viewWidth) {
viewwidth = HorInterval;
viewheight += (measureHeight + VerInterval);
} else {
viewwidth += (measuredWidth + HorInterval);
}
}
return viewheight;
}
private int getNextHorLastPos(int i) {
return getChildAt(i).getMeasuredWidth() + HorInterval;
}
private OnGroupItemClickListener onGroupItemClickListener;
public void setGroupClickListener(OnGroupItemClickListener listener) {
onGroupItemClickListener = listener;
for (int i = 0; i < getChildCount(); i++) {
final X childView = (X) getChildAt(i);
final int itemPos = i;
childView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
onGroupItemClickListener.onGroupItemClick(itemPos);
chooseItemStyle(itemPos);
}
});
}
}
//选中那个的样式
public void chooseItemStyle(int pos) {
clearItemsStyle();
if (pos < getChildCount()) {
X childView = (X) getChildAt(pos);
childView.setBackgroundResource(itemBGResPre);
childView.setTextColor(itemTextColorPre);
setItemPadding(childView);
}
}
private void setItemPadding(X view) {
if (view instanceof Button) {
view.setPadding(textModePadding, 0, textModePadding, 0);
} else {
view.setPadding(textModePadding, textModePadding, textModePadding, textModePadding);
}
}
//清除Group所有的样式
private void clearItemsStyle() {
for (int i = 0; i < getChildCount(); i++) {
X childView = (X) getChildAt(i);
childView.setBackgroundResource(itemBGResNor);
childView.setTextColor(itemTextColorNor);
setItemPadding(childView);
}
}
public void addItemViews(ArrayList<String> texts, String mode) {
mTexts = texts;
removeAllViews();
for (String text : texts) {
addItemView(text, mode);
}
}
private void addItemView(String text, String mode) {
X childView = null;
switch (mode) {
case BTN_MODE:
childView = (X) new Button(mContext);
break;
case TEV_MODE:
childView = (X) new TextView(mContext);
break;
}
childView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
childView.setTextSize(itemTextSize);
childView.setBackgroundResource(itemBGResNor);
setItemPadding(childView);
childView.setTextColor(itemTextColorNor);
childView.setText(text);
this.addView(childView);
}
public String getChooseText(int itemID) {
if (itemID >= 0) {
return mTexts.get(itemID);
}
return null;
}
public void setItemTextSize(float itemTextSize) {
this.itemTextSize = itemTextSize;
}
public void setItemBGResNor(int itemBGResNor) {
this.itemBGResNor = itemBGResNor;
}
public void setItemTextColorNor(int itemTextColorNor) {
this.itemTextColorNor = itemTextColorNor;
}
public void setItemBGResPre(int itemBGResPre) {
this.itemBGResPre = itemBGResPre;
}
public void setItemTextColorPre(int itemTextColorPre) {
this.itemTextColorPre = itemTextColorPre;
}
public interface OnGroupItemClickListener {
void onGroupItemClick(int item);
}
}
上面提供了可以设置按钮组的item的一些样式,还有这个 GoodsViewGroup 为什么要写成 GoodsViewGroup<X extends TextView> 这样呢?其实这里我是想做一个泛型,可以使用与 Button 跟 TextView ,而这里的 Button 本生就是继承 TextView 所以在代码中还要进行一个判断,可以看上面方法 setItemPadding(X view) 。那到了这里,有些好友可能就会问,为什么要搞两个呢?
其实这里因为 TextView 的不会自动有设置 padding 的,而 button 是有自动设置 padding 。这个时候你就要看看你是先要那种效果!不过通过我的代码中如果是选择 TextView 的话,这里也设置了一个 padding 给他,不然会很难看!
两种模式的写法:
1.Button :
GoodsViewGroup<Button> mGroup;
mGroup.addItemViews(viewtexts, GoodsViewGroup.BTN_MODE);
2.TextView
GoodsViewGroup<TextView> mGroup;
mGroup.addItemViews(viewtexts, GoodsViewGroup.TEV_MODE);
三、Drawable文件
上面涉及到的按钮选中与正常的两个Drawable
goods_item_btn_normal.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="#F5F5F5" />
<corners android:radius="15.0dip" />
</shape>
</item>
</layer-list>
goods_item_btn_selected.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="#FE4F00" />
<corners android:radius="15.0dip" />
</shape>
</item>
</layer-list>
四、例子:
ButtonGroupActivity
public class ButtonGroupActivity
extends Activity
implements GoodsViewGroup.OnGroupItemClickListener, View.OnClickListener {
private GoodsViewGroup<TextView> mGroup;
private Button mSubmitBtn;
private ArrayList<String> viewtexts = new ArrayList<>();
private int chooseID = -1;
private String chooseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_buttongroup);
mGroup = (GoodsViewGroup) findViewById(R.id.viewGroup);
mSubmitBtn = (Button) findViewById(R.id.submitBtn);
String text;
for (int i = 0; i < 10; i++) {
text = "L" + i;
viewtexts.add(text);
}
mGroup.addItemViews(viewtexts, GoodsViewGroup.TEV_MODE);
mGroup.setGroupClickListener(this);
mSubmitBtn.setOnClickListener(this);
super.onCreate(savedInstanceState);
}
@Override
public void onGroupItemClick(int item) {
chooseID = item;
chooseText = mGroup.getChooseText(item);
}
@Override
public void onClick(View view) {
if (chooseID >= 0) {
showToast("ID:" + chooseID + ";text:" + chooseText);
} else {
showToast("请选择");
}
}
private void showToast(String text) {
Toast.makeText(ButtonGroupActivity.this, text, Toast.LENGTH_SHORT).show();
}
}
activity_buttongroup.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linear_ayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.jisuanqi.GoodsViewGroup
android:id="@+id/viewGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.example.jisuanqi.GoodsViewGroup>
<Button
android:id="@+id/submitBtn"
android:text="确定"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>