android继承RadioGroup实现添加小红点 旋转drawableTop原来的配方原来的味道

github.com源码库 实现了加小红点,以及解决原来的drawableTop旋转 不能调节速度问题,卡顿问题

我这里的app:drawableTop支持旋转动画

https://github.com/qssq/radiogroupx

这是效果图

顺便亮一亮我的代码堆栈

      at cn.qssq666.radiogroupx.RadioGroupX.setCheckedStateForView(RadioGroupX.java:160)
      at cn.qssq666.radiogroupx.RadioGroupX.access$500(RadioGroupX.java:35)
      at cn.qssq666.radiogroupx.RadioGroupX$CheckedStateTracker.onCheckedChanged(RadioGroupX.java:322)
      at cn.qssq666.radiogroupx.ProxyWrapRadioButtonInner$1.onCheckedChanged(ProxyWrapRadioButtonInner.java:96)
      at android.widget.CompoundButton.setChecked(CompoundButton.java:159)
      at android.widget.CompoundButton.toggle(CompoundButton.java:115)
      at android.widget.RadioButton.toggle(RadioButton.java:76)
      at android.widget.CompoundButton.performClick(CompoundButton.java:120)
      at android.view.View$PerformClick.run(View.java:22295)

RadioGroup修改进行排斥,如何进行扩展兼容原来的RadioButton 其实 ,RadioButtonCheckBox的父类CompoundButton类有一个内部的选中监听类,
在调用setCheck的时候会回调2个监听

    if (mOnCheckedChangeListener != null) {
                mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
            }
            if (mOnCheckedChangeWidgetListener != null) {
                mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
            }

我这里mOnCheckedChangeWidgetListener是通过反射才能够实现兼容RadioButton的 这有啥办法,谁叫它私有化了,这让我怎么搞嘛。.

直接上源码

package cn.qssq666.radiogroupx;/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Checkable;
import android.widget.CompoundButton;
import android.widget.LinearLayout;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Random;

