Skip to content

Commit 3055095

Browse files
author
Zagumennyi Evgenii
authored
Merge pull request #4 from arychagov/master
enhanced ExpandIconView code to be more pretty and faster
2 parents 87c4462 + f79d444 commit 3055095

File tree

1 file changed

+108
-106
lines changed

1 file changed

+108
-106
lines changed

expandicon/src/main/java/com/github/zagum/expandicon/ExpandIconView.java

Lines changed: 108 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010
import android.graphics.Path;
1111
import android.graphics.Point;
1212
import android.os.Build;
13+
import android.support.annotation.FloatRange;
1314
import android.support.annotation.IntDef;
14-
import android.support.annotation.RequiresApi;
15+
import android.support.annotation.NonNull;
16+
import android.support.annotation.Nullable;
1517
import android.util.AttributeSet;
1618
import android.view.View;
1719
import android.view.animation.DecelerateInterpolator;
20+
1821
import java.lang.annotation.Retention;
1922
import java.lang.annotation.RetentionPolicy;
2023

@@ -34,42 +37,87 @@ public class ExpandIconView extends View {
3437
LESS,
3538
INTERMEDIATE
3639
})
37-
3840
@Retention(RetentionPolicy.SOURCE)
39-
4041
public @interface State {
4142
}
4243

4344
public static final int MORE = 0;
4445
public static final int LESS = 1;
45-
private static final int INTERMEDIATE = 2;
46+
public static final int INTERMEDIATE = 2;
4647

4748
@State
4849
private int state;
49-
private int width;
50-
private int height;
51-
private int arrowWidth;
5250
private float alpha = MORE_STATE_ALPHA;
5351
private float centerTranslation = 0f;
52+
@FloatRange(from = 0.f, to = 1.f)
5453
private float fraction = 0f;
55-
private float animationSpeed;
56-
private boolean useDefaultPadding;
54+
private final float animationSpeed;
5755

58-
private boolean roundedCorners = false;
5956
private boolean switchColor = false;
60-
private long animationDuration = DEFAULT_ANIMATION_DURATION;
6157
private int color = Color.BLACK;
62-
private int colorMore = Color.BLACK;
63-
private int colorLess = Color.RED;
58+
private final int colorMore;
59+
private final int colorLess;
60+
61+
@NonNull
62+
private final Paint paint;
63+
private final Point left = new Point();
64+
private final Point right = new Point();
65+
private final Point center = new Point();
66+
private final Point tempLeft = new Point();
67+
private final Point tempRight = new Point();
68+
69+
private final boolean useDefaultPadding;
6470
private int padding;
6571

66-
private Paint paint;
67-
private Point left;
68-
private Point right;
69-
private Point center;
7072
private final Path path = new Path();
73+
@Nullable
7174
private ValueAnimator arrowAnimator;
7275

