Android仿IOS的Segmented Control 选项卡。

时间:2022-02-04 06:28:24

参考Android仿IOS的Segmented Control 选项卡。
自定义View。

public class SwitchButton extends RadioGroup implements OnCheckedChangeListener {

    private final int[] CHECKED_STATE = { android.R.attr.state_checked }, UNCHECKED_STATE = { -android.R.attr.state_checked };

    /** 默认选项卡数量 */
    private final static int DEFAULT_SWITCH_COUNT = 2;

    private final static int EX = 5;// 余量

    private ColorStateList mTextColor;

    private int mParentWidth, mParentHeight;

    private int mRadioStyle;

    private float cornerRadius, textSize;

    private int checkedColor, unCheckedColor, strokeColor, strokeWidth;

    private CharSequence[] mTexts;

    private int switchCount;

    // 是否测量完毕
    private boolean isMeasure;

    private SparseArray<RadioButton> mRadioArrays;
    private SparseArray<Drawable> mButtonDrawables;
    private SparseArray<StateListDrawable> mStateDrawables;
    private SparseIntArray mSparseIds;

    private int mCurrentPosition;

    private OnChangeListener changeListener;

    /** * @param context */
    public SwitchButton(Context context) {
        super(context, null);
    }

    public SwitchButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs, new int[] { android.R.attr.orientation, android.R.attr.layout_height });
        setOrientation(a.getInt(0, LinearLayout.HORIZONTAL));
        mParentHeight = a.getDimensionPixelSize(1, 0);
        a.recycle();
        a = context.obtainStyledAttributes(attrs, R.styleable.switchButton);
        setTextColor(a.getColorStateList(R.styleable.switchButton_android_textColor));
        setTextArray(a.getTextArray(R.styleable.switchButton_sw_textArray));
        setSwitchCount(a.getInteger(R.styleable.switchButton_sw_switchCount, DEFAULT_SWITCH_COUNT));
        setSwitchStyle(a.getResourceId(R.styleable.switchButton_sw_ThemeStyle, 0));
        setCornerRadius(a.getDimension(R.styleable.switchButton_sw_CornerRadius, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, getResources().getDisplayMetrics())));
        setCheckedColor(a.getColor(R.styleable.switchButton_sw_checkedColor, Color.GREEN));
        setUnCheckedColor(a.getColor(R.styleable.switchButton_sw_unCheckedColor, Color.WHITE));
        setStrokeColor(a.getColor(R.styleable.switchButton_sw_strokeColor, Color.GREEN));
        setStrokeWidth((int) a.getDimension(R.styleable.switchButton_sw_strokeWidth, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, getResources().getDisplayMetrics())));
        setTextSize(a.getDimension(R.styleable.switchButton_android_textSize, 0f));
        a.recycle();
        setOnCheckedChangeListener(this);
    }

    /** * 初始化UI * * @param context */
    @SuppressWarnings("deprecation")
    @SuppressLint("NewApi")
    private void initUI(Context context) {
        if (mTexts != null && mTexts.length != switchCount) {
            throw new IllegalArgumentException("The textArray's length must equal to the switchCount");
        }
        if (mParentWidth == 0)
            return;
        ColorDrawable colorDrawable = new ColorDrawable();
        LayoutParams mParams = new LayoutParams(mParentWidth / (switchCount > 2 ? switchCount : (switchCount + 1)), mParentHeight, 1);
        for (int i = 0; i < switchCount; i++) {
            if (mRadioArrays == null)
                mRadioArrays = new SparseArray<RadioButton>();
            RadioButton mRadioButton = mRadioArrays.get(i, createRadioView());
            mRadioButton.setLayoutParams(mParams);
            mRadioButton.setButtonDrawable(mButtonDrawables != null ? mButtonDrawables.get(i, colorDrawable) : colorDrawable);
            if (Build.VERSION.SDK_INT >= 16) {
                mRadioButton.setBackground(getStateDrawable(i));
            } else {
                mRadioButton.setBackgroundDrawable(getStateDrawable(i));
            }
            mRadioButton.setText(mTexts[i]);
            if (mRadioButton.getId() < 0) {
                int id = getViewId();
                if (mSparseIds == null)
                    mSparseIds = new SparseIntArray();
                mSparseIds.put(i, id);
                mRadioButton.setId(id);
            } else {
                removeView(mRadioButton);
            }
            mRadioButton.setChecked(mCurrentPosition == i);
            addView(mRadioButton, i);
            mRadioArrays.put(i, mRadioButton);
        }
    }

    private Drawable getStateDrawable(int i) {
        if (mStateDrawables == null)
            mStateDrawables = new SparseArray<StateListDrawable>();
        StateListDrawable mStateListDrawable = mStateDrawables.size() >= i + 1 && (i != switchCount - 1 || i == switchCount - 1) ? null : mStateDrawables.get(i);
        if (mStateListDrawable == null) {
            float leftRadius = i == 0 ? cornerRadius : 0;
            float rightRadius = i == 0 ? 0 : i == switchCount - 1 ? cornerRadius : 0;
            float[] cRadius = { leftRadius, leftRadius, rightRadius, rightRadius, rightRadius, rightRadius, leftRadius, leftRadius };
            mStateListDrawable = new StateListDrawable();
            GradientDrawable cornerDrawable = new GradientDrawable();
            cornerDrawable.setColor(checkedColor);
            cornerDrawable.setCornerRadii(cRadius);
            mStateListDrawable.addState(CHECKED_STATE, cornerDrawable);
            cornerDrawable = new GradientDrawable();
            cornerDrawable.setColor(unCheckedColor);
            cornerDrawable.setStroke(strokeWidth, strokeColor);
            cornerDrawable.setCornerRadii(cRadius);
            mStateListDrawable.addState(UNCHECKED_STATE, cornerDrawable);
            mStateDrawables.put(i, mStateListDrawable);
        }
        return mStateListDrawable;
    }

    private RadioButton createRadioView() {
        RadioButton mRadioButton = new RadioButton(getContext(), null, mRadioStyle > 0 ? mRadioStyle : android.R.attr.radioButtonStyle);
        if (mRadioStyle == 0) {
            mRadioButton.setGravity(Gravity.CENTER);
            mRadioButton.setEllipsize(TruncateAt.END);
        }
        if (mTextColor != null)
            mRadioButton.setTextColor(mTextColor);
        if (textSize > 0)
            mRadioButton.setTextSize(textSize);
        return mRadioButton;
    }

    @Deprecated
    public void initialize() {
        notifyDataSetChange();
    }

    /** * 刷新数据(Button数量跟随刷新的文本数据变化) */
    public void notifyDataSetChange() {
        removeAllViews();
        switchCount = mTexts != null ? mTexts.length : switchCount;
        initUI(getContext());
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (!isMeasure) {
            initUI(getContext());
            isMeasure = !isMeasure;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mParentWidth = widthMeasureSpec - EX;
        mParentHeight = mParentHeight == 0 ? heightMeasureSpec : mParentHeight;
    }

    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        if (changeListener != null)
            changeListener.onChange(mSparseIds.indexOfValue(checkedId));
    }

    /** * 设置当前选中项 * * @param selectedPosition */
    public void setCurrentPosition(int selectedPosition) {
        if (selectedPosition >= 0 && selectedPosition <= switchCount) {
            mCurrentPosition = selectedPosition;
        }
    }

    /** * 设置选中项 * * @param selectedPosition */
    public void setCheckedPosition(int selectedPosition) {
        if (selectedPosition >= 0 && selectedPosition <= switchCount) {
            mCurrentPosition = selectedPosition;
            if (mSparseIds != null)
                check(mSparseIds.get(mSparseIds.keyAt(selectedPosition)));
        }
    }

    public void setTextColor(ColorStateList mTextColor) {
        this.mTextColor = mTextColor;
    }

    public void setSwitchStyle(int mSwitchStyle) {
        this.mRadioStyle = mSwitchStyle;
    }

    public void setTextArray(CharSequence[] mTexts) {
        this.mTexts = mTexts;
    }

    public int getSwitchCount() {
        return switchCount;
    }

    public void setParentWidth(int mParentWidth) {
        this.mParentWidth = mParentWidth;
    }

    public void setParentHeight(int mParentHeight) {
        this.mParentHeight = mParentHeight;
    }

    public void setSwitchCount(int switchCount) {
        this.switchCount = switchCount < 2 ? DEFAULT_SWITCH_COUNT : switchCount;
        if (mButtonDrawables == null)
            mButtonDrawables = new SparseArray<Drawable>();
    }

    public void setCornerRadius(float cornerRadius) {
        this.cornerRadius = cornerRadius;
    }

    public void setCheckedColor(int checkedColor) {
        this.checkedColor = checkedColor;
    }

    public void setUnCheckedColor(int unCheckedColor) {
        this.unCheckedColor = unCheckedColor;
    }

    public void setStrokeColor(int strokeColor) {
        this.strokeColor = strokeColor;
    }

    public void setStrokeWidth(int strokeWidth) {
        this.strokeWidth = strokeWidth;
    }

    public void setTextSize(float textSize) {
        this.textSize = textSize;
    }

    public void setSwitchButton(int position, int mDrawableResId) {
        mButtonDrawables.put(position, getResources().getDrawable(mDrawableResId));
    }

    public void setOnChangeListener(OnChangeListener eventListener) {
        this.changeListener = eventListener;
    }

    public interface OnChangeListener {
        public void onChange(int position);
    }

    private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);

    public int getViewId() {
        for (;;) {
            final int result = sNextGeneratedId.get();
            // aapt-generated IDs have the high byte nonzero; clamp to the range
            // under that.
            int newValue = result + 1;
            if (newValue > 0x00FFFFFF)
                newValue = 1; // Roll over to 1, not 0.
            if (sNextGeneratedId.compareAndSet(result, newValue)) {
                return result;
            }
        }
    }
}

