android自定义view实现开关

下面是一个自定义view实现的开关,方法使用并且附加监听

1、自定义view代码

package com.android.sf.view;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import com.android.sf.view.util.SimpleSpringListener;
import com.android.sf.view.util.Spring;
import com.android.sf.view.util.SpringConfig;
import com.android.sf.view.util.SpringSystem;
import com.android.sf.view.util.SpringUtil;
import com.android.sf.R;
/**
 * 自定义开关
 * @author ThinkPad
 */
public class MyToggleButton extends View {
    private SpringSystem springSystem;
    private Spring spring;
    /** */
    private float radius;
    /**
     * 开启颜色
     */
    private int onColor = Color.parseColor("#4ebb7f");
    /**
     * 关闭颜色
     */
    private int offBorderColor = Color.parseColor("#dadbda");
    /**
     * 灰色带颜色
     */
    private int offColor = Color.parseColor("#ffffff");
    /**
     * 手柄颜色
     */
    private int spotColor = Color.parseColor("#ffffff");
    /**
     * 边框颜色
     */
    private int borderColor = offBorderColor;
    /**
     * 画笔
     */
    private Paint paint;
    /**
     * 开关状态
     */
    private boolean toggleOn = false;
    /**
     * 边框大小
     */
    private int borderWidth = 2;
    /**
     * 垂直中心
     */
    private float centerY;
    /**
     * 按钮的开始和结束位置
     */
    private float startX, endX;
    /**
     * 手柄X位置的最小和最大值
     */
    private float spotMinX, spotMaxX;
    /**
     * 手柄大小
     */
    private int spotSize;
    /**
     * 手柄X位置
     */
    private float spotX;
    /**
     * 关闭时内部灰色带高度
     */
    private float offLineWidth;
    /** */
    private RectF rect = new RectF();
    /**
     * 默认使用动画
     */
    private boolean defaultAnimate = true;

    /**
     * 是否默认处于打开状态
     */
    private boolean isDefaultOn = false;

    private OnToggleChanged listener;

    private MyToggleButton(Context context) {
        super(context);
    }