public class RadioGroupX 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 RadioGroupX(Context context) {
        super(context);
        setOrientation(VERTICAL);
        init();
    }

    public RadioGroupX(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray attributes = context.obtainStyledAttributes(
                attrs, R.styleable.RadioGroupX, R.attr.radioButtonStyle, 0);

        int value = attributes.getResourceId(R.styleable.RadioGroupX_checkedButton, View.NO_ID);
        if (value != View.NO_ID) {
            mCheckedId = value;
        }

//        int orientation = getOrientation();

        final int index = attributes.getInt(R.styleable.RadioGroupX_orientation, VERTICAL);
        setOrientation(index);
        attributes.recycle();
        init();
    }

    private void init() {
        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 != -1) {
            mProtectFromCheckedChange = true;
            setCheckedStateForView(mCheckedId, true);
            mProtectFromCheckedChange = false;
            setCheckedId(mCheckedId);
        }
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (child instanceof Checkable && child instanceof View) {
            final Checkable button = (Checkable) child;
            if (button.isChecked()) {
                mProtectFromCheckedChange = true;
                if (mCheckedId != -1) {
                    setCheckedStateForView(mCheckedId, false);
                }
                mProtectFromCheckedChange = false;

                setCheckedId(((View) button).getId());
            }
        }

        super.addView(child, index, params);
    }

    /**
     * <p>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()}.</p>
     *
     * @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 Checkable) {
            ((Checkable) checkedView).setChecked(checked);
        }
    }

    /**
     * <p>Returns the identifier of the selected radio button in this group.
     * Upon empty selection, the returned value is -1.</p>
     *
     * @return the unique id of the selected radio button in this group
     * @attr ref android.R.styleable#RadioGroup_checkedButton
     * @see #check(int)
     * @see #clearCheck()
     */
    public int getCheckedRadioButtonId() {
        return mCheckedId;
    }

    /**
     * <p>Clears the selection. When the selection is cleared, no radio button
     * in this group is selected and {@link #getCheckedRadioButtonId()} returns
     * null.</p>
     *
     * @see #check(int)
     * @see #getCheckedRadioButtonId()
     */
    public void clearCheck() {
        check(-1);
    }

    /**
     * <p>Register a callback to be invoked when the checked radio button
     * changes in this group.</p>
     *
     * @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 RadioGroupX.LayoutParams(getContext(), attrs);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof RadioGroupX.LayoutParams;
    }

    @Override
    protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }

    @Override
    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
        super.onInitializeAccessibilityEvent(event);
        event.setClassName(RadioGroupX.class.getName());
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(RadioGroupX.class.getName());
    }

    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);
        }

        /**
         * <p>Fixes the child's width to
         * {@link ViewGroup.LayoutParams#WRAP_CONTENT} and the child's
         * height to  {@link ViewGroup.LayoutParams#WRAP_CONTENT}
         * when not specified in the XML file.</p>
         *
         * @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;
            }
        }
    }

    /**
     * <p>Interface definition for a callback to be invoked when the checked
     * radio button changed in this group.</p>
     */
    public interface OnCheckedChangeListener {
        /**
         * <p>Called when the checked radio button has changed. When the
         * selection is cleared, checkedId is -1.</p>
         *
         * @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(RadioGroupX 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);
        }
    }

    /**
     * <p>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.</p>
     */
    private class PassThroughHierarchyChangeListener implements
            OnHierarchyChangeListener {
        private OnHierarchyChangeListener mOnHierarchyChangeListener;

        /**
         * {@inheritDoc}
         */
        public void onChildViewAdded(View parent, View child) {
            if ((child instanceof Checkable || child instanceof RadioGroupX.OnCheckedChangeWidgetListener)) {
//            if (parent == RadioGroupX.this && (child instanceof Checkable || child instanceof RadioGroupX.OnCheckedChangeWidgetListener)) {
                int id = child.getId();
                // generates an id if it's missing
                if (id == View.NO_ID) {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                        id = View.generateViewId();
                    } else {
                        id = new Random().nextInt(10000);
                    }
                    child.setId(id);
                }
                setRadioButtonOnCheckedChangeWidgetListener(child,
                        mChildOnCheckedChangeListener);
            }

            if (mOnHierarchyChangeListener != null) {
                mOnHierarchyChangeListener.onChildViewAdded(parent, child);
            }
        }

        /**
         * {@inheritDoc}
         */
        public void onChildViewRemoved(View parent, View child) {
            if ((child instanceof Checkable || child instanceof RadioGroupX.OnCheckedChangeWidgetListener)) {
//            if (parent == RadioGroupX.this && (child instanceof Checkable || child instanceof RadioGroupX.OnCheckedChangeWidgetListener)) {
                setRadioButtonOnCheckedChangeWidgetListener(child, null);
            }

            if (mOnHierarchyChangeListener != null) {
                mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
            }
        }
    }


    public static boolean setRadioButtonOnCheckedChangeWidgetListener(View view, CompoundButton.OnCheckedChangeListener listener) {
        Class aClass = view.getClass();
        if (view instanceof OnCheckedChangeWidgetListener) {
            ((OnCheckedChangeWidgetListener) view).setOnCheckedChangeWidgetListener(listener);
        } else {
            while (aClass != Object.class) {
           /*
             void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) {
        mOnCheckedChangeWidgetListener = listener;
    }

            */
                try {
                    Method setOnCheckedChangeWidgetListener = aClass.getDeclaredMethod("setOnCheckedChangeWidgetListener", CompoundButton.OnCheckedChangeListener.class);
                    setOnCheckedChangeWidgetListener.setAccessible(true);
                    setOnCheckedChangeWidgetListener.invoke(view, listener);
                    return true;
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                    aClass = aClass.getSuperclass();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    aClass = aClass.getSuperclass();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                    aClass = aClass.getSuperclass();
                }
            }

        }
        return false;

    }

    public interface OnCheckedChangeWidgetListener {
        void setOnCheckedChangeWidgetListener(CompoundButton.OnCheckedChangeListener onCheckedChangeWidgetListener);
    }
}

BadgeRadioButton

package cn.qssq666.radiogroupx;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.text.InputFilter;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.widget.Checkable;
import android.widget.CompoundButton;
import android.widget.RadioButton;
import android.widget.RelativeLayout;
import android.widget.TextView;


/**
 * Created by qssq on 2018/1/19 qssq666@foxmail.com
 */

public class BadgeRadioButton extends RelativeLayout implements Checkable, RadioGroupX.OnCheckedChangeWidgetListener {

    private static final String TAG = "BadgeRadioButton";
    private RadioButton radioButton;
    private boolean isShown;
    private TextView badgePointView;
    private int minBadgeSize;
    private int badgeRadius;

    public BadgeRadioButton(Context context) {
        super(context);
        init(context, null, 0);
    }