values下创建array.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="test">
        <item></item>
        <item></item>
    </string-array>
</resources>

arrts.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="switchButton">
        <attr name="android:textColor" />
        <attr name="android:textSize" />
        <attr name="sw_textArray" format="string" />
        <attr name="sw_switchCount" format="integer" />
        <attr name="sw_ThemeStyle" format="reference" />
        <attr name="sw_CornerRadius" format="dimension" />
        <attr name="sw_checkedColor" format="color|reference" />
        <attr name="sw_unCheckedColor" format="color|reference" />
        <attr name="sw_strokeColor" format="color|reference" />
        <attr name="sw_strokeWidth" format="dimension" />
    </declare-styleable>

</resources>

dimens.xml

<resources>

    <dimen name="icon_arrow_width">16dp</dimen>
    <dimen name="body_margin">10dp</dimen>
    <dimen name="twenty">20dp</dimen>
</resources>

drawable.xml 的 switch_textcolor_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- title sitch -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="false" android:color="@color/colorPrimary"/>
    <item android:state_checked="true" android:color="@color/white"/>
</selector>
  mSwitchButton = (SwitchButton) findViewById(R.id.switchButton);
        mSwitchButton.setOnChangeListener(new SwitchButton.OnChangeListener() {

            @Override
            public void onChange(int position) {

                if (position==0){
                    FragmentManager fm = getFragmentManager();
                    final FragmentTransaction ft = fm.beginTransaction();
                    fragment1 = new Fragment1();
                    ft.replace(android.R.id.content, fragment1);
                    ft.commit();
                }
                else {
                    FragmentManager fm = getFragmentManager();
                    final FragmentTransaction ft = fm.beginTransaction();
                    fragment2 = new Fragment2();
                    ft.replace(android.R.id.content, fragment2);
                    ft.commit();
                }

            }
        });

效果
Android仿IOS的Segmented Control 选项卡。