diff --git a/.gitignore b/.gitignore index 315bf71..69c4a60 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,32 @@ -# built application files +Application files *.apk *.ap_ -# files for the dex VM +# Files for the Dalvik VM *.dex # Java class files *.class -# generated files +# Generated files bin/ gen/ +# Gradle files +.gradle/ +build/ + # Local configuration file (sdk path, etc) local.properties -# Eclipse project files -.classpath -.project - # Proguard folder generated by Eclipse proguard/ +# Log Files +*.log + # Intellij project files *.iml *.ipr *.iws -.idea/ -.gradle/ - -build +.idea/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index e98e660..016278d 100644 --- a/build.gradle +++ b/build.gradle @@ -5,14 +5,14 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:0.12.+' + classpath 'com.android.tools.build:gradle:1.2.2' classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.+' } } ext { - compileSdkVersion = 19 - buildToolsVersion = "19.1.0" + compileSdkVersion = 22 + buildToolsVersion = "22.0.1" } def isReleaseBuild() { diff --git a/example/build.gradle b/example/build.gradle index 1dd72de..2a82bc2 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -3,29 +3,29 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:0.12.+' + classpath 'com.android.tools.build:gradle:1.1.0' } } -apply plugin: 'android' +apply plugin: 'com.android.application' repositories { mavenCentral() } android { - compileSdkVersion 19 - buildToolsVersion "19.1" + compileSdkVersion 22 + buildToolsVersion "22.0.1" defaultConfig { minSdkVersion 11 - targetSdkVersion 19 + targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { - runProguard true + minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } @@ -33,6 +33,7 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar', '*.aar']) - compile 'com.android.support:appcompat-v7:19.1.0' - compile 'com.github.markushi:circlebutton:1.1' + compile 'com.android.support:appcompat-v7:22.0.0' + //compile 'com.github.markushi:circlebutton:1.1' + compile project(':library') } diff --git a/example/src/main/java/at/markushi/circlebutton/example/MainActivity.java b/example/src/main/java/at/markushi/circlebutton/example/MainActivity.java index 705c2d6..2a5b3a1 100644 --- a/example/src/main/java/at/markushi/circlebutton/example/MainActivity.java +++ b/example/src/main/java/at/markushi/circlebutton/example/MainActivity.java @@ -8,6 +8,8 @@ import android.view.ViewGroup; import android.widget.Toast; +import at.markushi.ui.CheckableCircleButton; + public class MainActivity extends ActionBarActivity { @Override @@ -20,7 +22,7 @@ protected void onCreate(Bundle savedInstanceState) { } } - public static class PlaceholderFragment extends Fragment implements View.OnClickListener { + public static class PlaceholderFragment extends Fragment implements View.OnClickListener, CheckableCircleButton.OnCheckedChangeListener { public PlaceholderFragment() { } @@ -31,7 +33,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa rootView.findViewById(R.id.button0).setOnClickListener(this); rootView.findViewById(R.id.button1).setOnClickListener(this); - rootView.findViewById(R.id.button2).setOnClickListener(this); + CheckableCircleButton ccb = (CheckableCircleButton) rootView.findViewById(R.id.button2); + ccb.setOnCheckedChangeListener(this); return rootView; } @@ -40,6 +43,11 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa public void onClick(View view) { Toast.makeText(getActivity(), "Button clicked", Toast.LENGTH_SHORT).show(); } - } + @Override + public void onCheckedChanged(CheckableCircleButton button, boolean isChecked) { + String state = isChecked ? "checked": "unchecked"; + Toast.makeText(getActivity(), "Button in " + state +" state", Toast.LENGTH_SHORT).show(); + } + } } diff --git a/example/src/main/res/drawable-xhdpi/ic_pause_white_36dp.png b/example/src/main/res/drawable-xhdpi/ic_pause_white_36dp.png new file mode 100644 index 0000000..72dfa9f Binary files /dev/null and b/example/src/main/res/drawable-xhdpi/ic_pause_white_36dp.png differ diff --git a/example/src/main/res/drawable-xhdpi/ic_play_arrow_white_36dp.png b/example/src/main/res/drawable-xhdpi/ic_play_arrow_white_36dp.png new file mode 100644 index 0000000..043acd8 Binary files /dev/null and b/example/src/main/res/drawable-xhdpi/ic_play_arrow_white_36dp.png differ diff --git a/example/src/main/res/layout/fragment_main.xml b/example/src/main/res/layout/fragment_main.xml index 1e584ec..0d0ce0b 100644 --- a/example/src/main/res/layout/fragment_main.xml +++ b/example/src/main/res/layout/fragment_main.xml @@ -31,15 +31,18 @@ android:src="@drawable/ic_action_tick" app:cb_color="#FFBB33" /> - + android:src="@drawable/ic_play_arrow_white_36dp" + app:cb_pressedRingWidth="8dp" + app:cb_checkedSrc="@drawable/ic_pause_white_36dp" + app:cb_color="#B48CF4" + app:cb_checkedColor="#AF5555"/> \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8c0fb64 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0c71e76 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/library/build.gradle b/library/build.gradle index e870fdb..42e268a 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -3,20 +3,20 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:0.12.+' + classpath 'com.android.tools.build:gradle:1.2.2' } } apply plugin: 'android-sdk-manager' -apply plugin: 'android-library' +apply plugin: 'com.android.library' repositories { mavenCentral() } android { - compileSdkVersion 19 - buildToolsVersion "19.1" + compileSdkVersion 22 + buildToolsVersion "22.0.1" defaultConfig { versionName = VERSION_NAME diff --git a/library/src/main/java/at/markushi/ui/CheckableCircleButton.java b/library/src/main/java/at/markushi/ui/CheckableCircleButton.java new file mode 100644 index 0000000..15ba66a --- /dev/null +++ b/library/src/main/java/at/markushi/ui/CheckableCircleButton.java @@ -0,0 +1,170 @@ +package at.markushi.ui; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.widget.Checkable; + +import at.markushi.circlebutton.R; + +public class CheckableCircleButton extends CircleButton implements Checkable { + private boolean mChecked = false; + + private int mDefaultColor = Color.RED; + private int mCheckedColor = Color.BLUE; + + private Drawable mDefaultDrawable; + private Drawable mCheckedDrawable; + + private OnCheckedChangeListener mListener; + private boolean mBroadcasting; + + public CheckableCircleButton(Context context) { + super(context); + } + + public CheckableCircleButton(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public CheckableCircleButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context, attrs); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public CheckableCircleButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(context, attrs); + } + + @Override + public void setPressed(boolean pressed) { + super.setPressed(pressed); + toggle(); + } + + @Override + public void setChecked(boolean b) { + if (mChecked != b) { + mChecked = b; + setVisualState(mChecked); + // Avoid infinite recursions if setChecked() is called from a listener + if (mBroadcasting) { + return; + } + mBroadcasting = true; + if (mListener != null) { + mListener.onCheckedChanged(this, mChecked); + } + mBroadcasting = false; + } + } + + @Override + public boolean isChecked() { + return mChecked; + } + + @Override + public void toggle() { + setChecked(!mChecked); + } + + private void init(Context context, AttributeSet attrs) { + if (attrs != null) { + final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleButton); + mDefaultColor = ta.getColor(R.styleable.CircleButton_cb_color, mDefaultColor); + ta.recycle(); + + final TypedArray ta_ccb = context.obtainStyledAttributes(attrs, R.styleable.CheckableCircleButton); + mCheckedColor = ta_ccb.getColor(R.styleable.CircleButton_cb_color, mCheckedColor); + mCheckedDrawable = ta_ccb.getDrawable(R.styleable.CheckableCircleButton_cb_checkedSrc); + ta_ccb.recycle(); + } + mDefaultDrawable = getDrawable(); + } + + private void setVisualState(boolean isChecked) { + setColor(isChecked ? mCheckedColor: mDefaultColor); + setImageDrawable(isChecked ? mCheckedDrawable: mDefaultDrawable); + } + + public void setCheckedState(boolean isChecked) { + if (mChecked != isChecked) { + mChecked = isChecked; + setVisualState(mChecked); + } + } + + public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { + mListener = listener; + } + + public interface OnCheckedChangeListener { + void onCheckedChanged(CheckableCircleButton button, boolean isChecked); + } + + + static class SavedState extends BaseSavedState { + boolean checked; + + SavedState(Parcelable superState) { + super(superState); + } + + private SavedState(Parcel in) { + super(in); + checked = (Boolean)in.readValue(null); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeValue(checked); + } + + @Override + public String toString() { + return "CheckableCircleButton.SavedState{" + + Integer.toHexString(System.identityHashCode(this)) + + " checked=" + checked + "}"; + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + @Override + public Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + SavedState ss = new SavedState(superState); + ss.checked = mChecked; + return ss; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + + super.onRestoreInstanceState(ss.getSuperState()); + mChecked = ss.checked; + setVisualState(ss.checked); + requestLayout(); + } +} diff --git a/library/src/main/java/at/markushi/ui/CircleButton.java b/library/src/main/java/at/markushi/ui/CircleButton.java index 0e0ca84..79cf41d 100644 --- a/library/src/main/java/at/markushi/ui/CircleButton.java +++ b/library/src/main/java/at/markushi/ui/CircleButton.java @@ -1,11 +1,13 @@ package at.markushi.ui; import android.animation.ObjectAnimator; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.os.Build; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.ImageView; @@ -32,7 +34,8 @@ public class CircleButton extends ImageView { private int pressedRingWidth; private int defaultColor = Color.BLACK; private int pressedColor; - private ObjectAnimator pressedAnimator; + + private OnPressListener mListener; public CircleButton(Context context) { super(context); @@ -49,18 +52,22 @@ public CircleButton(Context context, AttributeSet attrs, int defStyle) { init(context, attrs); } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public CircleButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(context, attrs); + } + @Override public void setPressed(boolean pressed) { super.setPressed(pressed); - if (circlePaint != null) { - circlePaint.setColor(pressed ? pressedColor : defaultColor); - } - - if (pressed) { - showPressedRing(); - } else { - hidePressedRing(); + if (mListener != null) { + if (pressed) { + mListener.onPressed(this); + } else { + mListener.onUnpressed(this); + } } } @@ -100,16 +107,6 @@ public void setColor(int color) { this.invalidate(); } - private void hidePressedRing() { - pressedAnimator.setFloatValues(pressedRingWidth, 0f); - pressedAnimator.start(); - } - - private void showPressedRing() { - pressedAnimator.setFloatValues(animationProgress, pressedRingWidth); - pressedAnimator.start(); - } - private void init(Context context, AttributeSet attrs) { this.setFocusable(true); this.setScaleType(ScaleType.CENTER_INSIDE); @@ -133,15 +130,50 @@ private void init(Context context, AttributeSet attrs) { } setColor(color); - focusPaint.setStrokeWidth(pressedRingWidth); - final int pressedAnimationTime = getResources().getInteger(ANIMATION_TIME_ID); - pressedAnimator = ObjectAnimator.ofFloat(this, "animationProgress", 0f, 0f); - pressedAnimator.setDuration(pressedAnimationTime); + + setOnPressListener(new DefaultPressListener()); } private int getHighlightColor(int color, int amount) { return Color.argb(Math.min(255, Color.alpha(color)), Math.min(255, Color.red(color) + amount), Math.min(255, Color.green(color) + amount), Math.min(255, Color.blue(color) + amount)); } + + public void setOnPressListener(OnPressListener listener) { + mListener = listener; + } + + public interface OnPressListener { + void onPressed(CircleButton button); + void onUnpressed(CircleButton button); + } + + public class DefaultPressListener implements OnPressListener { + private ObjectAnimator pressedAnimator; + + public DefaultPressListener() { + final int pressedAnimationTime = getResources().getInteger(ANIMATION_TIME_ID); + pressedAnimator = ObjectAnimator.ofFloat(CircleButton.this, "animationProgress", 0f, 0f); + pressedAnimator.setDuration(pressedAnimationTime); + } + + @Override + public void onPressed(CircleButton button) { + if (circlePaint != null) { + circlePaint.setColor(pressedColor); + } + pressedAnimator.setFloatValues(animationProgress, pressedRingWidth); + pressedAnimator.start(); + } + + @Override + public void onUnpressed(CircleButton button) { + if (circlePaint != null) { + circlePaint.setColor(defaultColor); + } + pressedAnimator.setFloatValues(pressedRingWidth, 0f); + pressedAnimator.start(); + } + } } diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 747de0a..8ed6b5f 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -4,4 +4,10 @@ + + + + + + \ No newline at end of file