<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/title_bg_color"
android:orientation="vertical">
<View
style="@style/ViewHorizontalLine"
android:layout_above="@+id/rg_tab_bar" />
<RadioGroup
android:id="@+id/rg_tab_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:checkedButton="@+id/rbHome"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="6dp">
<RadioButton
android:id="@+id/rbHome"
style="@style/RaidoButtonStyle"
android:drawableTop="@drawable/btn_tabbar_home_selector"
android:text="@string/tab_home" />
<RadioButton
android:id="@+id/rbBuy"
style="@style/RaidoButtonStyle"
android:drawableTop="@drawable/btn_tabbar_buy_selector"
android:text="@string/tab_buy" />
<RadioButton
android:id="@+id/rbCategory"
style="@style/RaidoButtonStyle"
android:drawableTop="@drawable/btn_tabbar_category_selector"
android:text="@string/tab_category" />
<RadioButton
android:id="@+id/rbCart"
style="@style/RaidoButtonStyle"
android:drawableTop="@drawable/btn_tabbar_cart_selector"
android:text="@string/tab_cart" />
<RadioButton
android:id="@+id/rbUserCenter"
style="@style/RaidoButtonStyle"
android:drawableTop="@drawable/btn_tabbar_user_center_selector"
android:text="@string/tab_user_center" />
RadioGroup>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_alignParentBottom="true"
android:background="@color/transparent"
android:numColumns="5"
android:orientation="horizontal">
<Button style="@style/BadgeViewButtonStyle" />
<Button style="@style/BadgeViewButtonStyle" />
<Button style="@style/BadgeViewButtonStyle" />
<Button
android:id="@+id/btn_msg"
style="@style/BadgeViewButtonStyle" />
<Button style="@style/BadgeViewButtonStyle" />
LinearLayout>
RelativeLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:clipToPadding="true"
android:fitsSystemWindows="true">
<FrameLayout
android:id="@+id/fragmentContent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/white" />
LinearLayout>
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewParent;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.TabWidget;
/**
* Description:com.mmbao.saas.util类
* Created by Administrator on 2018/1/25.
* Maxim:There is no smoke without fire
*/
public class BadgeView extends AppCompatTextView {
public static final int POSITION_TOP_LEFT = 1;
public static final int POSITION_TOP_RIGHT = 2;
public static final int POSITION_BOTTOM_LEFT = 3;
public static final int POSITION_BOTTOM_RIGHT = 4;
public static final int POSITION_CENTER = 5;
private static final int DEFAULT_MARGIN_DIP = 5;
private static final int DEFAULT_LR_PADDING_DIP = 5;
private static final int DEFAULT_CORNER_RADIUS_DIP = 8;
private static final int DEFAULT_POSITION = POSITION_TOP_RIGHT;
private static final int DEFAULT_BADGE_COLOR = Color.parseColor("#CCFF0000"); //Color.RED;
private static final int DEFAULT_TEXT_COLOR = Color.WHITE;
private static Animation fadeIn;
private static Animation fadeOut;
private Context context;
private View target;
private int badgePosition;
private int badgeMarginH;
private int badgeMarginV;
private int badgeColor;
private boolean isShown;
private ShapeDrawable badgeBg;
private int targetTabIndex;
public BadgeView(Context context) {
this(context, (AttributeSet) null, android.R.attr.textViewStyle);
}
public BadgeView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
}
/**
* Constructor -
*
* create a new BadgeView instance attached to a target {@link android.view.View}.
*
* @param context context for this view.
* @param target the View to attach the badge to.
*/
public BadgeView(Context context, View target) {
this(context, null, android.R.attr.textViewStyle, target, 0);
}
/**
* Constructor -
*
* create a new BadgeView instance attached to a target {@link android.widget.TabWidget}
* tab at a given index.
*
* @param context context for this view.
* @param target the TabWidget to attach the badge to.
* @param index the position of the tab within the target.
*/
public BadgeView(Context context, TabWidget target, int index) {
this(context, null, android.R.attr.textViewStyle, target, index);
}
public BadgeView(Context context, AttributeSet attrs, int defStyle) {
this(context, attrs, defStyle, null, 0);
}
public BadgeView(Context context, AttributeSet attrs, int defStyle, View target, int tabIndex) {
super(context, attrs, defStyle);
init(context, target, tabIndex);
}
private void init(Context context, View target, int tabIndex) {
this.context = context;
this.target = target;
this.targetTabIndex = tabIndex;
// apply defaults
badgePosition = DEFAULT_POSITION;
badgeMarginH = dipToPixels(DEFAULT_MARGIN_DIP);
badgeMarginV = badgeMarginH;
badgeColor = DEFAULT_BADGE_COLOR;
setTypeface(Typeface.DEFAULT_BOLD);
int paddingPixels = dipToPixels(DEFAULT_LR_PADDING_DIP);
setPadding(paddingPixels, 0, paddingPixels, 0);
setTextColor(DEFAULT_TEXT_COLOR);
fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new DecelerateInterpolator());
fadeIn.setDuration(200);
fadeOut = new AlphaAnimation(1, 0);
fadeOut.setInterpolator(new AccelerateInterpolator());
fadeOut.setDuration(200);
isShown = false;
if (this.target != null) {
applyTo(this.target);
} else {
show();
}
}
private void applyTo(View target) {
LayoutParams lp = target.getLayoutParams();
ViewParent parent = target.getParent();
FrameLayout container = new FrameLayout(context);
if (target instanceof TabWidget) {
// set target to the relevant tab child container
target = ((TabWidget) target).getChildTabViewAt(targetTabIndex);
this.target = target;
((ViewGroup) target).addView(container,
new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
this.setVisibility(View.GONE);
container.addView(this);
} else {
// TODO verify that parent is indeed a ViewGroup
ViewGroup group = (ViewGroup) parent;
int index = group.indexOfChild(target);
group.removeView(target);
group.addView(container, index, lp);
container.addView(target);
this.setVisibility(View.GONE);
container.addView(this);
group.invalidate();
}
}
/**
* Make the badge visible in the UI.
*/
public void show() {
show(false, null);
}
/**
* Make the badge visible in the UI.
*
* @param animate flag to apply the default fade-in animation.
*/
public void show(boolean animate) {
show(animate, fadeIn);
}
/**
* Make the badge visible in the UI.
*
* @param anim Animation to apply to the view when made visible.
*/
public void show(Animation anim) {
show(true, anim);
}
/**
* Make the badge non-visible in the UI.
*/
public void hide() {
hide(false, null);
}
/**
* Make the badge non-visible in the UI.
*
* @param animate flag to apply the default fade-out animation.
*/
public void hide(boolean animate) {
hide(animate, fadeOut);
}
/**
* Make the badge non-visible in the UI.
*
* @param anim Animation to apply to the view when made non-visible.
*/
public void hide(Animation anim) {
hide(true, anim);
}
/**
* Toggle the badge visibility in the UI.
*/
public void toggle() {
toggle(false, null, null);
}
/**
* Toggle the badge visibility in the UI.
*
* @param animate flag to apply the default fade-in/out animation.
*/
public void toggle(boolean animate) {
toggle(animate, fadeIn, fadeOut);
}
/**
* Toggle the badge visibility in the UI.
*
* @param animIn Animation to apply to the view when made visible.
* @param animOut Animation to apply to the view when made non-visible.
*/
public void toggle(Animation animIn, Animation animOut) {
toggle(true, animIn, animOut);
}
private void show(boolean animate, Animation anim) {
if (getBackground() == null) {
if (badgeBg == null) {
badgeBg = getDefaultBackground();
}
setBackgroundDrawable(badgeBg);
}
applyLayoutParams();
if (animate) {
this.startAnimation(anim);
}
this.setVisibility(View.VISIBLE);
isShown = true;
}
private void hide(boolean animate, Animation anim) {
this.setVisibility(View.GONE);
if (animate) {
this.startAnimation(anim);
}
isShown = false;
}
private void toggle(boolean animate, Animation animIn, Animation animOut) {
if (isShown) {
hide(animate && (animOut != null), animOut);
} else {
show(animate && (animIn != null), animIn);
}
}
/**
* Increment the numeric badge label. If the current badge label cannot be converted to
* an integer value, its label will be set to "0".
*
* @param offset the increment offset.
*/
public int increment(int offset) {
CharSequence txt = getText();
int i;
if (txt != null) {
try {
i = Integer.parseInt(txt.toString());
} catch (NumberFormatException e) {
i = 0;
}
} else {
i = 0;
}
i = i + offset;
setText(String.valueOf(i));
return i;
}
/**
* Decrement the numeric badge label. If the current badge label cannot be converted to
* an integer value, its label will be set to "0".
*
* @param offset the decrement offset.
*/
public int decrement(int offset) {
return increment(-offset);
}
private ShapeDrawable getDefaultBackground() {
int r = dipToPixels(DEFAULT_CORNER_RADIUS_DIP);
float[] outerR = new float[]{r, r, r, r, r, r, r, r};
RoundRectShape rr = new RoundRectShape(outerR, null, null);
ShapeDrawable drawable = new ShapeDrawable(rr);
drawable.getPaint().setColor(badgeColor);
return drawable;
}
private void applyLayoutParams() {
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
switch (badgePosition) {
case POSITION_TOP_LEFT:
lp.gravity = Gravity.LEFT | Gravity.TOP;
lp.setMargins(badgeMarginH, badgeMarginV, 0, 0);
break;
case POSITION_TOP_RIGHT:
lp.gravity = Gravity.RIGHT | Gravity.TOP;
lp.setMargins(0, badgeMarginV, badgeMarginH, 0);
break;
case POSITION_BOTTOM_LEFT:
lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
lp.setMargins(badgeMarginH, 0, 0, badgeMarginV);
break;
case POSITION_BOTTOM_RIGHT:
lp.gravity = Gravity.RIGHT | Gravity.BOTTOM;
lp.setMargins(0, 0, badgeMarginH, badgeMarginV);
break;
case POSITION_CENTER:
lp.gravity = Gravity.CENTER;
lp.setMargins(0, 0, 0, 0);
break;
default:
break;
}
setLayoutParams(lp);
}
/**
* Returns the target View this badge has been attached to.
*/
public View getTarget() {
return target;
}
/**
* Is this badge currently visible in the UI?
*/
@Override
public boolean isShown() {
return isShown;
}
/**
* Returns the positioning of this badge.
*
* one of POSITION_TOP_LEFT, POSITION_TOP_RIGHT, POSITION_BOTTOM_LEFT, POSITION_BOTTOM_RIGHT, POSTION_CENTER.
*/
public int getBadgePosition() {
return badgePosition;
}
/**
* Set the positioning of this badge.
*
* @param layoutPosition one of POSITION_TOP_LEFT, POSITION_TOP_RIGHT, POSITION_BOTTOM_LEFT, POSITION_BOTTOM_RIGHT, POSTION_CENTER.
*/
public void setBadgePosition(int layoutPosition) {
this.badgePosition = layoutPosition;
}
/**
* Returns the horizontal margin from the target View that is applied to this badge.
*/
public int getHorizontalBadgeMargin() {
return badgeMarginH;
}
/**
* Returns the vertical margin from the target View that is applied to this badge.
*/
public int getVerticalBadgeMargin() {
return badgeMarginV;
}
/**
* Set the horizontal/vertical margin from the target View that is applied to this badge.
*
* @param badgeMargin the margin in pixels.
*/
public void setBadgeMargin(int badgeMargin) {
this.badgeMarginH = badgeMargin;
this.badgeMarginV = badgeMargin;
}
/**
* Set the horizontal/vertical margin from the target View that is applied to this badge.
*
* @param horizontal margin in pixels.
* @param vertical margin in pixels.
*/
public void setBadgeMargin(int horizontal, int vertical) {
this.badgeMarginH = horizontal;
this.badgeMarginV = vertical;
}
/**
* Returns the color value of the badge background.
*/
public int getBadgeBackgroundColor() {
return badgeColor;
}
/**
* Set the color value of the badge background.
*
* @param badgeColor the badge background color.
*/
public void setBadgeBackgroundColor(int badgeColor) {
this.badgeColor = badgeColor;
badgeBg = getDefaultBackground();
}
private int dipToPixels(int dip) {
Resources r = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, r.getDisplayMetrics());
return (int) px;
}
}
public class DensityUtil {
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
}
public class MainActivity extends AppCompatActivity {
@BindView(R.id.fragmentContent)
FrameLayout fragmentContent;
@BindView(R.id.rbHome)
RadioButton rbHome;
@BindView(R.id.rbBuy)
RadioButton rbBuy;
@BindView(R.id.rbCategory)
RadioButton rbCategory;
@BindView(R.id.rbCart)
RadioButton rbCart;
@BindView(R.id.rbUserCenter)
RadioButton rbUserCenter;
private int count = 99;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
Button btnMsg = findViewById(R.id.btn_msg);
setCartMsg(btnMsg);
}
/**
* 显示购物车上的数量
* @param btnMsg
*/
private void setCartMsg(Button btnMsg) {
BadgeView badgeView = new BadgeView(this,btnMsg);
badgeView.setBadgePosition(BadgeView.POSITION_TOP_RIGHT);
badgeView.setTextColor(Color.WHITE);
badgeView.setBadgeBackgroundColor(Color.RED);
badgeView.setTextSize(12);
badgeView.setBadgeMargin(5);
if (TextUtils.isEmpty(count+"")){
badgeView.hide();
}else {
if (count>99){
count = 99;
badgeView.setText(""+count);
}else {
badgeView.setText(""+count);
}
badgeView.show();
}
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:clipToPadding="true"
android:fitsSystemWindows="true">
<FrameLayout
android:id="@+id/fragmentContent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/white" />
<com.mmbao.saas.custom.MyRadioGroup
android:id="@+id/rg_tab_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:checkedButton="@+id/rbHome"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="6dp">
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_weight="1" >
<RadioButton
android:id="@+id/rbHome"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_gravity="center_horizontal"
android:button="@null"
android:textSize="@dimen/bottom_menu_text_size"
android:textColor="@color/tab_text_color"
android:checked="true"
android:drawableTop="@drawable/btn_tabbar_home_selector"
android:text="@string/tab_home" />
<Button
android:visibility="gone"
android:layout_gravity="top|right"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:text="11"
android:background="@null"
android:textColor="@color/white"
android:textSize="12sp"
android:layout_width="21dp"
android:layout_height="21dp" />
FrameLayout>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_weight="1" >
<RadioButton
android:id="@+id/rbBuy"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_gravity="center_horizontal"
android:button="@null"
android:textSize="@dimen/bottom_menu_text_size"
android:textColor="@color/tab_text_color"
android:drawableTop="@drawable/btn_tabbar_buy_selector"
android:text="@string/tab_buy" />
<Button
android:visibility="gone"
android:layout_gravity="top|right"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:text="11"
android:background="@null"
android:textColor="@color/white"
android:textSize="12sp"
android:layout_width="21dp"
android:layout_height="21dp" />
FrameLayout>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_weight="1" >
<RadioButton
android:id="@+id/rbCategory"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_gravity="center_horizontal"
android:button="@null"
android:textSize="@dimen/bottom_menu_text_size"
android:textColor="@color/tab_text_color"
android:drawableTop="@drawable/btn_tabbar_category_selector"
android:text="@string/tab_category" />
<Button
android:visibility="gone"
android:layout_gravity="top|right"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:background="@null"
android:textColor="@color/white"
android:textSize="12sp"
android:layout_width="21dp"
android:layout_height="21dp" />
FrameLayout>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_weight="1" >
<RadioButton
android:id="@+id/rbCart"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_gravity="center_horizontal"
android:button="@null"
android:textSize="@dimen/bottom_menu_text_size"
android:textColor="@color/tab_text_color"
android:drawableTop="@drawable/btn_tabbar_cart_selector"
android:text="@string/tab_cart" />
<Button
android:layout_gravity="top|right"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:text="11"
android:background="@drawable/circle_bg"
android:textColor="@color/white"
android:textSize="12sp"
android:layout_width="21dp"
android:layout_height="21dp" />
FrameLayout>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_weight="1" >
<RadioButton
android:id="@+id/rbUserCenter"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:layout_gravity="center_horizontal"
android:button="@null"
android:textSize="@dimen/bottom_menu_text_size"
android:textColor="@color/tab_text_color"
android:drawableTop="@drawable/btn_tabbar_user_center_selector"
android:text="@string/tab_user_center" />
<Button
android:visibility="gone"
android:layout_gravity="top|right"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:text="11"
android:background="@null"
android:textColor="@color/white"
android:textSize="12sp"
android:layout_width="21dp"
android:layout_height="21dp" />
FrameLayout>
com.mmbao.saas.custom.MyRadioGroup>
LinearLayout>
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.RadioButton;
/**
* Description:com.mmbao.saas.custom类
* Created by Administrator on 2018/1/27.
* Maxim:There is no smoke without fire
*/
public class MyRadioGroup extends LinearLayout {
// holds the checked id; the selection is empty by default
private int mCheckedId = -1;
// tracks children radio buttons checked state
private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
// when true, mOnCheckedChangeListener discards events
private boolean mProtectFromCheckedChange = false;
private OnCheckedChangeListener mOnCheckedChangeListener;
private PassThroughHierarchyChangeListener mPassThroughListener;
public MyRadioGroup(Context context) {
super(context);
setOrientation(VERTICAL);
init();
}
public MyRadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mChildOnCheckedChangeListener = new CheckedStateTracker();
mPassThroughListener = new PassThroughHierarchyChangeListener();
super.setOnHierarchyChangeListener(mPassThroughListener);
}
@Override
public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
// the user listener is delegated to our pass-through listener
mPassThroughListener.mOnHierarchyChangeListener = listener;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// checks the appropriate radio button as requested in the XML file
if (mCheckedId != -1) {
mProtectFromCheckedChange = true;
setCheckedStateForView(mCheckedId, true);
mProtectFromCheckedChange = false;
setCheckedId(mCheckedId);
}
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (child instanceof RadioButton) {
final RadioButton button = (RadioButton) child;
if (button.isChecked()) {
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
setCheckedId(button.getId());
}
} else if (child instanceof ViewGroup) {
final RadioButton button = findRadioButton((ViewGroup) child);
if (button.isChecked()) {
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
setCheckedId(button.getId());
}
}
super.addView(child, index, params);
}
/** 查找radioButton控件 */
public RadioButton findRadioButton(ViewGroup group) {
RadioButton resBtn = null;
int len = group.getChildCount();
for (int i = 0; i < len; i++) {
if (group.getChildAt(i) instanceof RadioButton) {
resBtn = (RadioButton) group.getChildAt(i);
} else if (group.getChildAt(i) instanceof ViewGroup) {
findRadioButton((ViewGroup) group.getChildAt(i));
}
}
return resBtn;
}
/**
*
* Sets the selection to the radio button whose identifier is passed in
* parameter. Using -1 as the selection identifier clears the selection;
* such an operation is equivalent to invoking {@link #clearCheck()}.
*
*
* @param id
* the unique id of the radio button to select in this group
*
* @see #getCheckedRadioButtonId()
* @see #clearCheck()
*/
public void check(int id) {
// don't even bother
if (id != -1 && (id == mCheckedId)) {
return;
}
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
if (id != -1) {
setCheckedStateForView(id, true);
}
setCheckedId(id);
}
private void setCheckedId(int id) {
mCheckedId = id;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
}
private void setCheckedStateForView(int viewId, boolean checked) {
View checkedView = findViewById(viewId);
if (checkedView != null && checkedView instanceof RadioButton) {
((RadioButton) checkedView).setChecked(checked);
}else if (checkedView != null && checkedView instanceof ViewGroup){
((RadioButton) checkedView).setChecked(checked);
}
}
/**
*
* Returns the identifier of the selected radio button in this group. Upon
* empty selection, the returned value is -1.
*
*
* @return the unique id of the selected radio button in this group
*
* @see #check(int)
* @see #clearCheck()
*/
public int getCheckedRadioButtonId() {
return mCheckedId;
}
/**
*
* Clears the selection. When the selection is cleared, no radio button in
* this group is selected and {@link #getCheckedRadioButtonId()} returns
* null.
*
*
* @see #check(int)
* @see #getCheckedRadioButtonId()
*/
public void clearCheck() {
check(-1);
}
/**
*
* Register a callback to be invoked when the checked radio button changes
* in this group.
*
*
* @param listener
* the callback to call on checked state change
*/
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
mOnCheckedChangeListener = listener;
}
/**
* {@inheritDoc}
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MyRadioGroup.LayoutParams(getContext(), attrs);
}
/**
* {@inheritDoc}
*/
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof MyRadioGroup.LayoutParams;
}
@Override
protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
}
/**
*
* This set of layout parameters defaults the width and the height of the
* children to {@link #WRAP_CONTENT} when they are not specified in the XML
* file. Otherwise, this class ussed the value read from the XML file.
*
*
*
* See {@link android.R.styleable#LinearLayout_Layout LinearLayout
* Attributes} for a list of all child view attributes that this class
* supports.
*
*
*/
public static class LayoutParams extends LinearLayout.LayoutParams {
/**
* {@inheritDoc}
*/
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
/**
* {@inheritDoc}
*/
public LayoutParams(int w, int h) {
super(w, h);
}
/**
* {@inheritDoc}
*/
public LayoutParams(int w, int h, float initWeight) {
super(w, h, initWeight);
}
/**
* {@inheritDoc}
*/
public LayoutParams(ViewGroup.LayoutParams p) {
super(p);
}
/**
* {@inheritDoc}
*/
public LayoutParams(MarginLayoutParams source) {
super(source);
}
/**
*
* Fixes the child's width to
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and the
* child's height to
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} when not
* specified in the XML file.
*
*
* @param a
* the styled attributes set
* @param widthAttr
* the width attribute to fetch
* @param heightAttr
* the height attribute to fetch
*/
@Override
protected void setBaseAttributes(TypedArray a, int widthAttr,
int heightAttr) {
if (a.hasValue(widthAttr)) {
width = a.getLayoutDimension(widthAttr, "layout_width");
} else {
width = WRAP_CONTENT;
}
if (a.hasValue(heightAttr)) {
height = a.getLayoutDimension(heightAttr, "layout_height");
} else {
height = WRAP_CONTENT;
}
}
}
/**
*
* Interface definition for a callback to be invoked when the checked radio
* button changed in this group.
*
*/
public interface OnCheckedChangeListener {
/**
*
* Called when the checked radio button has changed. When the selection
* is cleared, checkedId is -1.
*
*
* @param group
* the group in which the checked radio button has changed
* @param checkedId
* the unique identifier of the newly checked radio button
*/
public void onCheckedChanged(MyRadioGroup group, int checkedId);
}
private class CheckedStateTracker implements
CompoundButton.OnCheckedChangeListener {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
// prevents from infinite recursion
if (mProtectFromCheckedChange) {
return;
}
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
int id = buttonView.getId();
setCheckedId(id);
}
}
/**
*
* A pass-through listener acts upon the events and dispatches them to
* another listener. This allows the table layout to set its own internal
* hierarchy change listener without preventing the user to setup his.
*
*/
private class PassThroughHierarchyChangeListener implements
ViewGroup.OnHierarchyChangeListener {
private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener;
public void onChildViewAdded(View parent, View child) {
if (parent == MyRadioGroup.this && child instanceof RadioButton) {
int id = child.getId();
// generates an id if it's missing
if (id == View.NO_ID) {
id = child.hashCode();
child.setId(id);
}
((RadioButton) child)
.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
} else if (parent == MyRadioGroup.this
&& child instanceof ViewGroup) {
RadioButton btn = findRadioButton((ViewGroup) child);
int id = btn.getId();
// generates an id if it's missing
if (id == View.NO_ID) {
id = btn.hashCode();
btn.setId(id);
}
btn.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
}
if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewAdded(parent, child);
}
}
public void onChildViewRemoved(View parent, View child) {
if (parent == MyRadioGroup.this && child instanceof RadioButton) {
((RadioButton) child).setOnCheckedChangeListener(null);
} else if (parent == MyRadioGroup.this
&& child instanceof ViewGroup) {
findRadioButton((ViewGroup) child).setOnCheckedChangeListener(
null);
}
if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
}
}
}
}
--view horizontal line-->
--RadioButton bottom tab bar -->
--BadgeView bottom button -->
<string name="app_name">MYstring>
<string name="tab_home">首页string>
<string name="tab_buy">求购string>
<string name="tab_category">分类string>
<string name="tab_cart">购物车string>
<string name="tab_user_center">个人中心string>
<resources>
<dimen name="activity_horizontal_margin">16dpdimen>
<dimen name="activity_vertical_margin">16dpdimen>
<dimen name="fab_margin_right">0dpdimen>
<dimen name="fab_margin_bottom">0dpdimen>
<dimen name="title_text_size">20spdimen>
<dimen name="bottom_menu_text_size">12spdimen>
<dimen name="list_live_name_size">13spdimen>
<dimen name="list_live_title_size">13spdimen>
<dimen name="list_live_more_size">12spdimen>
resources>
<resources>
<color name="colorPrimary">#3F51B5color>
<color name="colorPrimaryDark">#303F9Fcolor>
<color name="colorAccent">#FF4081color>
<color name="transparent">#00000000color>
<color name="line_bg">#CECECEcolor>
<color name="title_bg_color">#fafafacolor>
<color name="tab_text_color_normal">#A7A7A7color>
<color name="tab_text_color_selected">#3d9cfccolor>
resources>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="false" android:drawable="@drawable/btn_tabbar_buy_normal"/>
<item android:state_checked="true" android:drawable="@drawable/btn_tabbar_buy_selected"/>
selector>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="false" android:color="@color/tab_text_color_normal"/>
<item android:state_checked="true" android:color="@color/tab_text_color_selected"/>
selector>
/**
* 支持嵌套CompoundButton的NestRadioGroup
*
* @author 农民伯伯 http://www.cnblogs.com/over140/
*
*/
public class NestRadioGroup extends LinearLayout {
// holds the checked id; the selection is empty by default
private int mCheckedId = -1;
// tracks children radio buttons checked state
private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
// when true, mOnCheckedChangeListener discards events
private boolean mProtectFromCheckedChange = false;
private OnCheckedChangeListener mOnCheckedChangeListener;
private PassThroughHierarchyChangeListener mPassThroughListener;
/**
* {@inheritDoc}
*/
public NestRadioGroup(Context context) {
super(context);
init();
}
/**
* {@inheritDoc}
*/
public NestRadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mCheckedId = View.NO_ID;
setOrientation(HORIZONTAL);
mChildOnCheckedChangeListener = new CheckedStateTracker();
mPassThroughListener = new PassThroughHierarchyChangeListener();
super.setOnHierarchyChangeListener(mPassThroughListener);
}
/**
* {@inheritDoc}
*/
@Override
public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
// the user listener is delegated to our pass-through listener
mPassThroughListener.mOnHierarchyChangeListener = listener;
}
/**
* {@inheritDoc}
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// checks the appropriate radio button as requested in the XML file
if (mCheckedId != View.NO_ID) {
mProtectFromCheckedChange = true;
setCheckedStateForView(mCheckedId, true);
mProtectFromCheckedChange = false;
setCheckedId(mCheckedId);
}
}
/** 递归查找具有选中属性的子控件 */
private static CompoundButton findCheckedView(View child) {
if (child instanceof CompoundButton)
return (CompoundButton) child;
if (child instanceof ViewGroup) {
ViewGroup group = (ViewGroup) child;
for (int i = 0, j = group.getChildCount(); i < j; i++) {
CompoundButton check = findCheckedView(group.getChildAt(i));
if (check != null)
return check;
}
}
return null;//没有找到
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
final CompoundButton view = findCheckedView(child);
if (view != null) {
if (view.isChecked()) {
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
setCheckedId(view.getId());
}
}
super.addView(child, index, params);
}
/**
* Sets the selection to the radio button whose identifier is passed in
* parameter. Using -1 as the selection identifier clears the selection;
* such an operation is equivalent to invoking {@link #clearCheck()}.
*
* @param id the unique id of the radio button to select in this group
*
* @see #getCheckedRadioButtonId()
* @see #clearCheck()
*/
public void check(int id) {
// don't even bother
if (id != -1 && (id == mCheckedId)) {
return;
}
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
if (id != -1) {
setCheckedStateForView(id, true);
}
setCheckedId(id);
}
private void setCheckedId(int id) {
mCheckedId = id;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
}
private void setCheckedStateForView(int viewId, boolean checked) {
View checkedView = findViewById(viewId);
if (checkedView != null && checkedView instanceof CompoundButton) {
((CompoundButton) checkedView).setChecked(checked);
}
}
/**
* Returns the identifier of the selected radio button in this group.
* Upon empty selection, the returned value is -1.
*
* @return the unique id of the selected radio button in this group
*
* @see #check(int)
* @see #clearCheck()
*
* @attr ref android.R.styleable#NestRadioGroup_checkedButton
*/
public int getCheckedRadioButtonId() {
return mCheckedId;
}
/**
* Clears the selection. When the selection is cleared, no radio button
* in this group is selected and {@link #getCheckedRadioButtonId()} returns
* null.
*
* @see #check(int)
* @see #getCheckedRadioButtonId()
*/
public void clearCheck() {
check(-1);
}
/**
* Register a callback to be invoked when the checked radio button
* changes in this group.
*
* @param listener the callback to call on checked state change
*/
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
mOnCheckedChangeListener = listener;
}
/**
* {@inheritDoc}
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new NestRadioGroup.LayoutParams(getContext(), attrs);
}
/**
* {@inheritDoc}
*/
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof NestRadioGroup.LayoutParams;
}
@Override
protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
/**
* This set of layout parameters defaults the width and the height of
* the children to {@link #WRAP_CONTENT} when they are not specified in the
* XML file. Otherwise, this class ussed the value read from the XML file.
*
* See
* {@link android.R.styleable#LinearLayout_Layout LinearLayout Attributes}
* for a list of all child view attributes that this class supports.
*
*/
public static class LayoutParams extends LinearLayout.LayoutParams {
/**
* {@inheritDoc}
*/
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
/**
* {@inheritDoc}
*/
public LayoutParams(int w, int h) {
super(w, h);
}
/**
* {@inheritDoc}
*/
public LayoutParams(int w, int h, float initWeight) {
super(w, h, initWeight);
}
/**
* {@inheritDoc}
*/
public LayoutParams(ViewGroup.LayoutParams p) {
super(p);
}
/**
* {@inheritDoc}
*/
public LayoutParams(MarginLayoutParams source) {
super(source);
}
/**
* Fixes the child's width to
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and the child's
* height to {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
* when not specified in the XML file.
*
* @param a the styled attributes set
* @param widthAttr the width attribute to fetch
* @param heightAttr the height attribute to fetch
*/
@Override
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
if (a.hasValue(widthAttr)) {
width = a.getLayoutDimension(widthAttr, "layout_width");
} else {
width = WRAP_CONTENT;
}
if (a.hasValue(heightAttr)) {
height = a.getLayoutDimension(heightAttr, "layout_height");
} else {
height = WRAP_CONTENT;
}
}
}
/**
* Interface definition for a callback to be invoked when the checked
* radio button changed in this group.
*/
public interface OnCheckedChangeListener {
/**
* Called when the checked radio button has changed. When the
* selection is cleared, checkedId is -1.
*
* @param group the group in which the checked radio button has changed
* @param checkedId the unique identifier of the newly checked radio button
*/
public void onCheckedChanged(NestRadioGroup group, int checkedId);
}
private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// prevents from infinite recursion
if (mProtectFromCheckedChange) {
return;
}
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
int id = buttonView.getId();
setCheckedId(id);
}
}
/**
* A pass-through listener acts upon the events and dispatches them
* to another listener. This allows the table layout to set its own internal
* hierarchy change listener without preventing the user to setup his.
*/
private class PassThroughHierarchyChangeListener implements ViewGroup.OnHierarchyChangeListener {
private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener;
/**
* {@inheritDoc}
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public void onChildViewAdded(View parent, View child) {
if (parent == NestRadioGroup.this) {
CompoundButton view = findCheckedView(child);//查找子控件
if (view != null) {
int id = view.getId();
// generates an id if it's missing
if (id == View.NO_ID && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
id = View.generateViewId();
view.setId(id);
}
view.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
}
}
if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewAdded(parent, child);
}
}
/**
* {@inheritDoc}
*/
public void onChildViewRemoved(View parent, View child) {
if (parent == NestRadioGroup.this) {
CompoundButton view = findCheckedView(child);//查找子控件
if (view != null) {
view.setOnCheckedChangeListener(null);
}
}
if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
}
}
}
复制代码
}
<com.xxx.ui.view.NestRadioGroup
android:id="@+id/main_radio"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal" >
"@+id/radio_button0"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:checked="true"
android:text="@string/bottom_feed" />
"@+id/radio_button1"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:text="@string/bottom_square" />
"0dip"
android:layout_height="fill_parent"
android:layout_weight="1.0"
android:orientation="horizontal" >
"@+id/radio_button2"
style="@style/title_tab_text_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/bottom_message" />
"@+id/new_message_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:layout_toRightOf="@+id/radio_button2"
android:src="@drawable/news_tips_red" />
com.xxx.ui.view.NestRadioGroup>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/Red"/>
shape>