    public MyToggleButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setup(attrs);
    }

    public MyToggleButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        setup(attrs);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        spring.removeListener(springListener);
    }

    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        spring.addListener(springListener);
    }

    public void setup(AttributeSet attrs) {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Style.FILL);
        paint.setStrokeCap(Cap.ROUND);

        springSystem = SpringSystem.create();
        spring = springSystem.createSpring();
        spring.setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(50, 7));

        this.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View arg0) {
                toggle(defaultAnimate);
            }
        });

        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MyToggleButton);
        offBorderColor = typedArray.getColor(R.styleable.MyToggleButton_offBorderColor2, offBorderColor);
        onColor = typedArray.getColor(R.styleable.MyToggleButton_onColor2, onColor);
        spotColor = typedArray.getColor(R.styleable.MyToggleButton_spotColor2, spotColor);
        offColor = typedArray.getColor(R.styleable.MyToggleButton_offColor2, offColor);
        borderWidth = typedArray.getDimensionPixelSize(R.styleable.MyToggleButton_borderWidth2, borderWidth);
        defaultAnimate = typedArray.getBoolean(R.styleable.MyToggleButton_animate2, defaultAnimate);
        isDefaultOn = typedArray.getBoolean(R.styleable.MyToggleButton_isDefaultOn2, isDefaultOn);
        typedArray.recycle();
        borderColor = offBorderColor;

        if (isDefaultOn) {
            toggleOn();
        }
    }

    public void toggle() {
        toggle(true);
    }

    public void toggle(boolean animate) {
        toggleOn = !toggleOn;
        takeEffect(animate);

        if (listener != null) {
            listener.onToggle(toggleOn);
        }
    }

    public void toggleOn() {
        setToggleOn();
        if (listener != null) {
            listener.onToggle(toggleOn);
        }
    }

    public void toggleOff() {
        setToggleOff();
        if (listener != null) {
            listener.onToggle(toggleOn);
        }
    }

    /**
     * 设置显示成打开样式,不会触发toggle事件
     */
    public void setToggleOn() {
        setToggleOn(true);
    }

    /**
     * @param animate
     */
    public void setToggleOn(boolean animate) {
        toggleOn = true;
        takeEffect(animate);
    }

    /**
     * 设置显示成关闭样式,不会触发toggle事件
     */
    public void setToggleOff() {
        setToggleOff(true);
    }

    public void setToggleOff(boolean animate) {
        toggleOn = false;
        takeEffect(animate);
    }

    private void takeEffect(boolean animate) {
        if (animate) {
            spring.setEndValue(toggleOn ? 1 : 0);
        } else {
            //这里没有调用spring,所以spring里的当前值没有变更,这里要设置一下,同步两边的当前值
            spring.setCurrentValue(toggleOn ? 1 : 0);
            calculateEffect(toggleOn ? 1 : 0);
        }
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        Resources r = Resources.getSystem();
        if (widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST) {
            widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, r.getDisplayMetrics());
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
        }

        if (heightMode == MeasureSpec.UNSPECIFIED || heightSize == MeasureSpec.AT_MOST) {
            heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, r.getDisplayMetrics());
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
        }


        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }


    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
                            int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        final int width = getWidth();
        final int height = getHeight();

        radius = Math.min(width, height) * 0.5f;
        centerY = radius;
        startX = radius;
        endX = width - radius;
        spotMinX = startX + borderWidth;
        spotMaxX = endX - borderWidth;
        spotSize = height - 4 * borderWidth;
        spotX = toggleOn ? spotMaxX : spotMinX;
        offLineWidth = 0;
    }


    SimpleSpringListener springListener = new SimpleSpringListener() {
        @Override
        public void onSpringUpdate(Spring spring) {
            final double value = spring.getCurrentValue();
            calculateEffect(value);
        }
    };

    private int clamp(int value, int low, int high) {
        return Math.min(Math.max(value, low), high);
    }


    @Override
    public void draw(Canvas canvas) {
        //
        super.draw(canvas);
        rect.set(0, 0, getWidth(), getHeight());
        paint.setColor(borderColor);
        canvas.drawRoundRect(rect, radius, radius, paint);

        if (offLineWidth > 0) {
            final float cy = offLineWidth * 0.5f;
            rect.set(spotX - cy, centerY - cy, endX + cy, centerY + cy);
            paint.setColor(offColor);
            canvas.drawRoundRect(rect, cy, cy, paint);
        }

        rect.set(spotX - 1 - radius, centerY - radius, spotX + 1.1f + radius, centerY + radius);
        paint.setColor(borderColor);
        canvas.drawRoundRect(rect, radius, radius, paint);

        final float spotR = spotSize * 0.5f;
        rect.set(spotX - spotR, centerY - spotR, spotX + spotR, centerY + spotR);
        paint.setColor(spotColor);
        canvas.drawRoundRect(rect, spotR, spotR, paint);

    }

    /**
     * @param value
     */
    private void calculateEffect(final double value) {
        final float mapToggleX = (float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, spotMinX, spotMaxX);
        spotX = mapToggleX;

        float mapOffLineWidth = (float) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, 10, spotSize);

        offLineWidth = mapOffLineWidth;

        final int fb = Color.blue(onColor);
        final int fr = Color.red(onColor);
        final int fg = Color.green(onColor);

        final int tb = Color.blue(offBorderColor);
        final int tr = Color.red(offBorderColor);
        final int tg = Color.green(offBorderColor);

        int sb = (int) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, fb, tb);
        int sr = (int) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, fr, tr);
        int sg = (int) SpringUtil.mapValueFromRangeToRange(1 - value, 0, 1, fg, tg);

        sb = clamp(sb, 0, 255);
        sr = clamp(sr, 0, 255);
        sg = clamp(sg, 0, 255);

        borderColor = Color.rgb(sr, sg, sb);

        postInvalidate();
    }

    /**
     * @author ThinkPad
     */
    public interface OnToggleChanged {
        /**
         * @param on
         */
        public void onToggle(boolean on);
    }


    public void setOnToggleChanged(OnToggleChanged onToggleChanged) {
        listener = onToggleChanged;
    }

    public boolean isAnimate() {
        return defaultAnimate;
    }

    public void setAnimate(boolean animate) {
        this.defaultAnimate = animate;
    }

}