    public BadgeRadioButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }


    ColorStateList textColor = null;
    ColorStateList textColorHint = null;
    int textSize = 10;
    Drawable drawableLeft = null, drawableTop = null, drawableRight = null,
            drawableBottom = null, drawableStart = null, drawableEnd = null;
    int drawablePadding = 0;
    int ellipsize = -1;
    CharSequence text = "";
    CharSequence badgetext = "";
    int badgetextSize = 12;

    public boolean isNotNeedDight() {
        return notNeedDight;
    }

    public void setNotNeedDight(boolean notNeedDight) {
        this.notNeedDight = notNeedDight;
//        int i = dp2px(getContext(), 5);
        if (notNeedDight) {
//        this.badgePointView.setPadding(0, 0, 0, 0);

            setBadgePointPadding(0);
        } else {
            int padding = dp2px(getContext(), 1.5);
            setBadgePointPadding(padding);
        }
//        postInvalidate();
    }

    /**
     * 不需要数字表示只需要一个红点.这个时候padding需要修改一下
     */
    boolean notNeedDight;
    ColorStateList badgetextColor = null;


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return radioButton.onTouchEvent(event);
//        return super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {

        final Resources.Theme theme = context.getTheme();

        /*
         * Look the appearance up without checking first if it exists because
         * almost every TextView has one and it greatly simplifies the logic
         * to be able to parse the appearance first and then let specific tags
         * for this View override it.
         */
        radioButton = new RadioButton(context, attrs, defStyleAttr);

        //配置多少就有多少.的tyearray
        TypedArray typedArrayTextView = theme.obtainStyledAttributes(
                attrs, R.styleable.BadgeRadioButton, defStyleAttr, 0);

        int count = typedArrayTextView.getIndexCount();
        if (BuildConfig.DEBUG) {
            Prt.w(TAG, "badgeButton Count:" + count);

        }
        /*
                app:badgeRadius="8dp"
                    app:badgetextSize="5dp"
                    app:minBadgeSize="2dp"
         */
        notNeedDight = typedArrayTextView.getBoolean(R.styleable.BadgeRadioButton_onlypointer, false);  //设置描边颜色
        badgetextSize = typedArrayTextView.getDimensionPixelSize(R.styleable.BadgeRadioButton_badgetextSize, isNotNeedDight() ?
                dp2px(getContext(), 5) : dp2px(getContext(), 10));
        minBadgeSize = typedArrayTextView.getDimensionPixelSize(R.styleable.BadgeRadioButton_minBadgeSize, isNotNeedDight() ?
                dp2px(getContext(), 2) : dp2px(getContext(), 16));
        badgeRadius = typedArrayTextView.getDimensionPixelSize(R.styleable.BadgeRadioButton_badgeRadius, isNotNeedDight() ?
                dp2px(getContext(), 8) : dp2px(getContext(), 8));
        int badgePadding = typedArrayTextView.getDimensionPixelSize(R.styleable.BadgeRadioButton_badgePadding, isNotNeedDight() ? 0 : dp2px(getContext(), 2));

        for (int i = 0; i < count; i++) {
            int attr = typedArrayTextView.getIndex(i);

            switch (attr) {
                case R.styleable.BadgeRadioButton_drawableLeft:
                    drawableLeft = typedArrayTextView.getDrawable(attr);
                    break;

                case R.styleable.BadgeRadioButton_drawableTop:
                    drawableTop = typedArrayTextView.getDrawable(attr);
                    break;

                case R.styleable.BadgeRadioButton_drawableRight:
                    drawableRight = typedArrayTextView.getDrawable(attr);
                    break;

                case R.styleable.BadgeRadioButton_drawableBottom:
                    drawableBottom = typedArrayTextView.getDrawable(attr);
                    break;

                case R.styleable.BadgeRadioButton_drawableStart:
                    drawableStart = typedArrayTextView.getDrawable(attr);
                    break;
          /*      case R.styleable.BadgeRadioButton_onlypointer:
                    notNeedDight = typedArrayTextView.getBoolean(attr, false);//default need dight 不需要数字和仅仅需要点一个意思 由于循环优先级出现问题,导致默认只也有问题
                    break;*/

                case R.styleable.BadgeRadioButton_drawableEnd:
                    drawableEnd = typedArrayTextView.getDrawable(attr);
                    break;


                case R.styleable.BadgeRadioButton_drawablePadding:
                    drawablePadding = typedArrayTextView.getDimensionPixelSize(attr, drawablePadding);
                    break;
                case R.styleable.BadgeRadioButton_buttongravity:
                    radioButton.setGravity(typedArrayTextView.getInt(attr, -1));
                    break;
                case R.styleable.BadgeRadioButton_text:
                    text = typedArrayTextView.getText(attr);
                    break;
                case R.styleable.BadgeRadioButton_badgetext:
                    badgetext = typedArrayTextView.getText(attr);
                    if (!isNotNeedDight() && TextUtils.isEmpty(badgetext)) {
//                        badgetext = " ";
                    }
                    break;
                case R.styleable.BadgeRadioButton_ellipsize:
                    ellipsize = typedArrayTextView.getInt(attr, ellipsize);
                    break;


                case R.styleable.BadgeRadioButton_enabled:
                    setEnabled(typedArrayTextView.getBoolean(attr, isEnabled()));
                    break;


                case R.styleable.BadgeRadioButton_buttontextColor:
                    textColor = typedArrayTextView.getColorStateList(attr);
                    break;
                case R.styleable.BadgeRadioButton_badgetextColor:
                    badgetextColor = typedArrayTextView.getColorStateList(attr);
                    break;
                case R.styleable.BadgeRadioButton_buttontextSize:
                    textSize = typedArrayTextView.getDimensionPixelSize(attr, textSize);
                    break;
            }
        }

        typedArrayTextView.recycle();
        radioButton.setTextColor(textColor != null ? textColor : ColorStateList.valueOf(0xFF000000));

        radioButton.setButtonDrawable(0);

        radioButton.setBackgroundResource(0);
        setClickable(true);
        radioButton.setClickable(true);
        radioButton.setHintTextColor(textColorHint != null ? textColorHint : ColorStateList.valueOf(0xFF000000));
        radioButton.setText(text);
        fadeIn = new AlphaAnimation(0, 1);
        fadeIn.setInterpolator(new DecelerateInterpolator());
        fadeIn.setDuration(200);

        fadeOut = new AlphaAnimation(1, 0);
        fadeOut.setInterpolator(new AccelerateInterpolator());
        fadeOut.setDuration(200);
        setRawTextSize(radioButton, textSize, false);
        radioButton.setCompoundDrawablesWithIntrinsicBounds(
                drawableLeft, drawableTop, drawableRight, drawableBottom);

        //            radioButton.setRelativeDrawablesIfNeeded(drawableStart, drawableEnd);
        radioButton.setCompoundDrawablePadding(drawablePadding);
        radioButton.setId(getId());
        LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
        addView(radioButton, layoutParams);

        badgePointView = new TextView(context);

        setBadgePointDrawable(getContext(), badgeRadius);

        badgePointView.setText(badgetext);
