下面是一个自定义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 CopyOnWriteArraySetmListeners = new CopyOnWriteArraySet (); 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) ); } }
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实现开关的全部代码,使用起来简单方便