下面是几个相关的文件
1、SimpleSpringListener

package com.android.sf.view.util;

public class SimpleSpringListener implements SpringListener {
    @Override
    public void onSpringUpdate(Spring spring) {
    }

    @Override
    public void onSpringAtRest(Spring spring) {
    }

    @Override
    public void onSpringActivate(Spring spring) {
    }

    @Override
    public void onSpringEndStateChange(Spring spring) {
    }
}

2、Spring
package com.wisdomparents.moocsapp.view.rebound;

import java.util.concurrent.CopyOnWriteArraySet;


public class Spring {

    // unique incrementer id for springs
    private static int ID = 0;

    // maximum amount of time to simulate per physics iteration in seconds (4 frames at 60 FPS)
    private static final double MAX_DELTA_TIME_SEC = 0.064;
    // fixed timestep to use in the physics solver in seconds
    private static final double SOLVER_TIMESTEP_SEC = 0.001;
    private SpringConfig mSpringConfig;
    private boolean mOvershootClampingEnabled;

    // storage for the current and prior physics state while integration is occurring
    private static class PhysicsState {
        double position;
        double velocity;
    }

    // unique id for the spring in the system
    private final String mId;
    // all physics simulation objects are final and reused in each processing pass
    private final PhysicsState mCurrentState = new PhysicsState();
    private final PhysicsState mPreviousState = new PhysicsState();
    private final PhysicsState mTempState = new PhysicsState();
    private double mStartValue;
    private double mEndValue;
    private boolean mWasAtRest = true;
    // thresholds for determining when the spring is at rest
    private double mRestSpeedThreshold = 0.005;
    private double mDisplacementFromRestThreshold = 0.005;
    private CopyOnWriteArraySet<SpringListener> mListeners = new CopyOnWriteArraySet<SpringListener>();
    private double mTimeAccumulator = 0;

    private final BaseSpringSystem mSpringSystem;

    /**
     * create a new spring
     */
    Spring(BaseSpringSystem springSystem) {
        if (springSystem == null) {
            throw new IllegalArgumentException("Spring cannot be created outside of a BaseSpringSystem");
        }
        mSpringSystem = springSystem;
        mId = "spring:" + ID++;
        setSpringConfig(SpringConfig.defaultConfig);
    }

    /**
     * Destroys this Spring, meaning that it will be deregistered from its BaseSpringSystem so it won't be
     * iterated anymore and will clear its set of listeners. Do not use the Spring after calling this,
     * doing so may just cause an exception to be thrown.
     */
    public void destroy() {
        mListeners.clear();
        mSpringSystem.deregisterSpring(this);
    }

    /**
     * get the unique id for this spring
     *
     * @return the unique id
     */
    public String getId() {
        return mId;
    }

    /**
     * set the config class
     *
     * @param springConfig config class for the spring
     * @return this Spring instance for chaining
     */
    public Spring setSpringConfig(SpringConfig springConfig) {
        if (springConfig == null) {
            throw new IllegalArgumentException("springConfig is required");
        }
        mSpringConfig = springConfig;
        return this;
    }

    /**
     * retrieve the spring config for this spring
     *
     * @return the SpringConfig applied to this spring
     */
    public SpringConfig getSpringConfig() {
        return mSpringConfig;
    }

    /**
     * Set the displaced value to determine the displacement for the spring from the rest value.
     * This value is retained and used to calculate the displacement ratio.
     * This also updates the start value of the Spring.
     *
     * @param currentValue the new start and current value for the spring
     * @return the spring for chaining
     */
    public Spring setCurrentValue(double currentValue) {
        mStartValue = currentValue;
        mCurrentState.position = currentValue;
        mSpringSystem.activateSpring(this.getId());
        for (SpringListener listener : mListeners) {
            listener.onSpringUpdate(this);
        }
        return this;
    }

    /**
     * Get the displacement value from the last time setCurrentValue was called.
     *
     * @return displacement value
     */
    public double getStartValue() {
        return mStartValue;
    }

    /**
     * Get the current
     *
     * @return current value
     */
    public double getCurrentValue() {
        return mCurrentState.position;
    }

