文章转载自 http://blog.csdn.net/qq_30552993/article/details/52304744
今天刚好有个同学他想做一个仿淘宝中的选中商品不同尺寸,比如衣服有L、M、XL等等的款式。这时候我们就需要一个button组来进行了,当时这个时候里面的尺寸可能有很多,那怎么办呢?这里我们就肯定要做个自适应的按钮组了,要不然弄出来也没用。废话不多说,先上个效果图:
是不是感觉起来效果蛮不错的呢?
现在我们就来说说里面的一些原理把!说句实话,我也不是大神,这个也是查询网上的一些原理问题弄出来的,希望大家不要嫌弃哦!么么哒!
一、原理:
1.其实这里我们用到的是一个ViewGroup控件组,把这些按钮加进去就有这种效果了!不过这里要继承ViewGroup(命名为:GoodsViewGroup)重写里面的一些方法。
2.主要的方法有:
GoodsViewGroup按钮组的控件大小
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
- protected void onLayout(boolean changed, int l, int t, int r, int b)
这两个方法的具体使用大家可以网上查阅资料,这里就不多说了!
二、代码:
- /**
- * Created by ShaoLin on 2016/8/22.
- * 这里是类似淘宝中商品尺寸按钮组(这里做了支持button,textview)
- */
- 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
1.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>
2.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
- /**
- * Created by ShaoLin on 2016/8/22.
- */
- 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();
- }
- }
- <?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" />