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,兼容點擊圖片和點擊文字都可以觸發 兼容雙擊到頂,具體可以看我另外的一篇文章

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容