    /**
     * get the displacement of the springs current value from its rest value.
     *
     * @return the distance displaced by
     */
    public double getCurrentDisplacementDistance() {
        return getDisplacementDistanceForState(mCurrentState);
    }

    /**
     * get the displacement from rest for a given physics state
     *
     * @param state the state to measure from
     * @return the distance displaced by
     */
    private double getDisplacementDistanceForState(PhysicsState state) {
        return Math.abs(mEndValue - state.position);
    }

    /**
     * set the rest value to determine the displacement for the spring
     *
     * @param endValue the endValue for the spring
     * @return the spring for chaining
     */
    public Spring setEndValue(double endValue) {
        if (mEndValue == endValue && isAtRest()) {
            return this;
        }
        mStartValue = getCurrentValue();
        mEndValue = endValue;
        mSpringSystem.activateSpring(this.getId());
        for (SpringListener listener : mListeners) {
            listener.onSpringEndStateChange(this);
        }
        return this;
    }

    /**
     * get the rest value used for determining the displacement of the spring
     *
     * @return the rest value for the spring
     */
    public double getEndValue() {
        return mEndValue;
    }

    /**
     * set the velocity on the spring in pixels per second
     *
     * @return the spring for chaining
     */
    public Spring setVelocity(double velocity) {
        mCurrentState.velocity = velocity;
        mSpringSystem.activateSpring(this.getId());
        return this;
    }

    /**
     * get the velocity of the spring
     *
     * @return the current velocity
     */
    public double getVelocity() {
        return mCurrentState.velocity;
    }

    /**
     * Sets the speed at which the spring should be considered at rest.
     *
     * @param restSpeedThreshold speed pixels per second
     * @return the spring for chaining
     */
    public Spring setRestSpeedThreshold(double restSpeedThreshold) {
        mRestSpeedThreshold = restSpeedThreshold;
        return this;
    }

    /**
     * Returns the speed at which the spring should be considered at rest in pixels per second
     *
     * @return speed in pixels per second
     */
    public double getRestSpeedThreshold() {
        return mRestSpeedThreshold;
    }

    /**
     * set the threshold of displacement from rest below which the spring should be considered at rest
     *
     * @param displacementFromRestThreshold displacement to consider resting below
     * @return the spring for chaining
     */
    public Spring setRestDisplacementThreshold(double displacementFromRestThreshold) {
        mDisplacementFromRestThreshold = displacementFromRestThreshold;
        return this;
    }

    /**
     * get the threshold of displacement from rest below which the spring should be considered at rest
     *
     * @return displacement to consider resting below
     */
    public double getRestDisplacementThreshold() {
        return mDisplacementFromRestThreshold;
    }

    /**
     * Force the spring to clamp at its end value to avoid overshooting the target value.
     *
     * @param overshootClampingEnabled whether or not to enable overshoot clamping
     * @return the spring for chaining
     */
    public Spring setOvershootClampingEnabled(boolean overshootClampingEnabled) {
        mOvershootClampingEnabled = overshootClampingEnabled;
        return this;
    }

    /**
     * Check if overshoot clamping is enabled.
     *
     * @return is overshoot clamping enabled
     */
    public boolean isOvershootClampingEnabled() {
        return mOvershootClampingEnabled;
    }

    /**
     * Check if the spring is overshooting beyond its target.
     *
     * @return true if the spring is overshooting its target
     */
    public boolean isOvershooting() {
        return (mStartValue < mEndValue && getCurrentValue() > mEndValue) ||
                (mStartValue > mEndValue && getCurrentValue() < mEndValue);
    }

