{
60 |
61 | private final T mLayoutParams;
62 |
63 | LayoutParamsBuilder(@NonNull T layoutParams) {
64 | mLayoutParams = layoutParams;
65 | }
66 |
67 | public LayoutParamsBuilder setDrawable(Drawable drawable) {
68 | mLayoutParams.getAttributes().setDrawable(drawable);
69 | return this;
70 | }
71 |
72 | public LayoutParamsBuilder setDrawableWidth(float drawableWidth) {
73 | mLayoutParams.getAttributes().setDrawableWidth(drawableWidth);
74 | return this;
75 | }
76 |
77 | public LayoutParamsBuilder setDrawableHeight(float drawableHeight) {
78 | mLayoutParams.getAttributes().setDrawableHeight(drawableHeight);
79 | return this;
80 | }
81 |
82 | public LayoutParamsBuilder setAnimationDuration(int animationDuration) {
83 | mLayoutParams.getAttributes().setAnimationDuration(animationDuration);
84 | return this;
85 | }
86 |
87 | public LayoutParamsBuilder setProduceInterval(int produceInterval) {
88 | mLayoutParams.getAttributes().setProduceInterval(produceInterval);
89 | return this;
90 | }
91 |
92 | public LayoutParamsBuilder setTintMode(@LikesAttributes.TintMode int tintMode) {
93 | mLayoutParams.getAttributes().setTintMode(tintMode);
94 | return this;
95 | }
96 |
97 | public LayoutParamsBuilder setTintColors(@Nullable int[] tintColors) {
98 | mLayoutParams.getAttributes().setTintColors(tintColors);
99 | return this;
100 | }
101 |
102 | public LayoutParamsBuilder setDrawableAnimatorFactory(@Nullable DrawableAnimator.Factory drawableAnimator) {
103 | mLayoutParams.getAttributes().setDrawableAnimatorFactory(drawableAnimator);
104 | return this;
105 | }
106 |
107 | public LayoutParamsBuilder setPositionAnimatorFactory(@Nullable PositionAnimator.Factory positionAnimator) {
108 | mLayoutParams.getAttributes().setPositionAnimatorFactory(positionAnimator);
109 | return this;
110 | }
111 |
112 | public LayoutParamsBuilder setLikesMode(@LikesAttributes.LikesMode int likesMode) {
113 | mLayoutParams.getAttributes().setLikesMode(likesMode);
114 | return this;
115 | }
116 |
117 | public ViewGroup.LayoutParams build() {
118 | return mLayoutParams;
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/iojjj/likeslayout/LikesLayoutInternal.java:
--------------------------------------------------------------------------------
1 | package com.github.iojjj.likeslayout;
2 |
3 | /**
4 | * LikesLayout interface for internal usage.
5 | */
6 | interface LikesLayoutInternal extends OnChildTouchListener {
7 |
8 | void invalidate();
9 |
10 | int getWidth();
11 |
12 | int getHeight();
13 | }
14 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/iojjj/likeslayout/LikesLayoutParams.java:
--------------------------------------------------------------------------------
1 | package com.github.iojjj.likeslayout;
2 |
3 | /**
4 | * LikesLayout params interface.
5 | */
6 | interface LikesLayoutParams {
7 |
8 | /**
9 | * Get attributes from LayoutParams.
10 | * @return attributes from layout params
11 | */
12 | LikesAttributes getAttributes();
13 | }
14 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/iojjj/likeslayout/LikesLinearLayout.java:
--------------------------------------------------------------------------------
1 | package com.github.iojjj.likeslayout;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.graphics.Canvas;
6 | import android.os.Build;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.util.AttributeSet;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.LinearLayout;
13 |
14 | import java.util.concurrent.TimeUnit;
15 |
16 | /**
17 | * LinearLayout based implementation of likes layout..
18 | */
19 | public class LikesLinearLayout extends LinearLayout implements LikesLayout {
20 |
21 | private LikesDrawer mLikesDrawer;
22 | private LikesAttributes mDefaultAttributes;
23 | private OnChildTouchListener mOnChildTouchListener;
24 |
25 | public LikesLinearLayout(Context context) {
26 | this(context, null);
27 | }
28 |
29 | public LikesLinearLayout(Context context, AttributeSet attrs) {
30 | this(context, attrs, 0);
31 | }
32 |
33 | public LikesLinearLayout(Context context, AttributeSet attrs, int defStyle) {
34 | super(context, attrs, defStyle);
35 | init(context, attrs);
36 | }
37 |
38 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
39 | public LikesLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
40 | super(context, attrs, defStyleAttr, defStyleRes);
41 | init(context, attrs);
42 | }
43 |
44 | private void init(@NonNull Context context, @Nullable AttributeSet attrs) {
45 | setWillNotDraw(false);
46 | if (attrs != null) {
47 | mDefaultAttributes = LikesAttributesImpl.create(context, attrs, LikesAttributes.LAYOUT_TYPE_LINEAR);
48 | } else {
49 | mDefaultAttributes = LikesAttributesImpl.empty();
50 | }
51 | mLikesDrawer = new LikesDrawer(this, mDefaultAttributes);
52 | }
53 |
54 | @Override
55 | protected LayoutParams generateDefaultLayoutParams() {
56 | return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
57 | }
58 |
59 | @Override
60 | protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
61 | if (p instanceof LayoutParams) {
62 | return new LayoutParams(((LayoutParams) p));
63 | }
64 | return new LayoutParams(p);
65 | }
66 |
67 | @Override
68 | public LayoutParams generateLayoutParams(AttributeSet attrs) {
69 | return new LayoutParams(getContext(), attrs);
70 | }
71 |
72 | @Override
73 | protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
74 | return p instanceof LayoutParams;
75 | }
76 |
77 | @Override
78 | public void addView(View child, int index, ViewGroup.LayoutParams params) {
79 | super.addView(child, index, params);
80 | LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
81 | if (layoutParams.mAttributes.isLikesModeEnabled() && layoutParams.mAttributes.hasDrawable(mDefaultAttributes)) {
82 | child.setOnTouchListener(mLikesDrawer);
83 | }
84 | }
85 |
86 | @Override
87 | protected void onDraw(Canvas canvas) {
88 | super.onDraw(canvas);
89 | if (mLikesDrawer != null) {
90 | mLikesDrawer.onDraw(canvas);
91 | }
92 | }
93 |
94 | /**
95 | * Set instance of OnChildTouchListener.
96 | * @param onChildTouchListener instance of OnChildTouchListener
97 | */
98 | public void setOnChildTouchListener(@Nullable OnChildTouchListener onChildTouchListener) {
99 | mOnChildTouchListener = onChildTouchListener;
100 | }
101 |
102 | @Override
103 | public void onChildTouched(View child) {
104 | if (mOnChildTouchListener != null) {
105 | mOnChildTouchListener.onChildTouched(child);
106 | }
107 | }
108 |
109 | @Override
110 | public void onChildReleased(View child, boolean isCanceled) {
111 | if (mOnChildTouchListener != null) {
112 | mOnChildTouchListener.onChildReleased(child, isCanceled);
113 | }
114 | }
115 |
116 | @Override
117 | public void onLikeProduced(View child) {
118 | if (mOnChildTouchListener != null) {
119 | mOnChildTouchListener.onLikeProduced(child);
120 | }
121 | }
122 |
123 | @NonNull
124 | @Override
125 | public LayoutParamsBuilder newLayoutParamsBuilder(int width, int height) {
126 | return new LayoutParamsBuilder<>(new LayoutParams(width, height));
127 | }
128 |
129 | @NonNull
130 | @Override
131 | public LayoutParamsBuilder newLayoutParamsBuilder(@NonNull ViewGroup.LayoutParams source) {
132 | return new LayoutParamsBuilder<>(new LayoutParams(source));
133 | }
134 |
135 | @NonNull
136 | @Override
137 | public LikesAttributes getAttributes() {
138 | return mDefaultAttributes;
139 | }
140 |
141 | @Override
142 | public LikesProducer produceLikes(@NonNull View childView, long timeout) {
143 | if (childView.getLayoutParams() instanceof LayoutParams && childView.getParent() == this) {
144 | return mLikesDrawer.newLikesProducer(childView, timeout);
145 | }
146 | throw new IllegalArgumentException("Child view is not a child of this LikesLayout");
147 | }
148 |
149 | @Override
150 | public LikesProducer produceLikes(@NonNull View childView, long time, @NonNull TimeUnit timeUnit) {
151 | return produceLikes(childView, TimeUnit.MILLISECONDS.convert(time, timeUnit));
152 | }
153 |
154 | /**
155 | * Per-child layout information associated with LikesLinearLayout.
156 | */
157 | public static class LayoutParams extends LinearLayout.LayoutParams implements LikesLayoutParams {
158 |
159 | private final LikesAttributesImpl mAttributes;
160 |
161 | public LayoutParams(Context c, AttributeSet attrs) {
162 | super(c, attrs);
163 | mAttributes = LikesAttributesImpl.create(c, attrs, LikesAttributes.LAYOUT_TYPE_LINEAR);
164 | }
165 |
166 | public LayoutParams(int width, int height) {
167 | super(width, height);
168 | mAttributes = LikesAttributesImpl.empty();
169 | }
170 |
171 | public LayoutParams(ViewGroup.LayoutParams source) {
172 | super(source);
173 | mAttributes = LikesAttributesImpl.empty();
174 | }
175 |
176 | @SuppressWarnings("IncompleteCopyConstructor")
177 | @TargetApi(Build.VERSION_CODES.KITKAT)
178 | public LayoutParams(LayoutParams source) {
179 | super(source);
180 | mAttributes = LikesAttributesImpl.copy(source.mAttributes);
181 | }
182 |
183 | @Override
184 | public LikesAttributes getAttributes() {
185 | return mAttributes;
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/iojjj/likeslayout/LikesProducer.java:
--------------------------------------------------------------------------------
1 | package com.github.iojjj.likeslayout;
2 |
3 | /**
4 | * Likes producer that allows to stop producing of likes.
5 | */
6 | public interface LikesProducer {
7 |
8 | /**
9 | * Stop producing of likes
10 | */
11 | void stop();
12 | }
13 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/iojjj/likeslayout/LikesRelativeLayout.java:
--------------------------------------------------------------------------------
1 | package com.github.iojjj.likeslayout;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.graphics.Canvas;
6 | import android.os.Build;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.util.AttributeSet;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.RelativeLayout;
13 |
14 | import java.util.concurrent.TimeUnit;
15 |
16 | /**
17 | * RelativeLayout based implementation of likes layout.
18 | */
19 | public class LikesRelativeLayout extends RelativeLayout implements LikesLayout {
20 |
21 | private LikesDrawer mLikesDrawer;
22 | private LikesAttributes mDefaultAttributes;
23 | private OnChildTouchListener mOnChildTouchListener;
24 |
25 | public LikesRelativeLayout(Context context) {
26 | this(context, null);
27 | }
28 |
29 | public LikesRelativeLayout(Context context, AttributeSet attrs) {
30 | this(context, attrs, 0);
31 | }
32 |
33 | public LikesRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
34 | super(context, attrs, defStyle);
35 | init(context, attrs);
36 | }
37 |
38 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
39 | public LikesRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
40 | super(context, attrs, defStyleAttr, defStyleRes);
41 | init(context, attrs);
42 | }
43 |
44 | private void init(@NonNull Context context, @Nullable AttributeSet attrs) {
45 | setWillNotDraw(false);
46 | if (attrs != null) {
47 | mDefaultAttributes = LikesAttributesImpl.create(context, attrs, LikesAttributes.LAYOUT_TYPE_RELATIVE);
48 | } else {
49 | mDefaultAttributes = LikesAttributesImpl.empty();
50 | }
51 | mLikesDrawer = new LikesDrawer(this, mDefaultAttributes);
52 | }
53 |
54 | @Override
55 | protected LayoutParams generateDefaultLayoutParams() {
56 | return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
57 | }
58 |
59 | @Override
60 | protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
61 | if (p instanceof LayoutParams) {
62 | return new LayoutParams(((LayoutParams) p));
63 | }
64 | return new LayoutParams(p);
65 | }
66 |
67 | @Override
68 | public LayoutParams generateLayoutParams(AttributeSet attrs) {
69 | return new LayoutParams(getContext(), attrs);
70 | }
71 |
72 | @Override
73 | protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
74 | return p instanceof LayoutParams;
75 | }
76 |
77 | @Override
78 | public void addView(View child, int index, ViewGroup.LayoutParams params) {
79 | super.addView(child, index, params);
80 | LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
81 | if (layoutParams.mAttributes.isLikesModeEnabled() && layoutParams.mAttributes.hasDrawable(mDefaultAttributes)) {
82 | child.setOnTouchListener(mLikesDrawer);
83 | }
84 | }
85 |
86 | @Override
87 | protected void onDraw(Canvas canvas) {
88 | super.onDraw(canvas);
89 | if (mLikesDrawer != null) {
90 | mLikesDrawer.onDraw(canvas);
91 | }
92 | }
93 |
94 | /**
95 | * Set instance of OnChildTouchListener.
96 | * @param onChildTouchListener instance of OnChildTouchListener
97 | */
98 | public void setOnChildTouchListener(@Nullable OnChildTouchListener onChildTouchListener) {
99 | mOnChildTouchListener = onChildTouchListener;
100 | }
101 |
102 | @Override
103 | public void onChildTouched(View child) {
104 | if (mOnChildTouchListener != null) {
105 | mOnChildTouchListener.onChildTouched(child);
106 | }
107 | }
108 |
109 | @Override
110 | public void onChildReleased(View child, boolean isCanceled) {
111 | if (mOnChildTouchListener != null) {
112 | mOnChildTouchListener.onChildReleased(child, isCanceled);
113 | }
114 | }
115 |
116 | @Override
117 | public void onLikeProduced(View child) {
118 | if (mOnChildTouchListener != null) {
119 | mOnChildTouchListener.onLikeProduced(child);
120 | }
121 | }
122 |
123 | @NonNull
124 | @Override
125 | public LayoutParamsBuilder newLayoutParamsBuilder(int width, int height) {
126 | return new LayoutParamsBuilder<>(new LayoutParams(width, height));
127 | }
128 |
129 | @NonNull
130 | @Override
131 | public LayoutParamsBuilder newLayoutParamsBuilder(@NonNull ViewGroup.LayoutParams source) {
132 | return new LayoutParamsBuilder<>(new LayoutParams(source));
133 | }
134 |
135 | @NonNull
136 | @Override
137 | public LikesAttributes getAttributes() {
138 | return mDefaultAttributes;
139 | }
140 |
141 | @Override
142 | public LikesProducer produceLikes(@NonNull View childView, long timeout) {
143 | if (childView.getLayoutParams() instanceof LayoutParams && childView.getParent() == this) {
144 | return mLikesDrawer.newLikesProducer(childView, timeout);
145 | }
146 | throw new IllegalArgumentException("Child view is not a child of this LikesLayout");
147 | }
148 |
149 | @Override
150 | public LikesProducer produceLikes(@NonNull View childView, long time, @NonNull TimeUnit timeUnit) {
151 | return produceLikes(childView, TimeUnit.MILLISECONDS.convert(time, timeUnit));
152 | }
153 |
154 | /**
155 | * Per-child layout information associated with LikesRelativeLayout.
156 | */
157 | public static class LayoutParams extends RelativeLayout.LayoutParams implements LikesLayoutParams {
158 |
159 | private final LikesAttributesImpl mAttributes;
160 |
161 | public LayoutParams(Context c, AttributeSet attrs) {
162 | super(c, attrs);
163 | mAttributes = LikesAttributesImpl.create(c, attrs, LikesAttributes.LAYOUT_TYPE_RELATIVE);
164 | }
165 |
166 | public LayoutParams(int width, int height) {
167 | super(width, height);
168 | mAttributes = LikesAttributesImpl.empty();
169 | }
170 |
171 | public LayoutParams(ViewGroup.LayoutParams source) {
172 | super(source);
173 | mAttributes = LikesAttributesImpl.empty();
174 | }
175 |
176 | @SuppressWarnings("IncompleteCopyConstructor")
177 | @TargetApi(Build.VERSION_CODES.KITKAT)
178 | public LayoutParams(LayoutParams source) {
179 | super(source);
180 | mAttributes = LikesAttributesImpl.copy(source.mAttributes);
181 | }
182 |
183 | @Override
184 | public LikesAttributes getAttributes() {
185 | return mAttributes;
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/iojjj/likeslayout/OnChildTouchListener.java:
--------------------------------------------------------------------------------
1 | package com.github.iojjj.likeslayout;
2 |
3 | import android.view.MotionEvent;
4 | import android.view.View;
5 |
6 | /**
7 | * Listener for touch events of child views.
8 | */
9 | public interface OnChildTouchListener {
10 |
11 | /**
12 | * Called when child was touched.
13 | * @param child child view
14 | */
15 | void onChildTouched(View child);
16 |
17 | /**
18 | * Called when child was released.
19 | * @param child child view
20 | * @param isCanceled true if view was released because of {@link MotionEvent#ACTION_CANCEL} event, false otherwise
21 | */
22 | void onChildReleased(View child, boolean isCanceled);
23 |
24 | /**
25 | * Called when likes is produced.
26 | * @param child child view
27 | */
28 | void onLikeProduced(View child);
29 | }
30 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/iojjj/likeslayout/PositionAnimator.java:
--------------------------------------------------------------------------------
1 | package com.github.iojjj.likeslayout;
2 |
3 | import android.animation.FloatEvaluator;
4 | import android.support.annotation.Keep;
5 | import android.support.annotation.NonNull;
6 |
7 | import java.util.Random;
8 | import java.util.concurrent.atomic.AtomicInteger;
9 |
10 | /**
11 | * Interface of drawable's position animator. {@link #initialize(float, float, int, int, LikesAttributes, LikesAttributes)}
12 | * method of implementations of this interface will be called once to configure animator. Then subsequent calls of
13 | * {@link #onAnimationUpdate(float, float[])} will be performed.
14 | */
15 | @Keep
16 | public interface PositionAnimator {
17 |
18 | /**
19 | * X-coordinate index.
20 | */
21 | int POSITION_X = 0;
22 |
23 | /**
24 | * Y-coordinate index.
25 | */
26 | int POSITION_Y = 1;
27 |
28 | /**
29 | * Initialize animator.
30 | *
31 | * @param startX start center point's X-coordinate
32 | * @param startY start center point's Y-coordinate
33 | * @param layoutWidth LikesLayout width
34 | * @param layoutHeight LikesLayout height
35 | * @param attributes attributes of drawable
36 | * @param defaultAttributes default attributes of LikesLayout
37 | */
38 | void initialize(float startX, float startY, int layoutWidth, int layoutHeight,
39 | @NonNull LikesAttributes attributes, @NonNull LikesAttributes defaultAttributes);
40 |
41 | /**
42 | * Called every time of animation progress. Note that coordinates should represent a point in center of drawable.
43 | *
44 | * --------------
45 | * | |
46 | * | x |
47 | * | |
48 | * --------------
49 | *
50 | * where {@code x} - center point.
51 | *
52 | * @param fraction animation fraction in range {@code [0, 1]}
53 | * @param newPosition array with length of 2 where you should save new drawables coordinates
54 | * @see #POSITION_X
55 | * @see #POSITION_Y
56 | */
57 | void onAnimationUpdate(float fraction, float[] newPosition);
58 |
59 | /**
60 | * Factory that creates new instances of DrawableAnimator.
61 | * Implementations of this factory must have public constructor without arguments.
62 | */
63 | @Keep
64 | interface Factory {
65 |
66 | /**
67 | * Create new instance of PositionAnimator.
68 | *
69 | * @return new instance of PositionAnimator
70 | */
71 | PositionAnimator newInstance();
72 | }
73 |
74 | @Keep
75 | class DefaultFactory implements Factory {
76 |
77 | private final FloatEvaluator mFloatEvaluator = new FloatEvaluator();
78 |
79 | private final Random mRandom = new Random();
80 |
81 | @Override
82 | public PositionAnimator newInstance() {
83 | return new SinPositionAnimator(mFloatEvaluator, mRandom);
84 | }
85 | }
86 |
87 | abstract class AbstractLinearPositionAnimator implements PositionAnimator {
88 |
89 | static final int PATH_CENTER = 0;
90 | static final int PATH_LEFT = 1;
91 | static final int PATH_RIGHT = 2;
92 |
93 | private final FloatEvaluator mPositionEvaluator;
94 | private float mStartX, mStartY, mEndX, mEndY;
95 |
96 | AbstractLinearPositionAnimator() {
97 | mPositionEvaluator = new FloatEvaluator();
98 | }
99 |
100 | @Override
101 | public void initialize(float startX, float startY,
102 | int layoutWidth, int layoutHeight,
103 | @NonNull LikesAttributes attributes,
104 | @NonNull LikesAttributes defaultAttributes) {
105 | mStartX = startX;
106 | mStartY = startY;
107 | int path = getPath();
108 | if (path == PATH_CENTER) {
109 | mEndX = mStartX; // center
110 | } else if (path == PATH_LEFT) {
111 | mEndX = mStartX - attributes.getDrawableHeight(defaultAttributes) / 2f; // left
112 | } else {
113 | mEndX = mStartX + attributes.getDrawableHeight(defaultAttributes) / 2f; // right
114 | }
115 | mEndY = attributes.getDrawableHeight(defaultAttributes);
116 | }
117 |
118 | /**
119 | * Get the path for drawable.
120 | *
121 | * @return path for the drawable
122 | */
123 | protected abstract int getPath();
124 |
125 | @Override
126 | public void onAnimationUpdate(float fraction, float[] newPosition) {
127 | newPosition[POSITION_X] = mPositionEvaluator.evaluate(fraction, mStartX, mEndX);
128 | newPosition[POSITION_Y] = mPositionEvaluator.evaluate(fraction, mStartY, mEndY);
129 | }
130 | }
131 |
132 | /**
133 | * Linear position animator that randomly choose the route for moving drawable.
134 | */
135 | class LinearRandomRoutePositionAnimator extends AbstractLinearPositionAnimator {
136 |
137 | private final Random mRandom;
138 |
139 | public LinearRandomRoutePositionAnimator() {
140 | mRandom = new Random();
141 | }
142 |
143 | @Override
144 | protected int getPath() {
145 | return mRandom.nextInt(PATH_RIGHT + 1);
146 | }
147 | }
148 |
149 | /**
150 | * Linear position animator that randomly choose the route for moving drawable.
151 | */
152 | class LinearSuccessiveRoutePositionAnimator extends AbstractLinearPositionAnimator {
153 |
154 | private final AtomicInteger mCounter;
155 |
156 | /**
157 | * Constructor.
158 | * @param counter single instance of counter that belongs to factory.
159 | */
160 | public LinearSuccessiveRoutePositionAnimator(@NonNull AtomicInteger counter) {
161 | mCounter = counter;
162 | }
163 |
164 | @Override
165 | protected int getPath() {
166 | return mCounter.getAndIncrement() % (PATH_RIGHT + 1);
167 | }
168 | }
169 |
170 | /**
171 | * Implementation of PositionAnimator that animates drawable position using {@code sin()} function.
172 | */
173 | @Keep
174 | class SinPositionAnimator implements PositionAnimator {
175 |
176 | private static float sMaxAngle = 360.0f;
177 |
178 | private final FloatEvaluator mPositionEvaluator;
179 | private final Random mRandom;
180 | private float mStartX, mStartY, mEndY, mEndX;
181 | private float mCoefficient;
182 |
183 | public SinPositionAnimator() {
184 | mPositionEvaluator = new FloatEvaluator();
185 | mRandom = new Random();
186 | }
187 |
188 | public SinPositionAnimator(FloatEvaluator positionEvaluator, Random random) {
189 | mPositionEvaluator = positionEvaluator;
190 | mRandom = random;
191 | }
192 |
193 | @Override
194 | public void initialize(float startX, float startY,
195 | int layoutWidth, int layoutHeight,
196 | @NonNull LikesAttributes attributes,
197 | @NonNull LikesAttributes defaultAttributes) {
198 | mStartX = startX;
199 | mStartY = startY;
200 | mEndX = attributes.getDrawableWidth(defaultAttributes) / 2;
201 | mEndY = attributes.getDrawableHeight(defaultAttributes);
202 | mCoefficient = mRandom.nextBoolean() ? 1 : -1;
203 | }
204 |
205 | @Override
206 | public void onAnimationUpdate(float fraction, float[] newPosition) {
207 | final double radians = Math.toRadians(sMaxAngle * fraction);
208 | newPosition[POSITION_X] = (float) (mStartX + Math.sin(radians) * mCoefficient * mEndX);
209 | newPosition[POSITION_Y] = mPositionEvaluator.evaluate(fraction, mStartY, mEndY);
210 | }
211 |
212 | @Keep
213 | public static void setMaxAngle(float maxAngle) {
214 | sMaxAngle = maxAngle;
215 | }
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/library/src/main/java/com/github/iojjj/likeslayout/Utilities.java:
--------------------------------------------------------------------------------
1 | package com.github.iojjj.likeslayout;
2 |
3 | /**
4 | * Helpful utils.
5 | */
6 | class Utilities {
7 |
8 | private Utilities() {
9 | //no instance
10 | }
11 |
12 | /**
13 | * Trapeze function.
14 | * @param t current value
15 | * @param a value at aT point of time
16 | * @param aT first point
17 | * @param b value at bT point of time
18 | * @param bT second point
19 | * @param c value at cT point of time
20 | * @param cT third point
21 | * @param d value at dT point of time
22 | * @param dT forth point
23 | * @return calculated value
24 | */
25 | public static float trapeze(float t, float a, float aT, float b, float bT, float c, float cT, float d, float dT) {
26 | if (t < aT) {
27 | return a;
28 | }
29 | if (t >= aT && t < bT) {
30 | float norm = normalize(t, aT, bT);
31 | return a + norm * (b - a);
32 | }
33 | if (t >= bT && t < cT) {
34 | float norm = normalize(t, bT, cT);
35 | return b + norm * (c - b);
36 | }
37 | if (t >= cT && t <= dT) {
38 | float norm = normalize(t, cT, dT);
39 | return c + norm * (d - c);
40 | }
41 | return d;
42 | }
43 |
44 | /**
45 | * Normalize value between minimum and maximum.
46 | * @param val value
47 | * @param minVal minimum value
48 | * @param maxVal maximum value
49 | * @return normalized value in range 0..1
50 | * @throws IllegalArgumentException if value is out of range [minVal, maxVal]
51 | */
52 | public static float normalize(float val, float minVal, float maxVal) {
53 | if (val < minVal || val > maxVal)
54 | throw new IllegalArgumentException("Value must be between min and max values. [val, min, max]: [" + val + "," + minVal + ", " + maxVal + "]");
55 | return (val - minVal) / (maxVal - minVal);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/library/src/main/res/values/integers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1500
4 | 300
5 |
--------------------------------------------------------------------------------
/library/src/main/res/values/likes_attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/library/src/main/res/values/public.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | com.github.iojjj.likeslayout.DrawableAnimator$DefaultFactory
3 | com.github.iojjj.likeslayout.PositionAnimator$DefaultFactory
4 |
5 |
--------------------------------------------------------------------------------
/library/src/test/java/com/github/iojjj/likeslayout/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.github.iojjj.likeslayout;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------