针对本文进行了升级,控件更加强大,提供了很多方法及主题的修改: Android 仿淘宝选中商品不同尺寸的按钮组(二)

今天刚好有个同学他想做一个仿淘宝中的选中商品不同尺寸,比如衣服有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 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 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);        }    }    /**     * 获取控件的自适应高度     *     * @return     */    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();            //------------当前按钮按钮是否在水平上够位置(2017/7/10)------------            if (viewwidth + getNextHorLastPos(i) > viewWidth) {                //------------修正没有计算所在行第一个所需宽度(2017/7/10)------------                viewwidth = (measuredWidth + HorInterval * 2);                viewheight += (measureHeight + VerInterval);            } else {                viewwidth += (measuredWidth + HorInterval);            }        }        return viewheight;    }    /**     * 当前按钮所需的宽度     * @param i     * @return     */    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 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这样呢?其实这里我是想做一个泛型,可以使用与Button跟TextView,而这里的Button本生就是继承TextView所以在代码中还要进行一个判断,可以看上面方法setItemPadding(X view)。那到了这里,有些好友可能就会问,为什么要搞两个呢?

其实这里因为TextView的不会自动有设置padding的,而button是有自动设置padding。这个时候你就要看看你是先要那种效果!不过通过我的代码中如果是选择TextView的话,这里也设置了一个padding给他,不然会很难看!

两种模式的写法:

1.Button :

GoodsViewGroup