    /**
     * advance the physics simulation in SOLVER_TIMESTEP_SEC sized chunks to fulfill the required
     * realTimeDelta.
     * The math is inlined inside the loop since it made a huge performance impact when there are
     * several springs being advanced.
     *
     * @param realDeltaTime clock drift
     */
    void advance(double realDeltaTime) {

        boolean isAtRest = isAtRest();

        if (isAtRest && mWasAtRest) {
      /* begin debug
      Log.d(TAG, "bailing out because we are at rest:" + getName());
      end debug */
            return;
        }

        // clamp the amount of realTime to simulate to avoid stuttering in the UI. We should be able
        // to catch up in a subsequent advance if necessary.
        double adjustedDeltaTime = realDeltaTime;
        if (realDeltaTime > MAX_DELTA_TIME_SEC) {
            adjustedDeltaTime = MAX_DELTA_TIME_SEC;
        }

    /* begin debug
    long startTime = System.currentTimeMillis();
    int iterations = 0;
    end debug */

        mTimeAccumulator += adjustedDeltaTime;

        double tension = mSpringConfig.tension;
        double friction = mSpringConfig.friction;

        double position = mCurrentState.position;
        double velocity = mCurrentState.velocity;
        double tempPosition = mTempState.position;
        double tempVelocity = mTempState.velocity;

        double aVelocity, aAcceleration;
        double bVelocity, bAcceleration;
        double cVelocity, cAcceleration;
        double dVelocity, dAcceleration;

        double dxdt, dvdt;

        // iterate over the true time
        while (mTimeAccumulator >= SOLVER_TIMESTEP_SEC) {
      /* begin debug
      iterations++;
      end debug */
            mTimeAccumulator -= SOLVER_TIMESTEP_SEC;

            if (mTimeAccumulator < SOLVER_TIMESTEP_SEC) {
                // This will be the last iteration. Remember the previous state in case we need to
                // interpolate
                mPreviousState.position = position;
                mPreviousState.velocity = velocity;
            }

            // Perform an RK4 integration to provide better detection of the acceleration curve via
            // sampling of Euler integrations at 4 intervals feeding each derivative into the calculation
            // of the next and taking a weighted sum of the 4 derivatives as the final output.

            // This math was inlined since it made for big performance improvements when advancing several
            // springs in one pass of the BaseSpringSystem.

            // The initial derivative is based on the current velocity and the calculated acceleration
            aVelocity = velocity;
            aAcceleration = (tension * (mEndValue - tempPosition)) - friction * velocity;

            // Calculate the next derivatives starting with the last derivative and integrating over the
            // timestep
            tempPosition = position + aVelocity * SOLVER_TIMESTEP_SEC * 0.5;
            tempVelocity = velocity + aAcceleration * SOLVER_TIMESTEP_SEC * 0.5;
            bVelocity = tempVelocity;
            bAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;

            tempPosition = position + bVelocity * SOLVER_TIMESTEP_SEC * 0.5;
            tempVelocity = velocity + bAcceleration * SOLVER_TIMESTEP_SEC * 0.5;
            cVelocity = tempVelocity;
            cAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;

            tempPosition = position + cVelocity * SOLVER_TIMESTEP_SEC;
            tempVelocity = velocity + cAcceleration * SOLVER_TIMESTEP_SEC;
            dVelocity = tempVelocity;
            dAcceleration = (tension * (mEndValue - tempPosition)) - friction * tempVelocity;

            // Take the weighted sum of the 4 derivatives as the final output.
            dxdt = 1.0 / 6.0 * (aVelocity + 2.0 * (bVelocity + cVelocity) + dVelocity);
            dvdt = 1.0 / 6.0 * (aAcceleration + 2.0 * (bAcceleration + cAcceleration) + dAcceleration);

            position += dxdt * SOLVER_TIMESTEP_SEC;
            velocity += dvdt * SOLVER_TIMESTEP_SEC;
        }

        mTempState.position = tempPosition;
        mTempState.velocity = tempVelocity;

        mCurrentState.position = position;
        mCurrentState.velocity = velocity;

        if (mTimeAccumulator > 0) {
            interpolate(mTimeAccumulator / SOLVER_TIMESTEP_SEC);
        }

        // End the spring immediately if it is overshooting and overshoot clamping is enabled.
        // Also make sure that if the spring was considered within a resting threshold that it's now
        // snapped to its end value.
        if (isAtRest() || (mOvershootClampingEnabled && isOvershooting())) {
            // Don't call setCurrentValue because that forces a call to onSpringUpdate
            mStartValue = mEndValue;
            mCurrentState.position = mEndValue;
            setVelocity(0);
            isAtRest = true;
        }

    /* begin debug
    long endTime = System.currentTimeMillis();
    long elapsedMillis = endTime - startTime;
    Log.d(TAG,
        "iterations:" + iterations +
            " iterationTime:" + elapsedMillis +
            " position:" + mCurrentState.position +
            " velocity:" + mCurrentState.velocity +
            " realDeltaTime:" + realDeltaTime +
            " adjustedDeltaTime:" + adjustedDeltaTime +
            " isAtRest:" + isAtRest +
            " wasAtRest:" + mWasAtRest);
    end debug */

        // NB: do these checks outside the loop so all listeners are properly notified of the state
        //     transition
        boolean notifyActivate = false;
        if (mWasAtRest) {
            mWasAtRest = false;
            notifyActivate = true;
        }
        boolean notifyAtRest = false;
        if (isAtRest) {
            mWasAtRest = true;
            notifyAtRest = true;
        }
        for (SpringListener listener : mListeners) {
            // starting to move
            if (notifyActivate) {
                listener.onSpringActivate(this);
            }

            // updated
            listener.onSpringUpdate(this);

            // coming to rest
            if (notifyAtRest) {
                listener.onSpringAtRest(this);
            }
        }
    }