76+
public ExpandIconView(@NonNull Context context) {
77+
this(context, null);
78+
}
79+
80+
public ExpandIconView(@NonNull Context context, @Nullable AttributeSet attrs) {
81+
this(context, attrs, 0);
82+
}
83+
84+
public ExpandIconView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
85+
super(context, attrs, defStyleAttr);
86+
87+
TypedArray array = getContext().getTheme().obtainStyledAttributes(attrs,
88+
R.styleable.ExpandIconView,
89+
0, 0);
90+
91+
final boolean roundedCorners;
92+
final long animationDuration;
93+
try {
94+
roundedCorners = array.getBoolean(R.styleable.ExpandIconView_roundedCorners, false);
95+
switchColor = array.getBoolean(R.styleable.ExpandIconView_switchColor, false);
96+
color = array.getColor(R.styleable.ExpandIconView_color, Color.BLACK);
97+
colorMore = array.getColor(R.styleable.ExpandIconView_colorMore, Color.BLACK);
98+
colorLess = array.getColor(R.styleable.ExpandIconView_colorLess, Color.BLACK);
99+
animationDuration = array.getInteger(R.styleable.ExpandIconView_animationDuration, (int) DEFAULT_ANIMATION_DURATION);
100+
padding = array.getDimensionPixelSize(R.styleable.ExpandIconView_eiv_padding, -1);
101+
useDefaultPadding = (padding == -1);
102+
} finally {
103+
array.recycle();
104+
}
105+
106+
{
107+
paint = new Paint(ANTI_ALIAS_FLAG);
108+
paint.setColor(color);
109+
paint.setStyle(Paint.Style.STROKE);
110+
paint.setDither(true);
111+
if (roundedCorners) {
112+
paint.setStrokeJoin(Paint.Join.ROUND);
113+
paint.setStrokeCap(Paint.Cap.ROUND);
114+
}
115+
}
116+
117+
animationSpeed = DELTA_ALPHA / animationDuration;
118+
setState(MORE, false);
119+
}
120+
73121
public void switchState() {
74122
switchState(true);
75123
}
@@ -80,13 +128,21 @@ public void switchState() {
80128
* @param animate Indicates thaw state will be changed with animation or not
81129
*/
82130
public void switchState(boolean animate) {
83-
if (state == MORE) {
84-
setState(LESS, animate);
85-
} else if (state == LESS) {
86-
setState(MORE, animate);
87-
} else {
88-
setState(getFinalStateByFraction(), animate);
131+
final int newState;
132+
switch (state) {
133+
case MORE:
134+
newState = LESS;
135+
break;
136+
case LESS:
137+
newState = MORE;
138+
break;
139+
case INTERMEDIATE:
140+
newState = getFinalStateByFraction();
141+
break;
142+
default:
143+
throw new IllegalArgumentException("Unknown state [" + state + "]");
89144
}
145+
setState(newState, animate);
90146
}
91147

92148
/**
@@ -115,11 +171,15 @@ public void setState(@State int state, boolean animate) {
115171
* state value is 1f
116172
* @throws IllegalArgumentException if fraction is less than 0f or more than 1f
117173
*/
118-
public void setFraction(float fraction, boolean animate) {
174+
public void setFraction(@FloatRange(from = 0.f, to = 1.f) float fraction, boolean animate) {
119175
if (fraction < 0f || fraction > 1f) {
120176
throw new IllegalArgumentException("Fraction value must be from 0 to 1f, fraction=" + fraction);
121177
}
122-
if (this.fraction == fraction) return;
178+
179+
if (this.fraction == fraction) {
180+
return;
181+
}
182+
123183
this.fraction = fraction;
124184
if (fraction == 0f) {
125185
state = MORE;
@@ -128,31 +188,8 @@ public void setFraction(float fraction, boolean animate) {
128188
} else {
129189
state = INTERMEDIATE;
130190
}
131-
updateArrow(animate);
132-
}
133-
134-
public ExpandIconView(Context context) {
135-
super(context);
136-
init();
137-
}
138-
139-
public ExpandIconView(Context context, AttributeSet attrs) {
140-
super(context, attrs);
141-
readAttributes(attrs);
142-
init();
143-
}
144-
145-
public ExpandIconView(Context context, AttributeSet attrs, int defStyleAttr) {
146-
super(context, attrs, defStyleAttr);
147-
readAttributes(attrs);
148-
init();
149-
}
150191

151-
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
152-
public ExpandIconView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
153-
super(context, attrs, defStyleAttr, defStyleRes);
154-
readAttributes(attrs);
155-
init();
192+
updateArrow(animate);
156193
}
157194

158195
@Override
@@ -165,35 +202,15 @@ protected void onDraw(Canvas canvas) {
165202
@Override
166203
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
167204
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
168-
width = getMeasuredWidth();
169-
height = getMeasuredHeight();
170205
calculateArrowMetrics();
171206
updateArrowPath();
172207
}
173208

174-
private void readAttributes(AttributeSet attrs) {
175-
TypedArray array = getContext().getTheme().obtainStyledAttributes(
176-
attrs,
177-
R.styleable.ExpandIconView,
178-
0, 0);
179-
180-
try {
181-
roundedCorners = array.getBoolean(R.styleable.ExpandIconView_eiv_roundedCorners, false);
182-
switchColor = array.getBoolean(R.styleable.ExpandIconView_eiv_switchColor, false);
183-
color = array.getColor(R.styleable.ExpandIconView_eiv_color, Color.BLACK);
184-
colorMore = array.getColor(R.styleable.ExpandIconView_eiv_colorMore, Color.BLACK);
185-
colorLess = array.getColor(R.styleable.ExpandIconView_eiv_colorLess, Color.BLACK);
186-
animationDuration = array.getInteger(R.styleable.ExpandIconView_eiv_animationDuration, (int) DEFAULT_ANIMATION_DURATION);
187-
padding = array.getDimensionPixelSize(R.styleable.ExpandIconView_eiv_padding, -1);
188-
if (padding == -1) useDefaultPadding = true;
189-
} finally {
190-
array.recycle();
191-
}
192-
}
193-
194209
private void calculateArrowMetrics() {
195-
int arrowMaxHeight = height - 2 * padding;
196-
arrowWidth = width - 2 * padding;
210+
final int width = getMeasuredWidth();
211+
final int height = getMeasuredHeight();
212+
final int arrowMaxHeight = height - 2 * padding;
213+
int arrowWidth = width - 2 * padding;
197214
arrowWidth = arrowMaxHeight >= arrowWidth ? arrowWidth : arrowMaxHeight;
198215

199216
if (useDefaultPadding) {
@@ -208,25 +225,6 @@ private void calculateArrowMetrics() {
208225
right.set(center.x + arrowWidth / 2, center.y);
209226
}
210227

211-
private void init() {
212-
paint = new Paint(ANTI_ALIAS_FLAG);
213-
paint.setColor(color);
214-
paint.setStyle(Paint.Style.STROKE);
215-
paint.setDither(true);
216-
if (roundedCorners) {
217-
paint.setStrokeJoin(Paint.Join.ROUND);
218-
paint.setStrokeCap(Paint.Cap.ROUND);
219-
}
220-
221-
left = new Point();
222-
right = new Point();
223-
center = new Point();
224-
225-
animationSpeed = DELTA_ALPHA / animationDuration;
226-
227-
setState(MORE, false);
228-
}
229-
230228
private void updateArrow(boolean animate) {
231229
float toAlpha = MORE_STATE_ALPHA + (fraction * DELTA_ALPHA);
232230
if (animate) {
@@ -245,21 +243,22 @@ private void updateArrow(boolean animate) {
245243
private void updateArrowPath() {
246244
path.reset();
247245
if (left != null && right != null) {
248-
Point currLeft = rotate(left, -alpha);
249-
Point currRight = rotate(right, alpha);
250-
centerTranslation = (center.y - currLeft.y) / 2;
251-
path.moveTo(currLeft.x, currLeft.y);
246+
rotate(left, -alpha, tempLeft);
247+
rotate(right, alpha, tempRight);
248+
centerTranslation = (center.y - tempLeft.y) / 2;
249+
path.moveTo(tempLeft.x, tempLeft.y);
252250
path.lineTo(center.x, center.y);
253-
path.lineTo(currRight.x, currRight.y);
251+
path.lineTo(tempRight.x, tempRight.y);
254252
}
255253
}
256254

257255
private void animateArrow(float toAlpha) {
258256
cancelAnimation();
259-
final ArgbEvaluator colorEvaluator = new ArgbEvaluator();
260257

261-
arrowAnimator = ValueAnimator.ofFloat(alpha, toAlpha);
262-
arrowAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
258+
final ValueAnimator valueAnimator = ValueAnimator.ofFloat(alpha, toAlpha);
259+
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
260+
private final ArgbEvaluator colorEvaluator = new ArgbEvaluator();
261+
263262
@Override
264263
public void onAnimationUpdate(ValueAnimator valueAnimator) {
265264
alpha = (float) valueAnimator.getAnimatedValue();
@@ -270,9 +269,11 @@ public void onAnimationUpdate(ValueAnimator valueAnimator) {
270269
postInvalidateOnAnimationCompat();
271270
}
272271
});
273-
arrowAnimator.setInterpolator(new DecelerateInterpolator());
274-
arrowAnimator.setDuration(calculateAnimationDuration(toAlpha));
275-
arrowAnimator.start();
272+
valueAnimator.setInterpolator(new DecelerateInterpolator());
273+
valueAnimator.setDuration(calculateAnimationDuration(toAlpha));
274+
valueAnimator.start();
275+
276+
arrowAnimator = valueAnimator;
276277
}
277278

278279
private void cancelAnimation() {
@@ -281,7 +282,7 @@ private void cancelAnimation() {
281282
}
282283
}
283284

284-
private void updateColor(ArgbEvaluator colorEvaluator) {
285+
private void updateColor(@NonNull ArgbEvaluator colorEvaluator) {
285286
color = (int) colorEvaluator.evaluate((alpha + 45f) / 90f, colorMore, colorLess);
286287
paint.setColor(color);
287288
}
@@ -290,14 +291,15 @@ private long calculateAnimationDuration(float toAlpha) {
290291
return (long) (Math.abs(toAlpha - alpha) / animationSpeed);
291292
}
292293

293-
private Point rotate(Point startPosition, double degrees) {
294+
private void rotate(@NonNull Point startPosition, double degrees, @NonNull Point target) {
294295
double angle = Math.toRadians(degrees);
295296
int x = (int) (center.x + (startPosition.x - center.x) * Math.cos(angle) -
296297
(startPosition.y - center.y) * Math.sin(angle));
297298

298299
int y = (int) (center.y + (startPosition.x - center.x) * Math.sin(angle) +
299300
(startPosition.y - center.y) * Math.cos(angle));
300-
return new Point(x, y);
301+
302+
target.set(x, y);
301303
}
302304

303305
@State

0 commit comments

Comments
 (0)