diff --git a/app/build.gradle b/app/build.gradle index 251d795..ae97acd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,9 +63,11 @@ android { lintOptions { checkReleaseBuilds false } + dexOptions { jumboMode true } + //多版本 productFlavors { official { @@ -83,11 +85,12 @@ android { buildConfigField "String", "test", '"测试文本"' } } + sourceSets { beta.res.srcDirs = ['src/beta/res'] } -//签名 + //签名 signingConfigs { debug { storeFile file("src/jks/tt.jks") @@ -164,7 +167,7 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) - + implementation project(':viewlibrary') implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0' diff --git a/app/src/main/java/com/tt/activity/JMainActivity.java b/app/src/main/java/com/tt/activity/JMainActivity.java index 15ad5ed..f346b6f 100644 --- a/app/src/main/java/com/tt/activity/JMainActivity.java +++ b/app/src/main/java/com/tt/activity/JMainActivity.java @@ -1,6 +1,13 @@ package com.tt.activity; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Intent; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; import android.view.KeyEvent; +import android.view.View; import android.widget.TextView; import android.widget.Toast; @@ -32,6 +39,12 @@ public class JMainActivity extends BaseActivity { protected void initData() { String ndks = NDKTools.getStringFromNDK(); textView.setText(ndks); + textView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + open(); + } + }); } // 用来计算返回键的点击间隔时间 @@ -62,4 +75,21 @@ public class JMainActivity extends BaseActivity { finish(); } } + + private void open() { + Uri photoUri; + ContentResolver cr = getContentResolver(); + String status = Environment.getExternalStorageState(); + if (status.equals(Environment.MEDIA_MOUNTED)) { + photoUri = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues()); + } else { + photoUri = cr.insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, new ContentValues()); + } + if (null != photoUri) { + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); + startActivityForResult(intent, 1); + } + } } diff --git a/app/src/main/res/layout-port/activity_main.xml b/app/src/main/res/layout-port/activity_main.xml index cef4901..e548d91 100644 --- a/app/src/main/res/layout-port/activity_main.xml +++ b/app/src/main/res/layout-port/activity_main.xml @@ -16,4 +16,30 @@ app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + + + + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 36e1624..f900a8b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,3 @@ include ':app' -rootProject.name = "TTUtils" \ No newline at end of file +rootProject.name = "TTUtils" +include ':viewlibrary' diff --git a/viewlibrary/.gitignore b/viewlibrary/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/viewlibrary/.gitignore @@ -0,0 +1 @@ +/build diff --git a/viewlibrary/build.gradle b/viewlibrary/build.gradle new file mode 100644 index 0000000..7fd501a --- /dev/null +++ b/viewlibrary/build.gradle @@ -0,0 +1,36 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + minSdkVersion 23 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'androidx.appcompat:appcompat:1.2.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + implementation 'com.facebook.rebound:rebound:0.3.6' +// implementation 'com.zcw:togglebutton-library:1.0.0' + implementation 'com.github.zcweng:switch-button:0.0.3@aar' +} diff --git a/viewlibrary/consumer-rules.pro b/viewlibrary/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/viewlibrary/proguard-rules.pro b/viewlibrary/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/viewlibrary/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/viewlibrary/src/androidTest/java/com/ttstd/viewlibrary/ExampleInstrumentedTest.java b/viewlibrary/src/androidTest/java/com/ttstd/viewlibrary/ExampleInstrumentedTest.java new file mode 100644 index 0000000..422410f --- /dev/null +++ b/viewlibrary/src/androidTest/java/com/ttstd/viewlibrary/ExampleInstrumentedTest.java @@ -0,0 +1,27 @@ +package com.ttstd.viewlibrary; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("com.ttstd.viewlibrary.test", appContext.getPackageName()); + } +} diff --git a/viewlibrary/src/main/AndroidManifest.xml b/viewlibrary/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a33d1af --- /dev/null +++ b/viewlibrary/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/viewlibrary/src/main/java/com/ttstd/viewlibrary/ToggleButton.java b/viewlibrary/src/main/java/com/ttstd/viewlibrary/ToggleButton.java new file mode 100644 index 0000000..0a8f3b4 --- /dev/null +++ b/viewlibrary/src/main/java/com/ttstd/viewlibrary/ToggleButton.java @@ -0,0 +1,275 @@ +package com.ttstd.viewlibrary; + +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.RectF; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; + +import com.facebook.rebound.SimpleSpringListener; +import com.facebook.rebound.Spring; +import com.facebook.rebound.SpringConfig; +import com.facebook.rebound.SpringSystem; +import com.facebook.rebound.SpringUtil; + +/** + * The type Toggle button. + * https://www.jb51.net/article/95993.htm + * https://blog.csdn.net/Daisy_ruo/article/details/50283301 + * https://blog.csdn.net/u014752325/article/details/53227296 + * https://blog.csdn.net/qq_28535319/article/details/78720383 + * https://github.com/zcweng/SwitchButton + * https://github.com/zcweng/ToggleButton + */ +public class ToggleButton 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 = 1; + /** 垂直中心*/ + 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 ToggleButton(Context context) { + super(context); + } + public ToggleButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setup(attrs); + } + public ToggleButton(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(Paint.Style.FILL); + paint.setStrokeCap(Paint.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.ToggleButton); + offBorderColor = typedArray.getColor(R.styleable.ToggleButton_tbOffBorderColor, offBorderColor); + onColor = typedArray.getColor(R.styleable.ToggleButton_tbOnColor, onColor); + spotColor = typedArray.getColor(R.styleable.ToggleButton_tbSpotColor, spotColor); + offColor = typedArray.getColor(R.styleable.ToggleButton_tbOffColor, offColor); + borderWidth = typedArray.getDimensionPixelSize(R.styleable.ToggleButton_tbBorderWidth, borderWidth); + defaultAnimate = typedArray.getBoolean(R.styleable.ToggleButton_tbAnimate, defaultAnimate); + isDefaultOn = typedArray.getBoolean(R.styleable.ToggleButton_tbAsDefaultOn, 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 asd + */ + 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) { +// + 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 = = + */ + void onToggle(boolean on); + } + public void setOnToggleChanged(OnToggleChanged onToggleChanged) { + listener = onToggleChanged; + } + public boolean isAnimate() { + return defaultAnimate; + } + public void setAnimate(boolean animate) { + this.defaultAnimate = animate; + } +} diff --git a/viewlibrary/src/main/java/com/ttstd/viewlibrary/ToggleButton2.java b/viewlibrary/src/main/java/com/ttstd/viewlibrary/ToggleButton2.java new file mode 100644 index 0000000..317c0c3 --- /dev/null +++ b/viewlibrary/src/main/java/com/ttstd/viewlibrary/ToggleButton2.java @@ -0,0 +1,341 @@ +package com.ttstd.viewlibrary; + +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.RectF; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; + +import com.facebook.rebound.SimpleSpringListener; +import com.facebook.rebound.Spring; +import com.facebook.rebound.SpringConfig; +import com.facebook.rebound.SpringSystem; +import com.facebook.rebound.SpringUtil; + +public class ToggleButton2 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 boolean disable = false; + + private OnToggleChanged listener; + + public void setDisable(boolean dis) { + this.disable = dis; + } + private ToggleButton2(Context context) { + super(context); + } + + public ToggleButton2(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setup(attrs); + } + + public ToggleButton2(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(Paint.Style.FILL); + paint.setStrokeCap(Paint.Cap.ROUND); + springSystem = SpringSystem.create(); + spring = springSystem.createSpring(); + spring.setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(50, 7)); + this.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + if (disable) { +// T.showShort(getContext(), "没有可以启动的项目"); + } else { + toggle(defaultAnimate); + } + } + }); + TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ToggleButton); + offBorderColor = typedArray.getColor(R.styleable.ToggleButton_tbOffBorderColor, offBorderColor); + onColor = typedArray.getColor(R.styleable.ToggleButton_tbOnColor, onColor); + spotColor = typedArray.getColor(R.styleable.ToggleButton_tbSpotColor, spotColor); + offColor = typedArray.getColor(R.styleable.ToggleButton_tbOffColor, offColor); + borderWidth = typedArray.getDimensionPixelSize(R.styleable.ToggleButton_tbBorderWidth, borderWidth); + defaultAnimate = typedArray.getBoolean(R.styleable.ToggleButton_tbAnimate, defaultAnimate); + isDefaultOn = typedArray.getBoolean(R.styleable.ToggleButton_tbAsDefaultOn, 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 asd + */ + 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 - radius + borderWidth, centerY - radius, spotX + radius -borderWidth, 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; + } +} \ No newline at end of file diff --git a/viewlibrary/src/main/res/values/attrs.xml b/viewlibrary/src/main/res/values/attrs.xml new file mode 100644 index 0000000..3bfb79b --- /dev/null +++ b/viewlibrary/src/main/res/values/attrs.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/viewlibrary/src/test/java/com/ttstd/viewlibrary/ExampleUnitTest.java b/viewlibrary/src/test/java/com/ttstd/viewlibrary/ExampleUnitTest.java new file mode 100644 index 0000000..f9960b3 --- /dev/null +++ b/viewlibrary/src/test/java/com/ttstd/viewlibrary/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.ttstd.viewlibrary; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file