    /**
     * Check if this spring should be advanced by the system.  * The rule is if the spring is
     * currently at rest and it was at rest in the previous advance, the system can skip this spring
     *
     * @return should the system process this spring
     */
    public boolean systemShouldAdvance() {
        return !isAtRest() || !wasAtRest();
    }

    /**
     * Check if the spring was at rest in the prior iteration. This is used for ensuring the ending
     * callbacks are fired as the spring comes to a rest.
     *
     * @return true if the spring was at rest in the prior iteration
     */
    public boolean wasAtRest() {
        return mWasAtRest;
    }

    /**
     * check if the current state is at rest
     *
     * @return is the spring at rest
     */
    public boolean isAtRest() {
        return Math.abs(mCurrentState.velocity) <= mRestSpeedThreshold &&
                getDisplacementDistanceForState(mCurrentState) <= mDisplacementFromRestThreshold;
    }

    /**
     * Set the spring to be at rest by making its end value equal to its current value and setting
     * velocity to 0.
     */
    public Spring setAtRest() {
        mEndValue = mCurrentState.position;
        mTempState.position = mCurrentState.position;
        mCurrentState.velocity = 0;
        return this;
    }

    /**
     * linear interpolation between the previous and current physics state based on the amount of
     * timestep remaining after processing the rendering delta time in timestep sized chunks.
     *
     * @param alpha from 0 to 1, where 0 is the previous state, 1 is the current state
     */
    private void interpolate(double alpha) {
        mCurrentState.position = mCurrentState.position * alpha + mPreviousState.position * (1 - alpha);
        mCurrentState.velocity = mCurrentState.velocity * alpha + mPreviousState.velocity * (1 - alpha);
    }

    /** listeners **/

    /**
     * add a listener
     *
     * @param newListener to add
     * @return the spring for chaining
     */
    public Spring addListener(SpringListener newListener) {
        if (newListener == null) {
            throw new IllegalArgumentException("newListener is required");
        }
        mListeners.add(newListener);
        return this;
    }

    /**
     * remove a listener
     *
     * @param listenerToRemove to remove
     * @return the spring for chaining
     */
    public Spring removeListener(SpringListener listenerToRemove) {
        if (listenerToRemove == null) {
            throw new IllegalArgumentException("listenerToRemove is required");
        }
        mListeners.remove(listenerToRemove);
        return this;
    }

    /**
     * remove all of the listeners
     *
     * @return the spring for chaining
     */
    public Spring removeAllListeners() {
        mListeners.clear();
        return this;
    }

    /**
     * This method checks to see that the current spring displacement value is equal to the input,
     * accounting for the spring's rest displacement threshold.
     *
     * @param value The value to compare the spring value to
     * @return Whether the displacement value from the spring is within the bounds of the compare
     * value, accounting for threshold
     */
    public boolean currentValueIsApproximately(double value) {
        return Math.abs(getCurrentValue() - value) <= getRestDisplacementThreshold();
    }

}
3、SpringConfig
package com.android.sf.view.util;

/**
 * Data structure for storing spring configuration.
 */
public class SpringConfig {
    public double friction;
    public double tension;

