From 922647b18bbb8ffe2da10b9f5b2df64226d3ff1f Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 19 May 2017 17:48:57 -0500 Subject: [PATCH 01/11] Create Method to Set Button Result State *Set Button Result State to success or failure. --- .gitignore | 4 ++-- .../submitbuttonview/SubmitButton.java | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 39fb081..2927b1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ *.iml .gradle /local.properties -/.idea/workspace.xml -/.idea/libraries +.idea/* .DS_Store /build /captures .externalNativeBuild + diff --git a/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java b/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java index 1524c7d..29ce8be 100644 --- a/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java +++ b/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java @@ -446,6 +446,28 @@ public void reset() { invalidate(); } + /** + * Set Button to Result State + * + * @param success true = result success; false = failure + */ + public void setResult(boolean success) + { + isSucceed = success; + viewState = STATE_RESULT; + if (loadingAnim != null) { + loadingAnim.cancel(); + } + if (isSucceed) { + bgPaint.setColor(succeedColor); + } else { + bgPaint.setColor(failedColor); + } + bgPaint.setStyle(Paint.Style.FILL_AND_STROKE); + resultPaint.setAlpha(255); + invalidate(); + } + /** * 设置进度 * From f60c4964dc8f7ca2d120ad7b84886947a4bcdf2a Mon Sep 17 00:00:00 2001 From: Ray Li Date: Sat, 7 Oct 2017 12:13:10 -0500 Subject: [PATCH 02/11] Update Build Tools v26.0.2 *Update Build Tools and SDK to v26.0.2. *Version bump to v1.2. *Jitpack release. --- app/build.gradle | 61 +- .../submitbutton/MainActivity.java | 266 ++-- build.gradle | 45 +- submitbuttonview/build.gradle | 86 +- .../submitbuttonview/SubmitButton.java | 1110 ++++++++--------- 5 files changed, 771 insertions(+), 797 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index fe4f8b6..d1ceed1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,31 +1,30 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" - defaultConfig { - applicationId "com.unstoppable.submitbutton" - minSdkVersion 15 - targetSdkVersion 25 - versionCode 1 - versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) - compile project(':submitbuttonview') - compile 'com.android.support:appcompat-v7:25.3.1' - compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha4' - testCompile 'junit:junit:4.12' -} +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + buildToolsVersion "26.0.2" + defaultConfig { + applicationId "com.unstoppable.submitbutton" + minSdkVersion 15 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + repositories { + maven { url "https://maven.google.com" } + } + compile fileTree(include: ['*.jar'], dir: 'libs') + compile project(':submitbuttonview') + + compile 'com.android.support:appcompat-v7:26.0.2' + compile 'com.android.support.constraint:constraint-layout:1.0.2' +} diff --git a/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java b/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java index c3378c5..f65e891 100644 --- a/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java +++ b/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java @@ -1,133 +1,133 @@ -package com.unstoppable.submitbutton; - -import android.os.AsyncTask; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.view.accessibility.AccessibilityManager; -import android.widget.Button; -import android.widget.CompoundButton; -import android.widget.LinearLayout; -import android.widget.Switch; -import android.widget.Toast; - -import com.unstoppable.submitbuttonview.SubmitButton; - -public class MainActivity extends AppCompatActivity implements View.OnClickListener { - - private SubmitButton sBtnLoading, sBtnProgress; - private Button btnSucceed, btnFailed, btnReset; - private Switch mSwitch; - private MyTask task; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - sBtnLoading = (SubmitButton) findViewById(R.id.sbtn_loading); - sBtnProgress = (SubmitButton) findViewById(R.id.sbtn_progress); - mSwitch = (Switch) findViewById(R.id.switch1); - - btnFailed = (Button) findViewById(R.id.btn_failed); - btnSucceed = (Button) findViewById(R.id.btn_succeed); - btnReset = (Button) findViewById(R.id.btn_reset); - - sBtnLoading.setOnClickListener(this); - sBtnProgress.setOnClickListener(this); - btnSucceed.setOnClickListener(this); - btnFailed.setOnClickListener(this); - btnReset.setOnClickListener(this); - - mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (isChecked) { - sBtnLoading.setVisibility(View.GONE); - sBtnProgress.setVisibility(View.VISIBLE); - sBtnLoading.reset(); - } else { - sBtnLoading.setVisibility(View.VISIBLE); - sBtnProgress.setVisibility(View.GONE); - if (task != null && !task.isCancelled()) { - task.cancel(true); - sBtnProgress.reset(); - } - } - } - }); - } - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.sbtn_loading: - Toast.makeText(this, "SubmitButton be clicked", Toast.LENGTH_SHORT).show(); - break; - case R.id.sbtn_progress: - if (task == null || task.isCancelled()) { - task = new MyTask(); - task.execute(); - } - break; - case R.id.btn_succeed: - if (mSwitch.isChecked()) { - sBtnProgress.doResult(true); - } else { - sBtnLoading.doResult(true); - } - break; - case R.id.btn_failed: - if (mSwitch.isChecked()) { - sBtnProgress.doResult(false); - } else { - sBtnLoading.doResult(false); - } - break; - case R.id.btn_reset: - if (mSwitch.isChecked()) { - if (task != null && !task.isCancelled()) { - task.cancel(true); - sBtnProgress.reset(); - } - } else { - sBtnLoading.reset(); - } - break; - } - } - - private class MyTask extends AsyncTask { - - @Override - protected Boolean doInBackground(Void... params) { - int i = 0; - while (i <= 100) { - if (isCancelled()) { - return null; - } - try { - Thread.sleep(30); - } catch (InterruptedException e) { - e.printStackTrace(); - } - i++; - publishProgress(i); - } - return true; - } - - @Override - protected void onPostExecute(Boolean aBoolean) { - if (aBoolean == null) { - sBtnProgress.reset(); - } - sBtnProgress.doResult(aBoolean); - } - - @Override - protected void onProgressUpdate(Integer... values) { - sBtnProgress.setProgress(values[0]); - } - } -} +package com.unstoppable.submitbutton; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.view.accessibility.AccessibilityManager; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.Switch; +import android.widget.Toast; + +import com.unstoppable.submitbuttonview.SubmitButton; + +public class MainActivity extends AppCompatActivity implements View.OnClickListener { + + private SubmitButton sBtnLoading, sBtnProgress; + private Button btnSucceed, btnFailed, btnReset; + private Switch mSwitch; + private MyTask task; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + sBtnLoading = findViewById(R.id.sbtn_loading); + sBtnProgress = findViewById(R.id.sbtn_progress); + mSwitch = findViewById(R.id.switch1); + + btnFailed = findViewById(R.id.btn_failed); + btnSucceed = findViewById(R.id.btn_succeed); + btnReset = findViewById(R.id.btn_reset); + + sBtnLoading.setOnClickListener(this); + sBtnProgress.setOnClickListener(this); + btnSucceed.setOnClickListener(this); + btnFailed.setOnClickListener(this); + btnReset.setOnClickListener(this); + + mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + sBtnLoading.setVisibility(View.GONE); + sBtnProgress.setVisibility(View.VISIBLE); + sBtnLoading.reset(); + } else { + sBtnLoading.setVisibility(View.VISIBLE); + sBtnProgress.setVisibility(View.GONE); + if (task != null && !task.isCancelled()) { + task.cancel(true); + sBtnProgress.reset(); + } + } + } + }); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.sbtn_loading: + Toast.makeText(this, "SubmitButton be clicked", Toast.LENGTH_SHORT).show(); + break; + case R.id.sbtn_progress: + if (task == null || task.isCancelled()) { + task = new MyTask(); + task.execute(); + } + break; + case R.id.btn_succeed: + if (mSwitch.isChecked()) { + sBtnProgress.doResult(true); + } else { + sBtnLoading.doResult(true); + } + break; + case R.id.btn_failed: + if (mSwitch.isChecked()) { + sBtnProgress.doResult(false); + } else { + sBtnLoading.doResult(false); + } + break; + case R.id.btn_reset: + if (mSwitch.isChecked()) { + if (task != null && !task.isCancelled()) { + task.cancel(true); + sBtnProgress.reset(); + } + } else { + sBtnLoading.reset(); + } + break; + } + } + + private class MyTask extends AsyncTask { + + @Override + protected Boolean doInBackground(Void... params) { + int i = 0; + while (i <= 100) { + if (isCancelled()) { + return null; + } + try { + Thread.sleep(30); + } catch (InterruptedException e) { + e.printStackTrace(); + } + i++; + publishProgress(i); + } + return true; + } + + @Override + protected void onPostExecute(Boolean aBoolean) { + if (aBoolean == null) { + sBtnProgress.reset(); + } + sBtnProgress.doResult(aBoolean); + } + + @Override + protected void onProgressUpdate(Integer... values) { + sBtnProgress.setProgress(values[0]); + } + } +} diff --git a/build.gradle b/build.gradle index 843fc53..7af5374 100644 --- a/build.gradle +++ b/build.gradle @@ -1,30 +1,15 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' - classpath 'com.novoda:bintray-release:0.4.0' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - jcenter() - } - tasks.withType(Javadoc) { - options{ - encoding "UTF-8" - charSet 'UTF-8' - links "http://docs.oracle.com/javase/7/docs/api" - } - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + } +} + +allprojects { + repositories { + jcenter() + } +} \ No newline at end of file diff --git a/submitbuttonview/build.gradle b/submitbuttonview/build.gradle index 927fc91..9d10cb1 100644 --- a/submitbuttonview/build.gradle +++ b/submitbuttonview/build.gradle @@ -1,47 +1,39 @@ -apply plugin: 'com.android.library' -apply plugin: 'com.novoda.bintray-release' - -android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" - - defaultConfig { - minSdkVersion 15 - targetSdkVersion 25 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - lintOptions { - abortOnError false - } -} - -dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) - compile 'com.android.support:appcompat-v7:25.3.1' - testCompile 'junit:junit:4.12' -} - -publish { - userOrg = 'unstoppable' - groupId = 'com.unstoppable' - artifactId = 'submitbutton' - publishVersion = '1.1.2' - desc = 'It\'s a submit button with a fun animation for Android.' - website = 'https://github.com/Someonewow/SubmitButton' -} - +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' + +group='com.github.searchy2' +version = '1.2' + +android { + compileSdkVersion 26 + buildToolsVersion "26.0.2" + + defaultConfig { + minSdkVersion 15 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + lintOptions { + abortOnError false + } +} + +dependencies { + repositories { + maven { url "https://maven.google.com" } + } + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:26.0.2' +} \ No newline at end of file diff --git a/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java b/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java index d873ba2..0d2b3dc 100644 --- a/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java +++ b/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java @@ -1,557 +1,555 @@ -package com.unstoppable.submitbuttonview; - -import android.animation.Animator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PathMeasure; -import android.graphics.Rect; -import android.graphics.RectF; -import android.support.v4.content.ContextCompat; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import android.view.animation.AccelerateInterpolator; - -/** - * Created by Unstoppable on 2016/12/31. - */ - -public class SubmitButton extends View { - - private static final int STATE_NONE = 0; - private static final int STATE_SUBMIT = 1; - private static final int STATE_LOADING = 2; - private static final int STATE_RESULT = 3; - - //view状态 - private int viewState = STATE_NONE; - - //View宽高 - private int mWidth; - private int mHeight; - - private int MAX_WIDTH; - private int MAX_HEIGHT; - - //画布坐标原点 - private int x, y; - - private String buttonText = ""; - private int buttonColor; - private int succeedColor; - private int failedColor; - private int textSize; - - //文本宽高 - private int textWidth; - private int textHeight; - - private Paint bgPaint, loadingPaint, resultPaint, textPaint; - - private Path buttonPath; - private Path loadPath; - private Path dst; - private PathMeasure pathMeasure; - private Path resultPath; - - private RectF circleLeft, circleMid, circleRight; - - private float loadValue; - - private ValueAnimator submitAnim, loadingAnim, resultAnim; - - private boolean isDoResult; - private boolean isSucceed; - - private static final int STYLE_LOADING = 0; - private static final int STYLE_PROGRESS = 1; - - //view加载等待模式 - private int progressStyle = STYLE_LOADING; - private float currentProgress; - - private OnResultEndListener listener; - - public SubmitButton(Context context) { - this(context, null); - } - - public SubmitButton(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public SubmitButton(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SubmitButton, defStyleAttr, 0); - if (typedArray.getString(R.styleable.SubmitButton_buttonText) != null) { - buttonText = typedArray.getString(R.styleable.SubmitButton_buttonText); - } - buttonColor = typedArray.getColor(R.styleable.SubmitButton_buttonColor, Color.parseColor("#19CC95")); - succeedColor = typedArray.getColor(R.styleable.SubmitButton_succeedColor, Color.parseColor("#19CC95")); - failedColor = typedArray.getColor(R.styleable.SubmitButton_failedColor, Color.parseColor("#FC8E34")); - textSize = (int) typedArray.getDimension(R.styleable.SubmitButton_buttonTextSize, sp2px(15)); - progressStyle = typedArray.getInt(R.styleable.SubmitButton_progressStyle, STYLE_LOADING); - typedArray.recycle(); - //关闭硬件加速 - this.setLayerType(LAYER_TYPE_SOFTWARE,null); - init(); - } - - /** - * 初始化Paint、Path - */ - private void init() { - bgPaint = new Paint(); - bgPaint.setColor(buttonColor); - bgPaint.setStyle(Paint.Style.STROKE); - bgPaint.setStrokeWidth(5); - bgPaint.setAntiAlias(true); - - loadingPaint = new Paint(); - loadingPaint.setColor(buttonColor); - loadingPaint.setStyle(Paint.Style.STROKE); - loadingPaint.setStrokeWidth(9); - loadingPaint.setAntiAlias(true); - - resultPaint = new Paint(); - resultPaint.setColor(Color.WHITE); - resultPaint.setStyle(Paint.Style.STROKE); - resultPaint.setStrokeWidth(9); - resultPaint.setStrokeCap(Paint.Cap.ROUND); - resultPaint.setAntiAlias(true); - - textPaint = new Paint(); - textPaint.setColor(buttonColor); - textPaint.setStrokeWidth(textSize / 6); - textPaint.setTextSize(textSize); - textPaint.setAntiAlias(true); - - textWidth = getTextWidth(textPaint, buttonText); - textHeight = getTextHeight(textPaint, buttonText); - - buttonPath = new Path(); - loadPath = new Path(); - resultPath = new Path(); - dst = new Path(); - circleMid = new RectF(); - circleLeft = new RectF(); - circleRight = new RectF(); - pathMeasure = new PathMeasure(); - } - - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); - - if (widthMode == MeasureSpec.AT_MOST) { - widthSize = textWidth + 100; - } - - if (heightMode == MeasureSpec.AT_MOST) { - heightSize = (int) (textHeight * 2.5); - } - - if (heightSize > widthSize) { - heightSize = (int) (widthSize * 0.25); - } - - mWidth = widthSize - 10; - mHeight = heightSize - 10; - x = (int) (widthSize * 0.5); - y = (int) (heightSize * 0.5); - MAX_WIDTH = mWidth; - MAX_HEIGHT = mHeight; - - setMeasuredDimension(widthSize, heightSize); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - canvas.translate(x, y); - drawButton(canvas); - if (viewState == STATE_NONE || viewState == STATE_SUBMIT && mWidth > textWidth) { - drawButtonText(canvas); - } - if (viewState == STATE_LOADING) { - drawLoading(canvas); - } - if (viewState == STATE_RESULT) { - drawResult(canvas, isSucceed); - } - } - - /** - * 绘制初始状态Button - * - * @param canvas 画布 - */ - private void drawButton(Canvas canvas) { - buttonPath.reset(); - circleLeft.set(-mWidth / 2, -mHeight / 2, -mWidth / 2 + mHeight, mHeight / 2); - buttonPath.arcTo(circleLeft, 90, 180); - buttonPath.lineTo(mWidth / 2 - mHeight / 2, -mHeight / 2); - circleRight.set(mWidth / 2 - mHeight, -mHeight / 2, mWidth / 2, mHeight / 2); - buttonPath.arcTo(circleRight, 270, 180); - buttonPath.lineTo(-mWidth / 2 + mHeight / 2, mHeight / 2); - canvas.drawPath(buttonPath, bgPaint); - } - - /** - * 绘制加载状态Button - * - * @param canvas 画布 - */ - private void drawLoading(Canvas canvas) { - dst.reset(); - circleMid.set(-MAX_HEIGHT / 2, -MAX_HEIGHT / 2, MAX_HEIGHT / 2, MAX_HEIGHT / 2); - loadPath.addArc(circleMid, 270, 359.999f); - pathMeasure.setPath(loadPath, true); - float startD = 0f, stopD; - if (progressStyle == STYLE_LOADING) { - startD = pathMeasure.getLength() * loadValue; - stopD = startD + pathMeasure.getLength() / 2 * loadValue; - } else { - stopD = pathMeasure.getLength() * currentProgress; - } - pathMeasure.getSegment(startD, stopD, dst, true); - canvas.drawPath(dst, loadingPaint); - } - - /** - * 绘制结果状态Button - * - * @param canvas 画布 - */ - private void drawResult(Canvas canvas, boolean isSucceed) { - if (isSucceed) { - resultPath.moveTo(-mHeight / 6, 0); - resultPath.lineTo(0, (float) (-mHeight / 6 + (1 + Math.sqrt(5)) * mHeight / 12)); - resultPath.lineTo(mHeight / 6, -mHeight / 6); - } else { - resultPath.moveTo(-mHeight / 6, mHeight / 6); - resultPath.lineTo(mHeight / 6, -mHeight / 6); - resultPath.moveTo(-mHeight / 6, -mHeight / 6); - resultPath.lineTo(mHeight / 6, mHeight / 6); - } - canvas.drawPath(resultPath, resultPaint); - } - - /** - * 绘制Button文本 - * - * @param canvas 画布 - */ - private void drawButtonText(Canvas canvas) { - textPaint.setAlpha(((mWidth - textWidth) * 255) / (MAX_WIDTH - textWidth)); - canvas.drawText(buttonText, -textWidth / 2, getTextBaseLineOffset(), textPaint); - } - - /** - * 开始提交动画 - */ - private void startSubmitAnim() { - viewState = STATE_SUBMIT; - submitAnim = new ValueAnimator().ofInt(MAX_WIDTH, MAX_HEIGHT); - submitAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mWidth = (int) animation.getAnimatedValue(); - if (mWidth == mHeight) { - bgPaint.setColor(Color.parseColor("#DDDDDD")); - } - invalidate(); - } - }); - submitAnim.setDuration(300); - submitAnim.setInterpolator(new AccelerateInterpolator()); - submitAnim.start(); - submitAnim.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - - } - - @Override - public void onAnimationEnd(Animator animation) { - if (isDoResult) { - startResultAnim(); - } else { - startLoadingAnim(); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } - }); - } - - /** - * 开始加载动画 - */ - private void startLoadingAnim() { - viewState = STATE_LOADING; - if (progressStyle == STYLE_PROGRESS) { - return; - } - loadingAnim = new ValueAnimator().ofFloat(0.0f, 1.0f); - loadingAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - loadValue = (float) animation.getAnimatedValue(); - invalidate(); - } - }); - loadingAnim.setDuration(2000); - loadingAnim.setRepeatCount(ValueAnimator.INFINITE); - loadingAnim.start(); - } - - /** - * 开始结果动画 - */ - private void startResultAnim() { - viewState = STATE_RESULT; - if (loadingAnim != null) { - loadingAnim.cancel(); - } - resultAnim = new ValueAnimator().ofInt(MAX_HEIGHT, MAX_WIDTH); - resultAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mWidth = (int) animation.getAnimatedValue(); - resultPaint.setAlpha(((mWidth - mHeight) * 255) / (MAX_WIDTH - MAX_HEIGHT)); - if (mWidth == mHeight) { - if (isSucceed) { - bgPaint.setColor(succeedColor); - } else { - bgPaint.setColor(failedColor); - } - bgPaint.setStyle(Paint.Style.FILL_AND_STROKE); - } - invalidate(); - } - }); - resultAnim.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - - } - - @Override - public void onAnimationEnd(Animator animation) { - if (listener == null) { - return; - } - postDelayed(new Runnable() { - @Override - public void run() { - listener.onResultEnd(); - } - }, 500); - } - - @Override - public void onAnimationCancel(Animator animation) { - - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } - }); - resultAnim.setDuration(300); - resultAnim.setInterpolator(new AccelerateInterpolator()); - resultAnim.start(); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_UP: - if (viewState == STATE_NONE) { - startSubmitAnim(); - } - } - return super.onTouchEvent(event); - } - - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (submitAnim != null) { - submitAnim.cancel(); - } - if (loadingAnim != null) { - loadingAnim.cancel(); - } - if (resultAnim != null) { - resultAnim.cancel(); - } - } - - /** - * 设置submit结果 - * - * @param isSucceed 是否成功 - */ - public void doResult(boolean isSucceed) { - if (viewState == STATE_NONE || viewState == STATE_RESULT || isDoResult) { - return; - } - isDoResult = true; - this.isSucceed = isSucceed; - if (viewState == STATE_LOADING) { - startResultAnim(); - } - } - - /** - * 恢复初始化Button状态 - */ - public void reset() { - if (submitAnim != null) { - submitAnim.cancel(); - } - if (loadingAnim != null) { - loadingAnim.cancel(); - } - if (resultAnim != null) { - resultAnim.cancel(); - } - viewState = STATE_NONE; - mWidth = MAX_WIDTH; - mHeight = MAX_HEIGHT; - isSucceed = false; - isDoResult = false; - currentProgress = 0; - init(); - invalidate(); - } - - /** - * Set Button to Result State - * - * @param success true = result success; false = failure - */ - public void setResult(boolean success) - { - isSucceed = success; - viewState = STATE_RESULT; - if (loadingAnim != null) { - loadingAnim.cancel(); - } - if (isSucceed) { - bgPaint.setColor(succeedColor); - } else { - bgPaint.setColor(failedColor); - } - bgPaint.setStyle(Paint.Style.FILL_AND_STROKE); - resultPaint.setAlpha(255); - invalidate(); - } - - /** - * 设置进度 - * - * @param progress 进度值 (0-100) - */ - public void setProgress(int progress) { - if (progress < 0 || progress > 100) { - return; - } - currentProgress = (float) (progress * 0.01); - if (progressStyle == STYLE_PROGRESS && viewState == STATE_LOADING) { - invalidate(); - } - } - - /** - * 设置动画结束回调接口 - * - * @param listener - */ - public void setOnResultEndListener(OnResultEndListener listener) { - this.listener = listener; - } - - /** - * 结果动画结束回调接口 - */ - public interface OnResultEndListener { - void onResultEnd(); - } - - /** - * sp to dp - * - * @param sp - * @return dp - */ - private int sp2px(float sp) { - final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity; - return (int) (sp * fontScale + 0.5f); - } - - /** - * 计算水平居中的baseline - * - * @return - */ - private float getTextBaseLineOffset() { - Paint.FontMetrics fm = textPaint.getFontMetrics(); - return -(fm.bottom + fm.top) / 2; - } - - /** - * 获取Text高度 - * - * @param paint - * @param str 文本内容 - * @return - */ - private int getTextHeight(Paint paint, String str) { - Rect rect = new Rect(); - paint.getTextBounds(str, 0, str.length(), rect); - return rect.height(); - } - - /** - * 获取Text宽度 - * - * @param paint - * @param str 文本内容 - * @return - */ - private int getTextWidth(Paint paint, String str) { - int mRet = 0; - if (str != null && str.length() > 0) { - int len = str.length(); - float[] widths = new float[len]; - paint.getTextWidths(str, widths); - for (int j = 0; j < len; j++) { - mRet += (int) Math.ceil(widths[j]); - } - } - return mRet; - } +package com.unstoppable.submitbuttonview; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PathMeasure; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.AccelerateInterpolator; + +/** + * Created by Unstoppable on 2016/12/31. + */ + +public class SubmitButton extends View { + + private static final int STATE_NONE = 0; + private static final int STATE_SUBMIT = 1; + private static final int STATE_LOADING = 2; + private static final int STATE_RESULT = 3; + + //view状态 + private int viewState = STATE_NONE; + + //View宽高 + private int mWidth; + private int mHeight; + + private int MAX_WIDTH; + private int MAX_HEIGHT; + + //画布坐标原点 + private int x, y; + + private String buttonText = ""; + private int buttonColor; + private int succeedColor; + private int failedColor; + private int textSize; + + //文本宽高 + private int textWidth; + private int textHeight; + + private Paint bgPaint, loadingPaint, resultPaint, textPaint; + + private Path buttonPath; + private Path loadPath; + private Path dst; + private PathMeasure pathMeasure; + private Path resultPath; + + private RectF circleLeft, circleMid, circleRight; + + private float loadValue; + + private ValueAnimator submitAnim, loadingAnim, resultAnim; + + private boolean isDoResult; + private boolean isSucceed; + + private static final int STYLE_LOADING = 0; + private static final int STYLE_PROGRESS = 1; + + //view加载等待模式 + private int progressStyle = STYLE_LOADING; + private float currentProgress; + + private OnResultEndListener listener; + + public SubmitButton(Context context) { + this(context, null); + } + + public SubmitButton(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SubmitButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SubmitButton, defStyleAttr, 0); + if (typedArray.getString(R.styleable.SubmitButton_buttonText) != null) { + buttonText = typedArray.getString(R.styleable.SubmitButton_buttonText); + } + buttonColor = typedArray.getColor(R.styleable.SubmitButton_buttonColor, Color.parseColor("#19CC95")); + succeedColor = typedArray.getColor(R.styleable.SubmitButton_succeedColor, Color.parseColor("#19CC95")); + failedColor = typedArray.getColor(R.styleable.SubmitButton_failedColor, Color.parseColor("#FC8E34")); + textSize = (int) typedArray.getDimension(R.styleable.SubmitButton_buttonTextSize, sp2px(15)); + progressStyle = typedArray.getInt(R.styleable.SubmitButton_progressStyle, STYLE_LOADING); + typedArray.recycle(); + //关闭硬件加速 + this.setLayerType(LAYER_TYPE_SOFTWARE,null); + init(); + } + + /** + * 初始化Paint、Path + */ + private void init() { + bgPaint = new Paint(); + bgPaint.setColor(buttonColor); + bgPaint.setStyle(Paint.Style.STROKE); + bgPaint.setStrokeWidth(5); + bgPaint.setAntiAlias(true); + + loadingPaint = new Paint(); + loadingPaint.setColor(buttonColor); + loadingPaint.setStyle(Paint.Style.STROKE); + loadingPaint.setStrokeWidth(9); + loadingPaint.setAntiAlias(true); + + resultPaint = new Paint(); + resultPaint.setColor(Color.WHITE); + resultPaint.setStyle(Paint.Style.STROKE); + resultPaint.setStrokeWidth(9); + resultPaint.setStrokeCap(Paint.Cap.ROUND); + resultPaint.setAntiAlias(true); + + textPaint = new Paint(); + textPaint.setColor(buttonColor); + textPaint.setStrokeWidth(textSize / 6); + textPaint.setTextSize(textSize); + textPaint.setAntiAlias(true); + + textWidth = getTextWidth(textPaint, buttonText); + textHeight = getTextHeight(textPaint, buttonText); + + buttonPath = new Path(); + loadPath = new Path(); + resultPath = new Path(); + dst = new Path(); + circleMid = new RectF(); + circleLeft = new RectF(); + circleRight = new RectF(); + pathMeasure = new PathMeasure(); + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + if (widthMode == MeasureSpec.AT_MOST) { + widthSize = textWidth + 100; + } + + if (heightMode == MeasureSpec.AT_MOST) { + heightSize = (int) (textHeight * 2.5); + } + + if (heightSize > widthSize) { + heightSize = (int) (widthSize * 0.25); + } + + mWidth = widthSize - 10; + mHeight = heightSize - 10; + x = (int) (widthSize * 0.5); + y = (int) (heightSize * 0.5); + MAX_WIDTH = mWidth; + MAX_HEIGHT = mHeight; + + setMeasuredDimension(widthSize, heightSize); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.translate(x, y); + drawButton(canvas); + if (viewState == STATE_NONE || viewState == STATE_SUBMIT && mWidth > textWidth) { + drawButtonText(canvas); + } + if (viewState == STATE_LOADING) { + drawLoading(canvas); + } + if (viewState == STATE_RESULT) { + drawResult(canvas, isSucceed); + } + } + + /** + * 绘制初始状态Button + * + * @param canvas 画布 + */ + private void drawButton(Canvas canvas) { + buttonPath.reset(); + circleLeft.set(-mWidth / 2, -mHeight / 2, -mWidth / 2 + mHeight, mHeight / 2); + buttonPath.arcTo(circleLeft, 90, 180); + buttonPath.lineTo(mWidth / 2 - mHeight / 2, -mHeight / 2); + circleRight.set(mWidth / 2 - mHeight, -mHeight / 2, mWidth / 2, mHeight / 2); + buttonPath.arcTo(circleRight, 270, 180); + buttonPath.lineTo(-mWidth / 2 + mHeight / 2, mHeight / 2); + canvas.drawPath(buttonPath, bgPaint); + } + + /** + * 绘制加载状态Button + * + * @param canvas 画布 + */ + private void drawLoading(Canvas canvas) { + dst.reset(); + circleMid.set(-MAX_HEIGHT / 2, -MAX_HEIGHT / 2, MAX_HEIGHT / 2, MAX_HEIGHT / 2); + loadPath.addArc(circleMid, 270, 359.999f); + pathMeasure.setPath(loadPath, true); + float startD = 0f, stopD; + if (progressStyle == STYLE_LOADING) { + startD = pathMeasure.getLength() * loadValue; + stopD = startD + pathMeasure.getLength() / 2 * loadValue; + } else { + stopD = pathMeasure.getLength() * currentProgress; + } + pathMeasure.getSegment(startD, stopD, dst, true); + canvas.drawPath(dst, loadingPaint); + } + + /** + * 绘制结果状态Button + * + * @param canvas 画布 + */ + private void drawResult(Canvas canvas, boolean isSucceed) { + if (isSucceed) { + resultPath.moveTo(-mHeight / 6, 0); + resultPath.lineTo(0, (float) (-mHeight / 6 + (1 + Math.sqrt(5)) * mHeight / 12)); + resultPath.lineTo(mHeight / 6, -mHeight / 6); + } else { + resultPath.moveTo(-mHeight / 6, mHeight / 6); + resultPath.lineTo(mHeight / 6, -mHeight / 6); + resultPath.moveTo(-mHeight / 6, -mHeight / 6); + resultPath.lineTo(mHeight / 6, mHeight / 6); + } + canvas.drawPath(resultPath, resultPaint); + } + + /** + * 绘制Button文本 + * + * @param canvas 画布 + */ + private void drawButtonText(Canvas canvas) { + textPaint.setAlpha(((mWidth - textWidth) * 255) / (MAX_WIDTH - textWidth)); + canvas.drawText(buttonText, -textWidth / 2, getTextBaseLineOffset(), textPaint); + } + + /** + * 开始提交动画 + */ + private void startSubmitAnim() { + viewState = STATE_SUBMIT; + submitAnim = new ValueAnimator().ofInt(MAX_WIDTH, MAX_HEIGHT); + submitAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mWidth = (int) animation.getAnimatedValue(); + if (mWidth == mHeight) { + bgPaint.setColor(Color.parseColor("#DDDDDD")); + } + invalidate(); + } + }); + submitAnim.setDuration(300); + submitAnim.setInterpolator(new AccelerateInterpolator()); + submitAnim.start(); + submitAnim.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + if (isDoResult) { + startResultAnim(); + } else { + startLoadingAnim(); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + } + + /** + * 开始加载动画 + */ + private void startLoadingAnim() { + viewState = STATE_LOADING; + if (progressStyle == STYLE_PROGRESS) { + return; + } + loadingAnim = new ValueAnimator().ofFloat(0.0f, 1.0f); + loadingAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + loadValue = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + loadingAnim.setDuration(2000); + loadingAnim.setRepeatCount(ValueAnimator.INFINITE); + loadingAnim.start(); + } + + /** + * 开始结果动画 + */ + private void startResultAnim() { + viewState = STATE_RESULT; + if (loadingAnim != null) { + loadingAnim.cancel(); + } + resultAnim = new ValueAnimator().ofInt(MAX_HEIGHT, MAX_WIDTH); + resultAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mWidth = (int) animation.getAnimatedValue(); + resultPaint.setAlpha(((mWidth - mHeight) * 255) / (MAX_WIDTH - MAX_HEIGHT)); + if (mWidth == mHeight) { + if (isSucceed) { + bgPaint.setColor(succeedColor); + } else { + bgPaint.setColor(failedColor); + } + bgPaint.setStyle(Paint.Style.FILL_AND_STROKE); + } + invalidate(); + } + }); + resultAnim.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + if (listener == null) { + return; + } + postDelayed(new Runnable() { + @Override + public void run() { + listener.onResultEnd(); + } + }, 500); + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + resultAnim.setDuration(300); + resultAnim.setInterpolator(new AccelerateInterpolator()); + resultAnim.start(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_UP: + if (viewState == STATE_NONE) { + startSubmitAnim(); + } + } + return super.onTouchEvent(event); + } + + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (submitAnim != null) { + submitAnim.cancel(); + } + if (loadingAnim != null) { + loadingAnim.cancel(); + } + if (resultAnim != null) { + resultAnim.cancel(); + } + } + + /** + * 设置submit结果 + * + * @param isSucceed 是否成功 + */ + public void doResult(boolean isSucceed) { + if (viewState == STATE_NONE || viewState == STATE_RESULT || isDoResult) { + return; + } + isDoResult = true; + this.isSucceed = isSucceed; + if (viewState == STATE_LOADING) { + startResultAnim(); + } + } + + /** + * 恢复初始化Button状态 + */ + public void reset() { + if (submitAnim != null) { + submitAnim.cancel(); + } + if (loadingAnim != null) { + loadingAnim.cancel(); + } + if (resultAnim != null) { + resultAnim.cancel(); + } + viewState = STATE_NONE; + mWidth = MAX_WIDTH; + mHeight = MAX_HEIGHT; + isSucceed = false; + isDoResult = false; + currentProgress = 0; + init(); + invalidate(); + } + + /** + * Set Button to Result State + * + * @param success true = result success; false = failure + */ + public void setResult(boolean success) + { + isSucceed = success; + viewState = STATE_RESULT; + if (loadingAnim != null) { + loadingAnim.cancel(); + } + if (isSucceed) { + bgPaint.setColor(succeedColor); + } else { + bgPaint.setColor(failedColor); + } + bgPaint.setStyle(Paint.Style.FILL_AND_STROKE); + resultPaint.setAlpha(255); + invalidate(); + } + + /** + * 设置进度 + * + * @param progress 进度值 (0-100) + */ + public void setProgress(int progress) { + if (progress < 0 || progress > 100) { + return; + } + currentProgress = (float) (progress * 0.01); + if (progressStyle == STYLE_PROGRESS && viewState == STATE_LOADING) { + invalidate(); + } + } + + /** + * 设置动画结束回调接口 + * + * @param listener + */ + public void setOnResultEndListener(OnResultEndListener listener) { + this.listener = listener; + } + + /** + * 结果动画结束回调接口 + */ + public interface OnResultEndListener { + void onResultEnd(); + } + + /** + * sp to dp + * + * @param sp + * @return dp + */ + private int sp2px(float sp) { + final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity; + return (int) (sp * fontScale + 0.5f); + } + + /** + * 计算水平居中的baseline + * + * @return + */ + private float getTextBaseLineOffset() { + Paint.FontMetrics fm = textPaint.getFontMetrics(); + return -(fm.bottom + fm.top) / 2; + } + + /** + * 获取Text高度 + * + * @param paint + * @param str 文本内容 + * @return + */ + private int getTextHeight(Paint paint, String str) { + Rect rect = new Rect(); + paint.getTextBounds(str, 0, str.length(), rect); + return rect.height(); + } + + /** + * 获取Text宽度 + * + * @param paint + * @param str 文本内容 + * @return + */ + private int getTextWidth(Paint paint, String str) { + int mRet = 0; + if (str != null && str.length() > 0) { + int len = str.length(); + float[] widths = new float[len]; + paint.getTextWidths(str, widths); + for (int j = 0; j < len; j++) { + mRet += (int) Math.ceil(widths[j]); + } + } + return mRet; + } } \ No newline at end of file From 4e2788c0e2472ddcfb95dee4f1344c064f6b82d6 Mon Sep 17 00:00:00 2001 From: Ray Li Date: Sat, 7 Oct 2017 12:13:10 -0500 Subject: [PATCH 03/11] SetLoading Creation *Create method to set button to loading state. *Version bump to v1.3. --- app/build.gradle | 61 +- .../submitbutton/MainActivity.java | 266 ++-- build.gradle | 45 +- submitbuttonview/build.gradle | 86 +- .../submitbuttonview/SubmitButton.java | 1120 +++++++++-------- 5 files changed, 781 insertions(+), 797 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index fe4f8b6..d1ceed1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,31 +1,30 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" - defaultConfig { - applicationId "com.unstoppable.submitbutton" - minSdkVersion 15 - targetSdkVersion 25 - versionCode 1 - versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) - compile project(':submitbuttonview') - compile 'com.android.support:appcompat-v7:25.3.1' - compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha4' - testCompile 'junit:junit:4.12' -} +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + buildToolsVersion "26.0.2" + defaultConfig { + applicationId "com.unstoppable.submitbutton" + minSdkVersion 15 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + repositories { + maven { url "https://maven.google.com" } + } + compile fileTree(include: ['*.jar'], dir: 'libs') + compile project(':submitbuttonview') + + compile 'com.android.support:appcompat-v7:26.0.2' + compile 'com.android.support.constraint:constraint-layout:1.0.2' +} diff --git a/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java b/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java index c3378c5..f65e891 100644 --- a/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java +++ b/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java @@ -1,133 +1,133 @@ -package com.unstoppable.submitbutton; - -import android.os.AsyncTask; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.view.accessibility.AccessibilityManager; -import android.widget.Button; -import android.widget.CompoundButton; -import android.widget.LinearLayout; -import android.widget.Switch; -import android.widget.Toast; - -import com.unstoppable.submitbuttonview.SubmitButton; - -public class MainActivity extends AppCompatActivity implements View.OnClickListener { - - private SubmitButton sBtnLoading, sBtnProgress; - private Button btnSucceed, btnFailed, btnReset; - private Switch mSwitch; - private MyTask task; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - sBtnLoading = (SubmitButton) findViewById(R.id.sbtn_loading); - sBtnProgress = (SubmitButton) findViewById(R.id.sbtn_progress); - mSwitch = (Switch) findViewById(R.id.switch1); - - btnFailed = (Button) findViewById(R.id.btn_failed); - btnSucceed = (Button) findViewById(R.id.btn_succeed); - btnReset = (Button) findViewById(R.id.btn_reset); - - sBtnLoading.setOnClickListener(this); - sBtnProgress.setOnClickListener(this); - btnSucceed.setOnClickListener(this); - btnFailed.setOnClickListener(this); - btnReset.setOnClickListener(this); - - mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (isChecked) { - sBtnLoading.setVisibility(View.GONE); - sBtnProgress.setVisibility(View.VISIBLE); - sBtnLoading.reset(); - } else { - sBtnLoading.setVisibility(View.VISIBLE); - sBtnProgress.setVisibility(View.GONE); - if (task != null && !task.isCancelled()) { - task.cancel(true); - sBtnProgress.reset(); - } - } - } - }); - } - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.sbtn_loading: - Toast.makeText(this, "SubmitButton be clicked", Toast.LENGTH_SHORT).show(); - break; - case R.id.sbtn_progress: - if (task == null || task.isCancelled()) { - task = new MyTask(); - task.execute(); - } - break; - case R.id.btn_succeed: - if (mSwitch.isChecked()) { - sBtnProgress.doResult(true); - } else { - sBtnLoading.doResult(true); - } - break; - case R.id.btn_failed: - if (mSwitch.isChecked()) { - sBtnProgress.doResult(false); - } else { - sBtnLoading.doResult(false); - } - break; - case R.id.btn_reset: - if (mSwitch.isChecked()) { - if (task != null && !task.isCancelled()) { - task.cancel(true); - sBtnProgress.reset(); - } - } else { - sBtnLoading.reset(); - } - break; - } - } - - private class MyTask extends AsyncTask { - - @Override - protected Boolean doInBackground(Void... params) { - int i = 0; - while (i <= 100) { - if (isCancelled()) { - return null; - } - try { - Thread.sleep(30); - } catch (InterruptedException e) { - e.printStackTrace(); - } - i++; - publishProgress(i); - } - return true; - } - - @Override - protected void onPostExecute(Boolean aBoolean) { - if (aBoolean == null) { - sBtnProgress.reset(); - } - sBtnProgress.doResult(aBoolean); - } - - @Override - protected void onProgressUpdate(Integer... values) { - sBtnProgress.setProgress(values[0]); - } - } -} +package com.unstoppable.submitbutton; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.view.accessibility.AccessibilityManager; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.Switch; +import android.widget.Toast; + +import com.unstoppable.submitbuttonview.SubmitButton; + +public class MainActivity extends AppCompatActivity implements View.OnClickListener { + + private SubmitButton sBtnLoading, sBtnProgress; + private Button btnSucceed, btnFailed, btnReset; + private Switch mSwitch; + private MyTask task; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + sBtnLoading = findViewById(R.id.sbtn_loading); + sBtnProgress = findViewById(R.id.sbtn_progress); + mSwitch = findViewById(R.id.switch1); + + btnFailed = findViewById(R.id.btn_failed); + btnSucceed = findViewById(R.id.btn_succeed); + btnReset = findViewById(R.id.btn_reset); + + sBtnLoading.setOnClickListener(this); + sBtnProgress.setOnClickListener(this); + btnSucceed.setOnClickListener(this); + btnFailed.setOnClickListener(this); + btnReset.setOnClickListener(this); + + mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + sBtnLoading.setVisibility(View.GONE); + sBtnProgress.setVisibility(View.VISIBLE); + sBtnLoading.reset(); + } else { + sBtnLoading.setVisibility(View.VISIBLE); + sBtnProgress.setVisibility(View.GONE); + if (task != null && !task.isCancelled()) { + task.cancel(true); + sBtnProgress.reset(); + } + } + } + }); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.sbtn_loading: + Toast.makeText(this, "SubmitButton be clicked", Toast.LENGTH_SHORT).show(); + break; + case R.id.sbtn_progress: + if (task == null || task.isCancelled()) { + task = new MyTask(); + task.execute(); + } + break; + case R.id.btn_succeed: + if (mSwitch.isChecked()) { + sBtnProgress.doResult(true); + } else { + sBtnLoading.doResult(true); + } + break; + case R.id.btn_failed: + if (mSwitch.isChecked()) { + sBtnProgress.doResult(false); + } else { + sBtnLoading.doResult(false); + } + break; + case R.id.btn_reset: + if (mSwitch.isChecked()) { + if (task != null && !task.isCancelled()) { + task.cancel(true); + sBtnProgress.reset(); + } + } else { + sBtnLoading.reset(); + } + break; + } + } + + private class MyTask extends AsyncTask { + + @Override + protected Boolean doInBackground(Void... params) { + int i = 0; + while (i <= 100) { + if (isCancelled()) { + return null; + } + try { + Thread.sleep(30); + } catch (InterruptedException e) { + e.printStackTrace(); + } + i++; + publishProgress(i); + } + return true; + } + + @Override + protected void onPostExecute(Boolean aBoolean) { + if (aBoolean == null) { + sBtnProgress.reset(); + } + sBtnProgress.doResult(aBoolean); + } + + @Override + protected void onProgressUpdate(Integer... values) { + sBtnProgress.setProgress(values[0]); + } + } +} diff --git a/build.gradle b/build.gradle index 843fc53..7af5374 100644 --- a/build.gradle +++ b/build.gradle @@ -1,30 +1,15 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' - classpath 'com.novoda:bintray-release:0.4.0' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - jcenter() - } - tasks.withType(Javadoc) { - options{ - encoding "UTF-8" - charSet 'UTF-8' - links "http://docs.oracle.com/javase/7/docs/api" - } - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + } +} + +allprojects { + repositories { + jcenter() + } +} \ No newline at end of file diff --git a/submitbuttonview/build.gradle b/submitbuttonview/build.gradle index 927fc91..ad1d36b 100644 --- a/submitbuttonview/build.gradle +++ b/submitbuttonview/build.gradle @@ -1,47 +1,39 @@ -apply plugin: 'com.android.library' -apply plugin: 'com.novoda.bintray-release' - -android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" - - defaultConfig { - minSdkVersion 15 - targetSdkVersion 25 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - lintOptions { - abortOnError false - } -} - -dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) - compile 'com.android.support:appcompat-v7:25.3.1' - testCompile 'junit:junit:4.12' -} - -publish { - userOrg = 'unstoppable' - groupId = 'com.unstoppable' - artifactId = 'submitbutton' - publishVersion = '1.1.2' - desc = 'It\'s a submit button with a fun animation for Android.' - website = 'https://github.com/Someonewow/SubmitButton' -} - +apply plugin: 'com.android.library' +apply plugin: 'com.github.dcendents.android-maven' + +group='com.github.searchy2' +version = '1.3' + +android { + compileSdkVersion 26 + buildToolsVersion "26.0.2" + + defaultConfig { + minSdkVersion 15 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + lintOptions { + abortOnError false + } +} + +dependencies { + repositories { + maven { url "https://maven.google.com" } + } + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:26.0.2' +} \ No newline at end of file diff --git a/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java b/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java index d873ba2..1297543 100644 --- a/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java +++ b/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java @@ -1,557 +1,565 @@ -package com.unstoppable.submitbuttonview; - -import android.animation.Animator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PathMeasure; -import android.graphics.Rect; -import android.graphics.RectF; -import android.support.v4.content.ContextCompat; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import android.view.animation.AccelerateInterpolator; - -/** - * Created by Unstoppable on 2016/12/31. - */ - -public class SubmitButton extends View { - - private static final int STATE_NONE = 0; - private static final int STATE_SUBMIT = 1; - private static final int STATE_LOADING = 2; - private static final int STATE_RESULT = 3; - - //view状态 - private int viewState = STATE_NONE; - - //View宽高 - private int mWidth; - private int mHeight; - - private int MAX_WIDTH; - private int MAX_HEIGHT; - - //画布坐标原点 - private int x, y; - - private String buttonText = ""; - private int buttonColor; - private int succeedColor; - private int failedColor; - private int textSize; - - //文本宽高 - private int textWidth; - private int textHeight; - - private Paint bgPaint, loadingPaint, resultPaint, textPaint; - - private Path buttonPath; - private Path loadPath; - private Path dst; - private PathMeasure pathMeasure; - private Path resultPath; - - private RectF circleLeft, circleMid, circleRight; - - private float loadValue; - - private ValueAnimator submitAnim, loadingAnim, resultAnim; - - private boolean isDoResult; - private boolean isSucceed; - - private static final int STYLE_LOADING = 0; - private static final int STYLE_PROGRESS = 1; - - //view加载等待模式 - private int progressStyle = STYLE_LOADING; - private float currentProgress; - - private OnResultEndListener listener; - - public SubmitButton(Context context) { - this(context, null); - } - - public SubmitButton(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public SubmitButton(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SubmitButton, defStyleAttr, 0); - if (typedArray.getString(R.styleable.SubmitButton_buttonText) != null) { - buttonText = typedArray.getString(R.styleable.SubmitButton_buttonText); - } - buttonColor = typedArray.getColor(R.styleable.SubmitButton_buttonColor, Color.parseColor("#19CC95")); - succeedColor = typedArray.getColor(R.styleable.SubmitButton_succeedColor, Color.parseColor("#19CC95")); - failedColor = typedArray.getColor(R.styleable.SubmitButton_failedColor, Color.parseColor("#FC8E34")); - textSize = (int) typedArray.getDimension(R.styleable.SubmitButton_buttonTextSize, sp2px(15)); - progressStyle = typedArray.getInt(R.styleable.SubmitButton_progressStyle, STYLE_LOADING); - typedArray.recycle(); - //关闭硬件加速 - this.setLayerType(LAYER_TYPE_SOFTWARE,null); - init(); - } - - /** - * 初始化Paint、Path - */ - private void init() { - bgPaint = new Paint(); - bgPaint.setColor(buttonColor); - bgPaint.setStyle(Paint.Style.STROKE); - bgPaint.setStrokeWidth(5); - bgPaint.setAntiAlias(true); - - loadingPaint = new Paint(); - loadingPaint.setColor(buttonColor); - loadingPaint.setStyle(Paint.Style.STROKE); - loadingPaint.setStrokeWidth(9); - loadingPaint.setAntiAlias(true); - - resultPaint = new Paint(); - resultPaint.setColor(Color.WHITE); - resultPaint.setStyle(Paint.Style.STROKE); - resultPaint.setStrokeWidth(9); - resultPaint.setStrokeCap(Paint.Cap.ROUND); - resultPaint.setAntiAlias(true); - - textPaint = new Paint(); - textPaint.setColor(buttonColor); - textPaint.setStrokeWidth(textSize / 6); - textPaint.setTextSize(textSize); - textPaint.setAntiAlias(true); - - textWidth = getTextWidth(textPaint, buttonText); - textHeight = getTextHeight(textPaint, buttonText); - - buttonPath = new Path(); - loadPath = new Path(); - resultPath = new Path(); - dst = new Path(); - circleMid = new RectF(); - circleLeft = new RectF(); - circleRight = new RectF(); - pathMeasure = new PathMeasure(); - } - - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); - - if (widthMode == MeasureSpec.AT_MOST) { - widthSize = textWidth + 100; - } - - if (heightMode == MeasureSpec.AT_MOST) { - heightSize = (int) (textHeight * 2.5); - } - - if (heightSize > widthSize) { - heightSize = (int) (widthSize * 0.25); - } - - mWidth = widthSize - 10; - mHeight = heightSize - 10; - x = (int) (widthSize * 0.5); - y = (int) (heightSize * 0.5); - MAX_WIDTH = mWidth; - MAX_HEIGHT = mHeight; - - setMeasuredDimension(widthSize, heightSize); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - canvas.translate(x, y); - drawButton(canvas); - if (viewState == STATE_NONE || viewState == STATE_SUBMIT && mWidth > textWidth) { - drawButtonText(canvas); - } - if (viewState == STATE_LOADING) { - drawLoading(canvas); - } - if (viewState == STATE_RESULT) { - drawResult(canvas, isSucceed); - } - } - - /** - * 绘制初始状态Button - * - * @param canvas 画布 - */ - private void drawButton(Canvas canvas) { - buttonPath.reset(); - circleLeft.set(-mWidth / 2, -mHeight / 2, -mWidth / 2 + mHeight, mHeight / 2); - buttonPath.arcTo(circleLeft, 90, 180); - buttonPath.lineTo(mWidth / 2 - mHeight / 2, -mHeight / 2); - circleRight.set(mWidth / 2 - mHeight, -mHeight / 2, mWidth / 2, mHeight / 2); - buttonPath.arcTo(circleRight, 270, 180); - buttonPath.lineTo(-mWidth / 2 + mHeight / 2, mHeight / 2); - canvas.drawPath(buttonPath, bgPaint); - } - - /** - * 绘制加载状态Button - * - * @param canvas 画布 - */ - private void drawLoading(Canvas canvas) { - dst.reset(); - circleMid.set(-MAX_HEIGHT / 2, -MAX_HEIGHT / 2, MAX_HEIGHT / 2, MAX_HEIGHT / 2); - loadPath.addArc(circleMid, 270, 359.999f); - pathMeasure.setPath(loadPath, true); - float startD = 0f, stopD; - if (progressStyle == STYLE_LOADING) { - startD = pathMeasure.getLength() * loadValue; - stopD = startD + pathMeasure.getLength() / 2 * loadValue; - } else { - stopD = pathMeasure.getLength() * currentProgress; - } - pathMeasure.getSegment(startD, stopD, dst, true); - canvas.drawPath(dst, loadingPaint); - } - - /** - * 绘制结果状态Button - * - * @param canvas 画布 - */ - private void drawResult(Canvas canvas, boolean isSucceed) { - if (isSucceed) { - resultPath.moveTo(-mHeight / 6, 0); - resultPath.lineTo(0, (float) (-mHeight / 6 + (1 + Math.sqrt(5)) * mHeight / 12)); - resultPath.lineTo(mHeight / 6, -mHeight / 6); - } else { - resultPath.moveTo(-mHeight / 6, mHeight / 6); - resultPath.lineTo(mHeight / 6, -mHeight / 6); - resultPath.moveTo(-mHeight / 6, -mHeight / 6); - resultPath.lineTo(mHeight / 6, mHeight / 6); - } - canvas.drawPath(resultPath, resultPaint); - } - - /** - * 绘制Button文本 - * - * @param canvas 画布 - */ - private void drawButtonText(Canvas canvas) { - textPaint.setAlpha(((mWidth - textWidth) * 255) / (MAX_WIDTH - textWidth)); - canvas.drawText(buttonText, -textWidth / 2, getTextBaseLineOffset(), textPaint); - } - - /** - * 开始提交动画 - */ - private void startSubmitAnim() { - viewState = STATE_SUBMIT; - submitAnim = new ValueAnimator().ofInt(MAX_WIDTH, MAX_HEIGHT); - submitAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mWidth = (int) animation.getAnimatedValue(); - if (mWidth == mHeight) { - bgPaint.setColor(Color.parseColor("#DDDDDD")); - } - invalidate(); - } - }); - submitAnim.setDuration(300); - submitAnim.setInterpolator(new AccelerateInterpolator()); - submitAnim.start(); - submitAnim.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - - } - - @Override - public void onAnimationEnd(Animator animation) { - if (isDoResult) { - startResultAnim(); - } else { - startLoadingAnim(); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } - }); - } - - /** - * 开始加载动画 - */ - private void startLoadingAnim() { - viewState = STATE_LOADING; - if (progressStyle == STYLE_PROGRESS) { - return; - } - loadingAnim = new ValueAnimator().ofFloat(0.0f, 1.0f); - loadingAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - loadValue = (float) animation.getAnimatedValue(); - invalidate(); - } - }); - loadingAnim.setDuration(2000); - loadingAnim.setRepeatCount(ValueAnimator.INFINITE); - loadingAnim.start(); - } - - /** - * 开始结果动画 - */ - private void startResultAnim() { - viewState = STATE_RESULT; - if (loadingAnim != null) { - loadingAnim.cancel(); - } - resultAnim = new ValueAnimator().ofInt(MAX_HEIGHT, MAX_WIDTH); - resultAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mWidth = (int) animation.getAnimatedValue(); - resultPaint.setAlpha(((mWidth - mHeight) * 255) / (MAX_WIDTH - MAX_HEIGHT)); - if (mWidth == mHeight) { - if (isSucceed) { - bgPaint.setColor(succeedColor); - } else { - bgPaint.setColor(failedColor); - } - bgPaint.setStyle(Paint.Style.FILL_AND_STROKE); - } - invalidate(); - } - }); - resultAnim.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - - } - - @Override - public void onAnimationEnd(Animator animation) { - if (listener == null) { - return; - } - postDelayed(new Runnable() { - @Override - public void run() { - listener.onResultEnd(); - } - }, 500); - } - - @Override - public void onAnimationCancel(Animator animation) { - - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } - }); - resultAnim.setDuration(300); - resultAnim.setInterpolator(new AccelerateInterpolator()); - resultAnim.start(); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_UP: - if (viewState == STATE_NONE) { - startSubmitAnim(); - } - } - return super.onTouchEvent(event); - } - - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (submitAnim != null) { - submitAnim.cancel(); - } - if (loadingAnim != null) { - loadingAnim.cancel(); - } - if (resultAnim != null) { - resultAnim.cancel(); - } - } - - /** - * 设置submit结果 - * - * @param isSucceed 是否成功 - */ - public void doResult(boolean isSucceed) { - if (viewState == STATE_NONE || viewState == STATE_RESULT || isDoResult) { - return; - } - isDoResult = true; - this.isSucceed = isSucceed; - if (viewState == STATE_LOADING) { - startResultAnim(); - } - } - - /** - * 恢复初始化Button状态 - */ - public void reset() { - if (submitAnim != null) { - submitAnim.cancel(); - } - if (loadingAnim != null) { - loadingAnim.cancel(); - } - if (resultAnim != null) { - resultAnim.cancel(); - } - viewState = STATE_NONE; - mWidth = MAX_WIDTH; - mHeight = MAX_HEIGHT; - isSucceed = false; - isDoResult = false; - currentProgress = 0; - init(); - invalidate(); - } - - /** - * Set Button to Result State - * - * @param success true = result success; false = failure - */ - public void setResult(boolean success) - { - isSucceed = success; - viewState = STATE_RESULT; - if (loadingAnim != null) { - loadingAnim.cancel(); - } - if (isSucceed) { - bgPaint.setColor(succeedColor); - } else { - bgPaint.setColor(failedColor); - } - bgPaint.setStyle(Paint.Style.FILL_AND_STROKE); - resultPaint.setAlpha(255); - invalidate(); - } - - /** - * 设置进度 - * - * @param progress 进度值 (0-100) - */ - public void setProgress(int progress) { - if (progress < 0 || progress > 100) { - return; - } - currentProgress = (float) (progress * 0.01); - if (progressStyle == STYLE_PROGRESS && viewState == STATE_LOADING) { - invalidate(); - } - } - - /** - * 设置动画结束回调接口 - * - * @param listener - */ - public void setOnResultEndListener(OnResultEndListener listener) { - this.listener = listener; - } - - /** - * 结果动画结束回调接口 - */ - public interface OnResultEndListener { - void onResultEnd(); - } - - /** - * sp to dp - * - * @param sp - * @return dp - */ - private int sp2px(float sp) { - final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity; - return (int) (sp * fontScale + 0.5f); - } - - /** - * 计算水平居中的baseline - * - * @return - */ - private float getTextBaseLineOffset() { - Paint.FontMetrics fm = textPaint.getFontMetrics(); - return -(fm.bottom + fm.top) / 2; - } - - /** - * 获取Text高度 - * - * @param paint - * @param str 文本内容 - * @return - */ - private int getTextHeight(Paint paint, String str) { - Rect rect = new Rect(); - paint.getTextBounds(str, 0, str.length(), rect); - return rect.height(); - } - - /** - * 获取Text宽度 - * - * @param paint - * @param str 文本内容 - * @return - */ - private int getTextWidth(Paint paint, String str) { - int mRet = 0; - if (str != null && str.length() > 0) { - int len = str.length(); - float[] widths = new float[len]; - paint.getTextWidths(str, widths); - for (int j = 0; j < len; j++) { - mRet += (int) Math.ceil(widths[j]); - } - } - return mRet; - } +package com.unstoppable.submitbuttonview; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PathMeasure; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.AccelerateInterpolator; + +/** + * Created by Unstoppable on 2016/12/31. + */ + +public class SubmitButton extends View { + + private static final int STATE_NONE = 0; + private static final int STATE_SUBMIT = 1; + private static final int STATE_LOADING = 2; + private static final int STATE_RESULT = 3; + + //view状态 + private int viewState = STATE_NONE; + + //View宽高 + private int mWidth; + private int mHeight; + + private int MAX_WIDTH; + private int MAX_HEIGHT; + + //画布坐标原点 + private int x, y; + + private String buttonText = ""; + private int buttonColor; + private int succeedColor; + private int failedColor; + private int textSize; + + //文本宽高 + private int textWidth; + private int textHeight; + + private Paint bgPaint, loadingPaint, resultPaint, textPaint; + + private Path buttonPath; + private Path loadPath; + private Path dst; + private PathMeasure pathMeasure; + private Path resultPath; + + private RectF circleLeft, circleMid, circleRight; + + private float loadValue; + + private ValueAnimator submitAnim, loadingAnim, resultAnim; + + private boolean isDoResult; + private boolean isSucceed; + + private static final int STYLE_LOADING = 0; + private static final int STYLE_PROGRESS = 1; + + //view加载等待模式 + private int progressStyle = STYLE_LOADING; + private float currentProgress; + + private OnResultEndListener listener; + + public SubmitButton(Context context) { + this(context, null); + } + + public SubmitButton(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SubmitButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SubmitButton, defStyleAttr, 0); + if (typedArray.getString(R.styleable.SubmitButton_buttonText) != null) { + buttonText = typedArray.getString(R.styleable.SubmitButton_buttonText); + } + buttonColor = typedArray.getColor(R.styleable.SubmitButton_buttonColor, Color.parseColor("#19CC95")); + succeedColor = typedArray.getColor(R.styleable.SubmitButton_succeedColor, Color.parseColor("#19CC95")); + failedColor = typedArray.getColor(R.styleable.SubmitButton_failedColor, Color.parseColor("#FC8E34")); + textSize = (int) typedArray.getDimension(R.styleable.SubmitButton_buttonTextSize, sp2px(15)); + progressStyle = typedArray.getInt(R.styleable.SubmitButton_progressStyle, STYLE_LOADING); + typedArray.recycle(); + //关闭硬件加速 + this.setLayerType(LAYER_TYPE_SOFTWARE,null); + init(); + } + + /** + * 初始化Paint、Path + */ + private void init() { + bgPaint = new Paint(); + bgPaint.setColor(buttonColor); + bgPaint.setStyle(Paint.Style.STROKE); + bgPaint.setStrokeWidth(5); + bgPaint.setAntiAlias(true); + + loadingPaint = new Paint(); + loadingPaint.setColor(buttonColor); + loadingPaint.setStyle(Paint.Style.STROKE); + loadingPaint.setStrokeWidth(9); + loadingPaint.setAntiAlias(true); + + resultPaint = new Paint(); + resultPaint.setColor(Color.WHITE); + resultPaint.setStyle(Paint.Style.STROKE); + resultPaint.setStrokeWidth(9); + resultPaint.setStrokeCap(Paint.Cap.ROUND); + resultPaint.setAntiAlias(true); + + textPaint = new Paint(); + textPaint.setColor(buttonColor); + textPaint.setStrokeWidth(textSize / 6); + textPaint.setTextSize(textSize); + textPaint.setAntiAlias(true); + + textWidth = getTextWidth(textPaint, buttonText); + textHeight = getTextHeight(textPaint, buttonText); + + buttonPath = new Path(); + loadPath = new Path(); + resultPath = new Path(); + dst = new Path(); + circleMid = new RectF(); + circleLeft = new RectF(); + circleRight = new RectF(); + pathMeasure = new PathMeasure(); + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + if (widthMode == MeasureSpec.AT_MOST) { + widthSize = textWidth + 100; + } + + if (heightMode == MeasureSpec.AT_MOST) { + heightSize = (int) (textHeight * 2.5); + } + + if (heightSize > widthSize) { + heightSize = (int) (widthSize * 0.25); + } + + mWidth = widthSize - 10; + mHeight = heightSize - 10; + x = (int) (widthSize * 0.5); + y = (int) (heightSize * 0.5); + MAX_WIDTH = mWidth; + MAX_HEIGHT = mHeight; + + setMeasuredDimension(widthSize, heightSize); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.translate(x, y); + drawButton(canvas); + if (viewState == STATE_NONE || viewState == STATE_SUBMIT && mWidth > textWidth) { + drawButtonText(canvas); + } + if (viewState == STATE_LOADING) { + drawLoading(canvas); + } + if (viewState == STATE_RESULT) { + drawResult(canvas, isSucceed); + } + } + + /** + * 绘制初始状态Button + * + * @param canvas 画布 + */ + private void drawButton(Canvas canvas) { + buttonPath.reset(); + circleLeft.set(-mWidth / 2, -mHeight / 2, -mWidth / 2 + mHeight, mHeight / 2); + buttonPath.arcTo(circleLeft, 90, 180); + buttonPath.lineTo(mWidth / 2 - mHeight / 2, -mHeight / 2); + circleRight.set(mWidth / 2 - mHeight, -mHeight / 2, mWidth / 2, mHeight / 2); + buttonPath.arcTo(circleRight, 270, 180); + buttonPath.lineTo(-mWidth / 2 + mHeight / 2, mHeight / 2); + canvas.drawPath(buttonPath, bgPaint); + } + + /** + * 绘制加载状态Button + * + * @param canvas 画布 + */ + private void drawLoading(Canvas canvas) { + dst.reset(); + circleMid.set(-MAX_HEIGHT / 2, -MAX_HEIGHT / 2, MAX_HEIGHT / 2, MAX_HEIGHT / 2); + loadPath.addArc(circleMid, 270, 359.999f); + pathMeasure.setPath(loadPath, true); + float startD = 0f, stopD; + if (progressStyle == STYLE_LOADING) { + startD = pathMeasure.getLength() * loadValue; + stopD = startD + pathMeasure.getLength() / 2 * loadValue; + } else { + stopD = pathMeasure.getLength() * currentProgress; + } + pathMeasure.getSegment(startD, stopD, dst, true); + canvas.drawPath(dst, loadingPaint); + } + + /** + * 绘制结果状态Button + * + * @param canvas 画布 + */ + private void drawResult(Canvas canvas, boolean isSucceed) { + if (isSucceed) { + resultPath.moveTo(-mHeight / 6, 0); + resultPath.lineTo(0, (float) (-mHeight / 6 + (1 + Math.sqrt(5)) * mHeight / 12)); + resultPath.lineTo(mHeight / 6, -mHeight / 6); + } else { + resultPath.moveTo(-mHeight / 6, mHeight / 6); + resultPath.lineTo(mHeight / 6, -mHeight / 6); + resultPath.moveTo(-mHeight / 6, -mHeight / 6); + resultPath.lineTo(mHeight / 6, mHeight / 6); + } + canvas.drawPath(resultPath, resultPaint); + } + + /** + * 绘制Button文本 + * + * @param canvas 画布 + */ + private void drawButtonText(Canvas canvas) { + textPaint.setAlpha(((mWidth - textWidth) * 255) / (MAX_WIDTH - textWidth)); + canvas.drawText(buttonText, -textWidth / 2, getTextBaseLineOffset(), textPaint); + } + + /** + * 开始提交动画 + */ + private void startSubmitAnim() { + viewState = STATE_SUBMIT; + submitAnim = new ValueAnimator().ofInt(MAX_WIDTH, MAX_HEIGHT); + submitAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mWidth = (int) animation.getAnimatedValue(); + if (mWidth == mHeight) { + bgPaint.setColor(Color.parseColor("#DDDDDD")); + } + invalidate(); + } + }); + submitAnim.setDuration(300); + submitAnim.setInterpolator(new AccelerateInterpolator()); + submitAnim.start(); + submitAnim.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + if (isDoResult) { + startResultAnim(); + } else { + startLoadingAnim(); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + } + + /** + * 开始加载动画 + */ + private void startLoadingAnim() { + viewState = STATE_LOADING; + if (progressStyle == STYLE_PROGRESS) { + return; + } + loadingAnim = new ValueAnimator().ofFloat(0.0f, 1.0f); + loadingAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + loadValue = (float) animation.getAnimatedValue(); + invalidate(); + } + }); + loadingAnim.setDuration(2000); + loadingAnim.setRepeatCount(ValueAnimator.INFINITE); + loadingAnim.start(); + } + + /** + * 开始结果动画 + */ + private void startResultAnim() { + viewState = STATE_RESULT; + if (loadingAnim != null) { + loadingAnim.cancel(); + } + resultAnim = new ValueAnimator().ofInt(MAX_HEIGHT, MAX_WIDTH); + resultAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mWidth = (int) animation.getAnimatedValue(); + resultPaint.setAlpha(((mWidth - mHeight) * 255) / (MAX_WIDTH - MAX_HEIGHT)); + if (mWidth == mHeight) { + if (isSucceed) { + bgPaint.setColor(succeedColor); + } else { + bgPaint.setColor(failedColor); + } + bgPaint.setStyle(Paint.Style.FILL_AND_STROKE); + } + invalidate(); + } + }); + resultAnim.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + if (listener == null) { + return; + } + postDelayed(new Runnable() { + @Override + public void run() { + listener.onResultEnd(); + } + }, 500); + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + resultAnim.setDuration(300); + resultAnim.setInterpolator(new AccelerateInterpolator()); + resultAnim.start(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_UP: + if (viewState == STATE_NONE) { + startSubmitAnim(); + } + } + return super.onTouchEvent(event); + } + + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (submitAnim != null) { + submitAnim.cancel(); + } + if (loadingAnim != null) { + loadingAnim.cancel(); + } + if (resultAnim != null) { + resultAnim.cancel(); + } + } + + /** + * 设置submit结果 + * + * @param isSucceed 是否成功 + */ + public void doResult(boolean isSucceed) { + if (viewState == STATE_NONE || viewState == STATE_RESULT || isDoResult) { + return; + } + isDoResult = true; + this.isSucceed = isSucceed; + if (viewState == STATE_LOADING) { + startResultAnim(); + } + } + + /** + * 恢复初始化Button状态 + */ + public void reset() { + if (submitAnim != null) { + submitAnim.cancel(); + } + if (loadingAnim != null) { + loadingAnim.cancel(); + } + if (resultAnim != null) { + resultAnim.cancel(); + } + viewState = STATE_NONE; + mWidth = MAX_WIDTH; + mHeight = MAX_HEIGHT; + isSucceed = false; + isDoResult = false; + currentProgress = 0; + init(); + invalidate(); + } + + /** + * Set Button to Result State + * + * @param success true = result success; false = failure + */ + public void setResult(boolean success) + { + isSucceed = success; + viewState = STATE_RESULT; + if (loadingAnim != null) { + loadingAnim.cancel(); + } + if (isSucceed) { + bgPaint.setColor(succeedColor); + } else { + bgPaint.setColor(failedColor); + } + bgPaint.setStyle(Paint.Style.FILL_AND_STROKE); + resultPaint.setAlpha(255); + invalidate(); + } + + /** + * Set Button to Loading State + */ + public void setLoading() + { + viewState = STATE_LOADING; + mWidth = mHeight; + startLoadingAnim(); + } + + /** + * 设置进度 + * + * @param progress 进度值 (0-100) + */ + public void setProgress(int progress) { + if (progress < 0 || progress > 100) { + return; + } + currentProgress = (float) (progress * 0.01); + if (progressStyle == STYLE_PROGRESS && viewState == STATE_LOADING) { + invalidate(); + } + } + + /** + * 设置动画结束回调接口 + * + * @param listener + */ + public void setOnResultEndListener(OnResultEndListener listener) { + this.listener = listener; + } + + /** + * 结果动画结束回调接口 + */ + public interface OnResultEndListener { + void onResultEnd(); + } + + /** + * sp to dp + * + * @param sp + * @return dp + */ + private int sp2px(float sp) { + final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity; + return (int) (sp * fontScale + 0.5f); + } + + /** + * 计算水平居中的baseline + * + * @return + */ + private float getTextBaseLineOffset() { + Paint.FontMetrics fm = textPaint.getFontMetrics(); + return -(fm.bottom + fm.top) / 2; + } + + /** + * 获取Text高度 + * + * @param paint + * @param str 文本内容 + * @return + */ + private int getTextHeight(Paint paint, String str) { + Rect rect = new Rect(); + paint.getTextBounds(str, 0, str.length(), rect); + return rect.height(); + } + + /** + * 获取Text宽度 + * + * @param paint + * @param str 文本内容 + * @return + */ + private int getTextWidth(Paint paint, String str) { + int mRet = 0; + if (str != null && str.length() > 0) { + int len = str.length(); + float[] widths = new float[len]; + paint.getTextWidths(str, widths); + for (int j = 0; j < len; j++) { + mRet += (int) Math.ceil(widths[j]); + } + } + return mRet; + } } \ No newline at end of file From ee4599625ae9f2bc6cd3a4435f8651b4e8fa4aa1 Mon Sep 17 00:00:00 2001 From: Ray Li Date: Sat, 28 Oct 2017 11:31:24 -0500 Subject: [PATCH 04/11] Update Gradle v3.0.0 *Update Gradle to v3.0.0. *Update Dependencies to v26.1.0. *Version bump to v1.4. --- app/build.gradle | 3 +-- build.gradle | 3 ++- gradle/wrapper/gradle-wrapper.properties | 12 ++++++------ submitbuttonview/build.gradle | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d1ceed1..e92ce0c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,6 +25,5 @@ dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile project(':submitbuttonview') - compile 'com.android.support:appcompat-v7:26.0.2' - compile 'com.android.support.constraint:constraint-layout:1.0.2' + compile 'com.android.support:appcompat-v7:26.1.0' } diff --git a/build.gradle b/build.gradle index 7af5374..8cbbc0a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,10 @@ buildscript { repositories { jcenter() + maven { url "https://maven.google.com" } } dependencies { - classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.android.tools.build:gradle:3.0.0' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 16176e9..e893bd2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Apr 18 09:09:14 CST 2017 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +#Sat Oct 28 10:51:25 CDT 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/submitbuttonview/build.gradle b/submitbuttonview/build.gradle index ad1d36b..0aabd77 100644 --- a/submitbuttonview/build.gradle +++ b/submitbuttonview/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' group='com.github.searchy2' -version = '1.3' +version = '1.4' android { compileSdkVersion 26 @@ -35,5 +35,5 @@ dependencies { maven { url "https://maven.google.com" } } compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:26.0.2' + compile 'com.android.support:appcompat-v7:26.1.0' } \ No newline at end of file From 859a7ca80b1b2ed7ba61876e63d7b0474a3b4e38 Mon Sep 17 00:00:00 2001 From: Ray Li Date: Wed, 6 Dec 2017 15:09:45 -0600 Subject: [PATCH 05/11] Update Build Tools v27.0.2 *Update Build Tools and dependencies to v27.0.2. *Update Gradle to v3.0.1. --- app/build.gradle | 8 ++++---- build.gradle | 2 +- submitbuttonview/build.gradle | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e92ce0c..d2d3e79 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 26 - buildToolsVersion "26.0.2" + compileSdkVersion 27 + buildToolsVersion "27.0.1" defaultConfig { applicationId "com.unstoppable.submitbutton" minSdkVersion 15 - targetSdkVersion 26 + targetSdkVersion 27 versionCode 1 versionName "1.0" } @@ -25,5 +25,5 @@ dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile project(':submitbuttonview') - compile 'com.android.support:appcompat-v7:26.1.0' + compile 'com.android.support:appcompat-v7:27.0.2' } diff --git a/build.gradle b/build.gradle index 8cbbc0a..3a8ebc2 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { maven { url "https://maven.google.com" } } dependencies { - classpath 'com.android.tools.build:gradle:3.0.0' + classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' } } diff --git a/submitbuttonview/build.gradle b/submitbuttonview/build.gradle index 0aabd77..7bd453e 100644 --- a/submitbuttonview/build.gradle +++ b/submitbuttonview/build.gradle @@ -2,15 +2,15 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' group='com.github.searchy2' -version = '1.4' +version = '1.5' android { - compileSdkVersion 26 - buildToolsVersion "26.0.2" + compileSdkVersion 27 + buildToolsVersion "27.0.1" defaultConfig { minSdkVersion 15 - targetSdkVersion 26 + targetSdkVersion 27 versionCode 1 versionName "1.0" @@ -35,5 +35,5 @@ dependencies { maven { url "https://maven.google.com" } } compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:26.1.0' + compile 'com.android.support:appcompat-v7:27.0.2' } \ No newline at end of file From b59b63fbcf4017ab3ba5557b4aeb25e9ce380466 Mon Sep 17 00:00:00 2001 From: Ray Li Date: Wed, 6 Dec 2017 15:22:31 -0600 Subject: [PATCH 06/11] QUICKFIX Gradle Build *QUICKFIX Gradle Building. *Remove test files. *Version Bump to v1.5.1. --- app/build.gradle | 3 --- .../submitbutton/ExampleInstrumentedTest.java | 26 ------------------- .../submitbutton/ExampleUnitTest.java | 17 ------------ build.gradle | 1 + submitbuttonview/build.gradle | 8 +----- .../ExampleInstrumentedTest.java | 26 ------------------- .../submitbuttonview/ExampleUnitTest.java | 17 ------------ 7 files changed, 2 insertions(+), 96 deletions(-) delete mode 100644 app/src/androidTest/java/com/unstoppable/submitbutton/ExampleInstrumentedTest.java delete mode 100644 app/src/test/java/com/unstoppable/submitbutton/ExampleUnitTest.java delete mode 100644 submitbuttonview/src/androidTest/java/com/unstoppable/submitbuttonview/ExampleInstrumentedTest.java delete mode 100644 submitbuttonview/src/test/java/com/unstoppable/submitbuttonview/ExampleUnitTest.java diff --git a/app/build.gradle b/app/build.gradle index d2d3e79..ca1ee49 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,9 +19,6 @@ android { } dependencies { - repositories { - maven { url "https://maven.google.com" } - } compile fileTree(include: ['*.jar'], dir: 'libs') compile project(':submitbuttonview') diff --git a/app/src/androidTest/java/com/unstoppable/submitbutton/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/unstoppable/submitbutton/ExampleInstrumentedTest.java deleted file mode 100644 index d9e8bb3..0000000 --- a/app/src/androidTest/java/com/unstoppable/submitbutton/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.unstoppable.submitbutton; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumentation test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.unstoppable.submitbutton", appContext.getPackageName()); - } -} diff --git a/app/src/test/java/com/unstoppable/submitbutton/ExampleUnitTest.java b/app/src/test/java/com/unstoppable/submitbutton/ExampleUnitTest.java deleted file mode 100644 index d464d6b..0000000 --- a/app/src/test/java/com/unstoppable/submitbutton/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.unstoppable.submitbutton; - -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() throws Exception { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 3a8ebc2..6786120 100644 --- a/build.gradle +++ b/build.gradle @@ -12,5 +12,6 @@ buildscript { allprojects { repositories { jcenter() + maven { url "https://maven.google.com" } } } \ No newline at end of file diff --git a/submitbuttonview/build.gradle b/submitbuttonview/build.gradle index 7bd453e..513626a 100644 --- a/submitbuttonview/build.gradle +++ b/submitbuttonview/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' group='com.github.searchy2' -version = '1.5' +version = '1.5.1' android { compileSdkVersion 27 @@ -13,9 +13,6 @@ android { targetSdkVersion 27 versionCode 1 versionName "1.0" - - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } buildTypes { @@ -31,9 +28,6 @@ android { } dependencies { - repositories { - maven { url "https://maven.google.com" } - } compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:27.0.2' } \ No newline at end of file diff --git a/submitbuttonview/src/androidTest/java/com/unstoppable/submitbuttonview/ExampleInstrumentedTest.java b/submitbuttonview/src/androidTest/java/com/unstoppable/submitbuttonview/ExampleInstrumentedTest.java deleted file mode 100644 index eaa6ac7..0000000 --- a/submitbuttonview/src/androidTest/java/com/unstoppable/submitbuttonview/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.unstoppable.submitbuttonview; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumentation test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.unstoppable.submitbuttonview.test", appContext.getPackageName()); - } -} diff --git a/submitbuttonview/src/test/java/com/unstoppable/submitbuttonview/ExampleUnitTest.java b/submitbuttonview/src/test/java/com/unstoppable/submitbuttonview/ExampleUnitTest.java deleted file mode 100644 index bb8de15..0000000 --- a/submitbuttonview/src/test/java/com/unstoppable/submitbuttonview/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.unstoppable.submitbuttonview; - -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() throws Exception { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file From b2180a915997459043a869d7d8826002d08f83c9 Mon Sep 17 00:00:00 2001 From: Ray Li Date: Thu, 1 Feb 2018 18:12:32 -0600 Subject: [PATCH 07/11] SetText Creation *Create method to set button text. *Update Gradle and BuildTools to v27.0.2. --- app/build.gradle | 2 +- build.gradle | 2 +- submitbuttonview/build.gradle | 2 +- .../submitbuttonview/SubmitButton.java | 15 +++++++++++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ca1ee49..ca1cd10 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 27 - buildToolsVersion "27.0.1" + buildToolsVersion "27.0.2" defaultConfig { applicationId "com.unstoppable.submitbutton" minSdkVersion 15 diff --git a/build.gradle b/build.gradle index 6786120..ea3bda8 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' } } diff --git a/submitbuttonview/build.gradle b/submitbuttonview/build.gradle index 513626a..bce7733 100644 --- a/submitbuttonview/build.gradle +++ b/submitbuttonview/build.gradle @@ -6,7 +6,7 @@ version = '1.5.1' android { compileSdkVersion 27 - buildToolsVersion "27.0.1" + buildToolsVersion "27.0.2" defaultConfig { minSdkVersion 15 diff --git a/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java b/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java index 1297543..97dfac5 100644 --- a/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java +++ b/submitbuttonview/src/main/java/com/unstoppable/submitbuttonview/SubmitButton.java @@ -493,6 +493,21 @@ public void setProgress(int progress) { } } + /** + * Set Button text. Redraws button if text is visible. + * + * @param text Text to display on button. + */ + public void setText(String text) + { + buttonText = text; + if (viewState == STATE_NONE) + { + init(); + invalidate(); + } + } + /** * 设置动画结束回调接口 * From 5dfab3566dedb8bc132fc09ee9fa2373ad3fc11a Mon Sep 17 00:00:00 2001 From: Ray Li Date: Thu, 8 Mar 2018 11:11:20 -0600 Subject: [PATCH 08/11] Update Build Tools v27.0.3 *Update Build Tools to v27.0.3. *Update Dependencies to v27.1.0. --- app/build.gradle | 4 ++-- submitbuttonview/build.gradle | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ca1cd10..f9e7d54 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 27 - buildToolsVersion "27.0.2" + buildToolsVersion "27.0.3" defaultConfig { applicationId "com.unstoppable.submitbutton" minSdkVersion 15 @@ -22,5 +22,5 @@ dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile project(':submitbuttonview') - compile 'com.android.support:appcompat-v7:27.0.2' + compile 'com.android.support:appcompat-v7:27.1.0' } diff --git a/submitbuttonview/build.gradle b/submitbuttonview/build.gradle index bce7733..837205d 100644 --- a/submitbuttonview/build.gradle +++ b/submitbuttonview/build.gradle @@ -2,11 +2,11 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' group='com.github.searchy2' -version = '1.5.1' +version = '1.7' android { compileSdkVersion 27 - buildToolsVersion "27.0.2" + buildToolsVersion "27.0.3" defaultConfig { minSdkVersion 15 @@ -29,5 +29,5 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:27.0.2' + compile 'com.android.support:appcompat-v7:27.1.0' } \ No newline at end of file From 4d51b106eb928b1246ccafd167996c036a3ad028 Mon Sep 17 00:00:00 2001 From: Ray Li Date: Sun, 1 Jul 2018 14:48:41 -0500 Subject: [PATCH 09/11] Update AndroidX *Update to AndroidX. *Update Gradle to v3.2.0-beta02. *TargetSDK v28. --- app/build.gradle | 10 +++++----- .../com/unstoppable/submitbutton/MainActivity.java | 2 +- build.gradle | 3 ++- gradle.properties | 2 ++ gradle/wrapper/gradle-wrapper.properties | 2 +- submitbuttonview/build.gradle | 8 ++++---- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f9e7d54..02a9c97 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 27 + compileSdkVersion 28 buildToolsVersion "27.0.3" defaultConfig { applicationId "com.unstoppable.submitbutton" minSdkVersion 15 - targetSdkVersion 27 + targetSdkVersion 28 versionCode 1 versionName "1.0" } @@ -19,8 +19,8 @@ android { } dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') - compile project(':submitbuttonview') + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation project(':submitbuttonview') - compile 'com.android.support:appcompat-v7:27.1.0' + implementation 'androidx.appcompat:appcompat:1.0.0-alpha3' } diff --git a/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java b/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java index f65e891..e5fe06e 100644 --- a/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java +++ b/app/src/main/java/com/unstoppable/submitbutton/MainActivity.java @@ -2,7 +2,7 @@ import android.os.AsyncTask; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import android.view.View; import android.view.accessibility.AccessibilityManager; import android.widget.Button; diff --git a/build.gradle b/build.gradle index ea3bda8..eafb2bd 100644 --- a/build.gradle +++ b/build.gradle @@ -2,9 +2,10 @@ buildscript { repositories { jcenter() maven { url "https://maven.google.com" } + google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.2.0-beta02' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' } } diff --git a/gradle.properties b/gradle.properties index aac7c9b..9e6fce1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e893bd2..e136dd5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip diff --git a/submitbuttonview/build.gradle b/submitbuttonview/build.gradle index 837205d..cbc0b25 100644 --- a/submitbuttonview/build.gradle +++ b/submitbuttonview/build.gradle @@ -5,12 +5,12 @@ group='com.github.searchy2' version = '1.7' android { - compileSdkVersion 27 + compileSdkVersion 28 buildToolsVersion "27.0.3" defaultConfig { minSdkVersion 15 - targetSdkVersion 27 + targetSdkVersion 28 versionCode 1 versionName "1.0" } @@ -28,6 +28,6 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:27.1.0' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'androidx.appcompat:appcompat:1.0.0-alpha3' } \ No newline at end of file From 9c4e1d54177982032830f501be17a384ffba2ca4 Mon Sep 17 00:00:00 2001 From: Ray Li Date: Sun, 1 Jul 2018 14:57:56 -0500 Subject: [PATCH 10/11] v1.8 Rollup *Version Bump to v1.8. --- submitbuttonview/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitbuttonview/build.gradle b/submitbuttonview/build.gradle index bd8737b..6ce9717 100644 --- a/submitbuttonview/build.gradle +++ b/submitbuttonview/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' group='com.github.searchy2' -version = '1.7' +version = '1.8' android { compileSdkVersion 28 From 78035c61e5e18a015d25c345f622fa48a56cae98 Mon Sep 17 00:00:00 2001 From: Ray Li Date: Sat, 20 Oct 2018 14:14:45 -0500 Subject: [PATCH 11/11] v1.8.1 Rollup *Version Bump to v1.8.1. *Update Gradle to v3.2.1. *Update dependencies. --- app/build.gradle | 3 +-- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- submitbuttonview/build.gradle | 5 ++--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 02a9c97..660f1e3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,6 @@ apply plugin: 'com.android.application' android { compileSdkVersion 28 - buildToolsVersion "27.0.3" defaultConfig { applicationId "com.unstoppable.submitbutton" minSdkVersion 15 @@ -22,5 +21,5 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation project(':submitbuttonview') - implementation 'androidx.appcompat:appcompat:1.0.0-alpha3' + implementation 'androidx.appcompat:appcompat:1.0.0' } diff --git a/build.gradle b/build.gradle index eafb2bd..3717ab3 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.0-beta02' + classpath 'com.android.tools.build:gradle:3.2.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e136dd5..39a0f5c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip diff --git a/submitbuttonview/build.gradle b/submitbuttonview/build.gradle index 6ce9717..12fad08 100644 --- a/submitbuttonview/build.gradle +++ b/submitbuttonview/build.gradle @@ -2,11 +2,10 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' group='com.github.searchy2' -version = '1.8' +version = '1.8.1' android { compileSdkVersion 28 - buildToolsVersion "27.0.3" defaultConfig { minSdkVersion 15 @@ -29,5 +28,5 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.0.0-alpha3' + implementation 'androidx.appcompat:appcompat:1.0.0' } \ No newline at end of file