view函数_自定义 View,切换按钮

67c4757e06078486c4a64c39086fe0a2.png

自定义 View 的步骤

今天来介绍下如何自定义类似 iOS 的按钮。

fa848eceec389713101d73cc9f16b4eb.png

要想自定义 View,需要了解自定义 View 的相关步骤,一般来说自定义 View 需要实现以下操作:

337807da49d9c9c7d5f2cc0dc97341a7.png

自定义切换按钮

1.自定义属性

根据自定义 View 中需要用户设置的属性,来构造自定义属性,直接在 xml 资源文件中的resources标签下声明:

<declare-styleable name="Switch">
    <attr name="checked" format="reference|boolean"/>
    <attr name="switchOnColor" format="reference|color"/>
    <attr name="switchOffColor" format="reference|color"/>
    <attr name="spotOnColor" format="reference|color"/>
    <attr name="spotOffColor" format="reference|color"/>
    <attr name="spotPadding" format="reference|dimension"/>
    <attr name="duration" format="reference|integer" />
</declare-styleable>

并在继承 View 类的Switch类中声明全局变量:

private int switchOnColor;
private int switchOffColor;
private int spotOnColor;
private int spotOffColor;
private int spotPadding;
private boolean mChecked;
private int duration;

2.初始化构造函数

一般来说,如果自定义View用于在布局文件声明,则必须初始化覆盖基类的两个参数的构造函数;其他带参构造函数,根据需求进行初始化。

在构造函数中通过obtainStyledAttributes()方法获取用户设置的属性值:

public Switch(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Switch);
        switchOnColor = a.getColor(R.styleable.Switch_switchOnColor, DEFAULT_SWITCH_ON_COLOR);
        switchOffColor = a.getColor(R.styleable.Switch_switchOffColor, DEFAULT_SWITCH_OFF_COLOR);
        spotOnColor = a.getColor(R.styleable.Switch_spotOnColor, DEFAULT_SPOT_ON_COLOR);
        spotOffColor = a.getColor(R.styleable.Switch_spotOffColor, DEFAULT_SPOT_OFF_COLOR);
        spotPadding = a.getDimensionPixelSize(R.styleable.Switch_spotPadding, dp2px(DEFAULT_SPOT_PADDING));
        duration = a.getInteger(R.styleable.Switch_duration, ANIMATION_DURATION);
        mChecked = a.getBoolean(R.styleable.Switch_checked, false);
        a.recycle();

        state = mChecked ? State.SWITCH_ON : State.SWITCH_OFF;
        setClickable(true);
}

3.测量 View——onMeasure

onMeasure方法是决定自定义View布局大小的关键,可通过MeasureSpec获取父布局分配给子布局的大小及布局模式。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

    int width = dp2px(DEFAULT_WIDTH) + getPaddingLeft() + getPaddingRight();
    int height = dp2px(DEFAULT_HEIGHT) + getPaddingTop() + getPaddingBottom();

    if (widthSpecMode != MeasureSpec.AT_MOST) {
        width = Math.max(width, widthSpecSize);
    }

    if (heightSpecMode != MeasureSpec.AT_MOST) {
        height = Math.max(height, heightSpecSize);
    }

    setMeasuredDimension(width, height);
}

4.给定 View 高宽——setMeasuredDimension

通过setMeasuredDimension方法设置自定义布局想要的View大小。代码如上所示。

5.绘制 View——onDraw

最核心的方法莫过于onDraw了,onDraw是绘制View的关键,结合ValueAnimatorObjectAnimator等可实现View的动画效果。

private void animateToCheckedState() {
    valueAnimator = ValueAnimator.ofFloat(0, 1);
    valueAnimator.setDuration(duration);
    valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            currentPos = (float) animation.getAnimatedValue();
            invalidate();
        }
    });

    valueAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            isMoving = true;
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            isMoving = false;
        }
    });

    if (!valueAnimator.isRunning()) {
        valueAnimator.start();
        currentPos = 0;
    }
}

6.回调监听

切换按钮的回调,主要体现在开关状态的变化:

public interface OnCheckedChangeListener {
    /**
     * Called when the checked state of a switch has changed.
     *
     * @param s The switch whose state has changed.
     * @param isChecked The new checked state of switch.
     */
    void onCheckedChanged(Switch s, boolean isChecked);
}

7.手势处理

本项目中并未使用 onTouchEvent 方法处理手势监听,而是覆盖了父类的performClick方法,同时保证了OnClickListener的生效。

@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;
}
github​github.com 知我者乎​www.zhihu.com
0747c55c1c4e413ebba91aebb1bb00f3.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值