    public static SpringConfig defaultConfig = SpringConfig.fromOrigamiTensionAndFriction(40, 7);

    /**
     * constructor for the SpringConfig
     *
     * @param tension  tension value for the SpringConfig
     * @param friction friction value for the SpringConfig
     */
    public SpringConfig(double tension, double friction) {
        this.tension = tension;
        this.friction = friction;
    }

    /**
     * A helper to make creating a SpringConfig easier with values mapping to the Origami values.
     *
     * @param qcTension  tension as defined in the Quartz Composition
     * @param qcFriction friction as defined in the Quartz Composition
     * @return a SpringConfig that maps to these values
     */
    public static SpringConfig fromOrigamiTensionAndFriction(double qcTension, double qcFriction) {
        return new SpringConfig(
                OrigamiValueConverter.tensionFromOrigamiValue(qcTension),
                OrigamiValueConverter.frictionFromOrigamiValue(qcFriction)
        );
    }
}

4、SpringSystem
package com.wisdomparents.moocsapp.view.rebound;

public class SpringSystem extends BaseSpringSystem {

    /**
     * Create a new SpringSystem providing the appropriate constructor parameters to work properly
     * in an Android environment.
     *
     * @return the SpringSystem
     */
    public static SpringSystem create() {
        return new SpringSystem(AndroidSpringLooperFactory.createSpringLooper());
    }

    private SpringSystem(SpringLooper springLooper) {
        super(springLooper);
    }

}
5、SpringUtil
package com.android.sf.view.util;

public class SpringUtil {

    /**
     * Map a value within a given range to another range.
     *
     * @param value    the value to map
     * @param fromLow  the low end of the range the value is within
     * @param fromHigh the high end of the range the value is within
     * @param toLow    the low end of the range to map to
     * @param toHigh   the high end of the range to map to
     * @return the mapped value
     */
    public static double mapValueFromRangeToRange(
            double value,
            double fromLow,
            double fromHigh,
            double toLow,
            double toHigh) {
        double fromRangeSize = fromHigh - fromLow;
        double toRangeSize = toHigh - toLow;
        double valueScale = (value - fromLow) / fromRangeSize;
        return toLow + (valueScale * toRangeSize);
    }

    /**
     * Clamp a value to be within the provided range.
     *
     * @param value the value to clamp
     * @param low   the low end of the range
     * @param high  the high end of the range
     * @return the clamped value
     */
    public static double clamp(double value, double low, double high) {
        return Math.min(Math.max(value, low), high);
    }

    public static int clamp(int value, int low, int high) {
        return Math.min(Math.max(value, low), high);
    }

}

6、还需要在values文件夹下创建一个文件attrs.xml,里面代码如下
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyToggleButton">
        <attr name="borderWidth2" format="dimension"></attr>
        <attr name="offBorderColor2" format="reference|color"></attr>
        <attr name="offColor2" format="reference|color"></attr>
        <attr name="onColor2" format="reference|color"></attr>
        <attr name="spotColor2" format="reference|color"></attr>
        <attr name="animate2" format="reference|boolean"></attr>
        <attr name="isDefaultOn2" format="reference|boolean"></attr>
    </declare-styleable>
</resources>

以上就是自定义view实现开关的全部代码,下面是使用方法
1、布局中直接使用自定义控件,可以设置开启颜色关闭颜色之类的属性
<com.android.sf.view.MyToggleButton
    android:id="@+id/pushMTB"
    android:layout_width="50dp"
    app:onColor2="@color/appthemecolor"
    android:visibility="gone"
    app:offColor2="#ffffff"
    app:spotColor2="#ffffff"
    app:offBorderColor2="#d8d8d8"
    app:borderWidth2="1px"
    android:layout_gravity="center_vertical"
    android:layout_height="30dp"
    android:layout_margin="10dp" />
2、class文件中直接寻找控件,附加监听,可以在监听中获取到开关的状态
xxxxxx.setOnToggleChanged(new MyToggleButton.OnToggleChanged() {
    @Override
    public void onToggle(boolean on) {
        if (on) {
            Ruwei = "1";
        } else {
            Ruwei = "0";
        }
    }
});

以上就是自定义view实现开关的全部代码,使用起来简单方便

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值