/*        if (TextUtils.isEmpty(badgetext) && !isNotNeedDight()) {
            hide();
        } else {
            show();
        }*/
        if (badgetextColor != null) {
            badgePointView.setTextColor(badgetextColor);

        } else {
            badgePointView.setTextColor(Color.WHITE);
        }
        badgePointView.setEllipsize(TextUtils.TruncateAt.END);
        badgePointView.setGravity(Gravity.CENTER);
        setRawTextSize(badgePointView, badgetextSize, false);
        setMinBadgeSize(context, minBadgeSize);
//        badgePointView.maxl(dp2px(context,20));//maxLength
        if (notNeedDight) {
            setBadgePointPadding(0);
        } else {
            setBadgePointPadding(badgePadding);

        }
        badgePointView.setLines(1);
        badgePointView.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1)});
        layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        layoutParams.addRule(RelativeLayout.ALIGN_TOP, radioButton.getId());
        layoutParams.addRule(RelativeLayout.ALIGN_RIGHT, radioButton.getId());

        /*
               <item name="android:ellipsize">end</item>
        <item name="android:minWidth">20dp</item>
        <item name="android:minHeight">20dp</item>
        <item name="android:gravity">center</item>
        <item name="android:maxLength">4</item>
        <item name="android:lines">1</item>
        <item name="android:text">1</item>
        <item name="android:layout_gravity">top|right</item>
        <item name="android:paddingTop">1.5dp</item>
        <item name="android:paddingBottom">1.5dp</item>
        <item name="android:paddingLeft">1.5dp</item>
        <item name="android:paddingRight">1.5dp</item>
        <item name="android:textColor">@color/colorWhite</item>
        <item name="android:textSize">@dimen/text_size_12</item>
        <item name="android:background">@drawable/shape_circle_badge</item>
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>*/


        addView(badgePointView, layoutParams);

    }

    private void setMinBadgeSize(Context context, int px) {
        badgePointView.setMinWidth(px);
        badgePointView.setMinHeight(px);
/*        badgePointView.setMinWidth(dp2px(context, px));
        badgePointView.setMinHeight(dp2px(context, px));*/
    }

    public void setBadgePointPadding(int padding) {
        badgePointView.setPadding(padding, padding, padding, padding);
    }

    public void setBadgePointDrawable(Context context, int badgeRadius) {
        GradientDrawable gradientDrawable = new GradientDrawable();
        gradientDrawable.setShape(GradientDrawable.RECTANGLE);
        gradientDrawable.setSize(badgeRadius, badgeRadius);
        gradientDrawable.setCornerRadius(badgeRadius);
        gradientDrawable.setColor(Color.parseColor("#ef132c"));
        setBadgePointDrawable(context, gradientDrawable);
    }


    public void setBadgePointDrawable(Context context, Drawable drawable) {
        badgePointView.setBackgroundDrawable(drawable);
    }


    protected int dp2px(Context context, double dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;//density=dpi/160
        return (int) (dpValue * scale + 0.5f);

    }


    private void setRawTextSize(TextView textView, float size, boolean shouldRequestLayout) {
        if (size != textView.getPaint().getTextSize()) {
            textView.getPaint().setTextSize(size);

            if (shouldRequestLayout && textView.getLayout() != null) {
                requestLayout();
                invalidate();
            }
        }
    }

    public BadgeRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    public final void setText(CharSequence text) {
        radioButton.setText(text);
    }

    @Override
    public void setChecked(boolean checked) {
        radioButton.setChecked(checked);

       /* if (mOnCheckedChangeWidgetListener != null) {
            mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
        }*/

    }

    @Override
    public boolean isChecked() {
        return radioButton.isChecked();
    }

    @Override
    public void toggle() {

        if (!radioButton.isChecked()) {
            radioButton.toggle();
        }
    }


    @Override
    public boolean performClick() {
        toggle();

        final boolean handled = super.performClick();
        if (!handled) {
            // View only makes a sound effect if the onClickListener was
            // called, so we'll need to make one here instead.
            playSoundEffect(SoundEffectConstants.CLICK);
        }

        return handled;
    }

    @Override
    public void setOnCheckedChangeWidgetListener(CompoundButton.OnCheckedChangeListener onCheckedChangeWidgetListener) {
        RadioGroupX.setRadioButtonOnCheckedChangeWidgetListener(radioButton, onCheckedChangeWidgetListener);
    }


    /**
     * 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.
     *
     * @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 (animate) {
            badgePointView.startAnimation(anim);
        }
        badgePointView.setVisibility(View.VISIBLE);
        isShown = true;
    }

    private void hide(boolean animate, Animation anim) {
        badgePointView.setVisibility(View.GONE);
        if (animate) {
            badgePointView.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);
        }
    }

    private static Animation fadeIn;
    private static Animation fadeOut;

}

最后,还可以通过绘制添加小红点 继承RadioButton

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (notify) {

        //获取到DrawableTop,  0 drawableleft 1 drawabletop 2 drawableright 3 drawablebottom
        Drawable drawable = getCompoundDrawables()[1];

        //获取到Drawable的left right top bottom的值
        Rect bounds = drawable.getBounds();

        //这里分析: 
        //getMeasuredWidth() / 2 等于整个控件的水平位置中心点
        //bounds.width() /2 drawable宽度的一半
        //radius / 2 小圆点宽度的一半
        //由于我们在布局文件中设置了Gravity为Center,所以最后小红点的x坐标为drawable图片右边对其+radius/2的位置上
        float cx = getMeasuredWidth() / 2 + bounds.width() / 2 - radius / 2;
        //y就比较好定义了,为drawable 1/4即可
        float cy = getPaddingTop() + bounds.height() / 4;

        //float cx, float cy, float radius, @NonNull Paint paint
        //把小红点绘制上去
        canvas.drawCircle(cx, cy, radius, paint);
    }
}

另外,我打算改一下我的RadioGroupX让它支持直接findById我发现有人非常好

具体是这篇文章了

https://www.cnblogs.com/Jaylong/p/radiogroup.html

我这里踩到的坑是兼容findbyid寻找index,兼容点击图片和点击文字都可以触发 兼容双击到顶,具体可以看我另外的一篇文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容