Extract DragButton into a separate module
This commit is contained in:
@@ -1,109 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static android.graphics.Color.BLACK;
|
||||
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
|
||||
import static android.util.TypedValue.applyDimension;
|
||||
import static org.solovyev.android.views.dragbutton.DirectionTextView.SHADOW_RADIUS_DPS;
|
||||
|
||||
public class DirectionDragButton extends DragButton implements DirectionDragView {
|
||||
private final DirectionTextView textView = new DirectionTextView();
|
||||
@NonNull
|
||||
private final TextPaint baseTextPaint = new TextPaint();
|
||||
private boolean highContrast;
|
||||
|
||||
public DirectionDragButton(Context context) {
|
||||
super(context);
|
||||
init(null);
|
||||
}
|
||||
|
||||
public DirectionDragButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
public DirectionDragButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
public DirectionDragButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
private void init(@Nullable AttributeSet attrs) {
|
||||
textView.init(this, attrs);
|
||||
baseTextPaint.set(getPaint());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
final TextPaint paint = getPaint();
|
||||
if (baseTextPaint.getTextSize() != paint.getTextSize() ||
|
||||
baseTextPaint.getTypeface() != paint.getTypeface() ||
|
||||
baseTextPaint.getColor() != paint.getColor() ||
|
||||
baseTextPaint.getAlpha() != paint.getAlpha()) {
|
||||
baseTextPaint.set(paint);
|
||||
textView.setBaseTextPaint(paint);
|
||||
}
|
||||
textView.draw(canvas);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getTextValue(@NonNull DragDirection direction) {
|
||||
return getText(direction).getValue();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public DirectionDragButton setText(@NonNull DragDirection direction, @NonNull String value) {
|
||||
getText(direction).setValue(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public DirectionTextView.Text getText(@NonNull DragDirection direction) {
|
||||
return textView.getText(direction);
|
||||
}
|
||||
|
||||
public void setShowDirectionText(@NonNull DragDirection direction, boolean show) {
|
||||
getText(direction).setVisible(show);
|
||||
}
|
||||
|
||||
public void setDirectionTextColor(@ColorInt int color) {
|
||||
for (DragDirection direction : DragDirection.values()) {
|
||||
getText(direction).setColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDirectionTextAlpha(float alpha) {
|
||||
for (DragDirection direction : DragDirection.values()) {
|
||||
getText(direction).setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHighContrast(boolean highContrast) {
|
||||
if(this.highContrast == highContrast) {
|
||||
return;
|
||||
}
|
||||
this.highContrast = highContrast;
|
||||
this.textView.setHighContrast(highContrast);
|
||||
if (highContrast && DirectionTextView.needsShadow(getCurrentTextColor())) {
|
||||
setShadowLayer(applyDimension(COMPLEX_UNIT_DIP, SHADOW_RADIUS_DPS, getResources().getDisplayMetrics()), 0, 0, BLACK);
|
||||
} else {
|
||||
setShadowLayer(0, 0, 0, BLACK);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Typeface;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class DirectionDragImageButton extends DragImageButton implements DirectionDragView {
|
||||
private final DirectionTextView textView = new DirectionTextView();
|
||||
private final TextPaint baseTextPaint = new TextPaint();
|
||||
|
||||
public DirectionDragImageButton(Context context) {
|
||||
super(context);
|
||||
init(null);
|
||||
}
|
||||
|
||||
public DirectionDragImageButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
public DirectionDragImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
public DirectionDragImageButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
private void init(@Nullable AttributeSet attrs) {
|
||||
final TextView view = new TextView(getContext(), attrs);
|
||||
baseTextPaint.set(view.getPaint());
|
||||
textView.init(this, attrs, baseTextPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
textView.draw(canvas);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public DirectionTextView.Text getText(@NonNull DragDirection direction) {
|
||||
return textView.getText(direction);
|
||||
}
|
||||
|
||||
public void setTypeface(@Nonnull Typeface newTypeface) {
|
||||
final Typeface oldTypeface = baseTextPaint.getTypeface();
|
||||
if (oldTypeface == newTypeface) {
|
||||
return;
|
||||
}
|
||||
baseTextPaint.setTypeface(newTypeface);
|
||||
textView.setBaseTextPaint(baseTextPaint);
|
||||
}
|
||||
|
||||
public void setTextSize(float textSizePxs) {
|
||||
baseTextPaint.setTextSize(textSizePxs);
|
||||
textView.setBaseTextPaint(baseTextPaint);
|
||||
}
|
||||
|
||||
public float getTextSize() {
|
||||
return baseTextPaint.getTextSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHighContrast(boolean highContrast) {
|
||||
}
|
||||
}
|
@@ -1,71 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import org.solovyev.android.calculator.R;
|
||||
|
||||
import static java.lang.Math.toDegrees;
|
||||
import static org.solovyev.android.views.dragbutton.Drag.distance;
|
||||
import static org.solovyev.android.views.dragbutton.Drag.getAngle;
|
||||
import static org.solovyev.android.views.dragbutton.Drag.sum;
|
||||
|
||||
|
||||
public abstract class DirectionDragListener implements DragListener {
|
||||
|
||||
@NonNull
|
||||
private static final PointF axis = new PointF(0, 1);
|
||||
|
||||
private final float minDistancePxs;
|
||||
private final boolean[] right = new boolean[1];
|
||||
|
||||
public DirectionDragListener(@NonNull Context context) {
|
||||
this.minDistancePxs = context.getResources().getDimensionPixelSize(R.dimen.drag_min_distance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDrag(@NonNull View view, @NonNull DragEvent e) {
|
||||
final long duration = e.motionEvent.getEventTime() - e.motionEvent.getDownTime();
|
||||
if (duration < 40 || duration > 2500) {
|
||||
Log.v("DirectionDragListener", "Drag stopped: too fast movement, " + duration + "ms");
|
||||
return false;
|
||||
}
|
||||
|
||||
final float distance = distance(e.start, e.end);
|
||||
if (distance < minDistancePxs) {
|
||||
Log.v("DirectionDragListener", "Drag stopped: too short distance, " + distance + "pxs");
|
||||
return false;
|
||||
}
|
||||
|
||||
final double angle = toDegrees(getAngle(e.start, sum(e.start, axis), e.end, right));
|
||||
final DragDirection direction = getDirection((float) angle, right[0]);
|
||||
if (direction == null) {
|
||||
Log.v("DirectionDragListener", "Drag stopped: unknown direction");
|
||||
return false;
|
||||
}
|
||||
|
||||
return onDrag(view, e, direction);
|
||||
}
|
||||
|
||||
protected abstract boolean onDrag(@NonNull View view, @NonNull DragEvent event, @NonNull DragDirection direction);
|
||||
|
||||
@Nullable
|
||||
private static DragDirection getDirection(float angle, boolean right) {
|
||||
for (DragDirection direction : DragDirection.values()) {
|
||||
if (direction == DragDirection.left && right) {
|
||||
continue;
|
||||
}
|
||||
if (direction == DragDirection.right && !right) {
|
||||
continue;
|
||||
}
|
||||
if (direction.angleFrom <= angle && angle <= direction.angleTo) {
|
||||
return direction;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public interface DirectionDragView extends DragView {
|
||||
@Nonnull
|
||||
DirectionTextView.Text getText(@NonNull DragDirection direction);
|
||||
}
|
@@ -1,316 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.graphics.ColorUtils;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import com.google.common.base.Strings;
|
||||
import org.solovyev.android.Check;
|
||||
import org.solovyev.android.calculator.R;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static android.graphics.Color.BLACK;
|
||||
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
|
||||
import static android.util.TypedValue.applyDimension;
|
||||
|
||||
public class DirectionTextView {
|
||||
|
||||
public static final float DEF_ALPHA = 0.4f;
|
||||
public static final float DEF_SCALE = 0.4f;
|
||||
public static final float SHADOW_RADIUS_DPS = 2;
|
||||
|
||||
@NonNull
|
||||
private final Map<DragDirection, Text> texts = new EnumMap<>(DragDirection.class);
|
||||
|
||||
public DirectionTextView() {
|
||||
}
|
||||
|
||||
public void init(@NonNull TextView view, @Nullable AttributeSet attrs) {
|
||||
init(view, attrs, view.getPaint());
|
||||
}
|
||||
|
||||
public void setBaseTextPaint(@NonNull TextPaint baseTextPaint) {
|
||||
for (Text text : texts.values()) {
|
||||
text.initPaint(baseTextPaint);
|
||||
}
|
||||
}
|
||||
|
||||
public void init(@NonNull View view, @Nullable AttributeSet attrs, @NonNull TextPaint baseTextPaint) {
|
||||
Check.isTrue(texts.isEmpty());
|
||||
final Context context = view.getContext();
|
||||
final int defColor = baseTextPaint.getColor();
|
||||
final int defPadding = context.getResources().getDimensionPixelSize(R.dimen.cpp_direction_text_default_padding);
|
||||
final float minTextSize = context.getResources().getDimensionPixelSize(R.dimen.cpp_direction_text_min_size);
|
||||
|
||||
|
||||
if (attrs == null) {
|
||||
for (DragDirection direction : DragDirection.values()) {
|
||||
final Text text = new Text(direction, view, minTextSize);
|
||||
text.init(baseTextPaint, null, DEF_SCALE, defColor, DEF_ALPHA, defPadding);
|
||||
texts.put(direction, text);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DirectionText);
|
||||
final float scale = array.getFloat(R.styleable.DirectionText_directionTextScale, DEF_SCALE);
|
||||
final float alpha = array.getFloat(R.styleable.DirectionText_directionTextAlpha, DEF_ALPHA);
|
||||
final int color = array.getColor(R.styleable.DirectionText_directionTextColor, defColor);
|
||||
final int padding = array.getDimensionPixelSize(R.styleable.DirectionText_directionTextPadding, defPadding);
|
||||
for (DragDirection direction : DragDirection.values()) {
|
||||
final Text text = new Text(direction, view, minTextSize);
|
||||
text.init(baseTextPaint, array, scale, color, alpha, padding);
|
||||
texts.put(direction, text);
|
||||
}
|
||||
array.recycle();
|
||||
}
|
||||
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
for (Text text : texts.values()) {
|
||||
text.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Text getText(@NonNull DragDirection direction) {
|
||||
return texts.get(direction);
|
||||
}
|
||||
|
||||
public void setHighContrast(boolean highContrast) {
|
||||
for (Text text : texts.values()) {
|
||||
text.setHighContrast(highContrast);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Text {
|
||||
public final Rect bounds = new Rect();
|
||||
@NonNull
|
||||
private final TextPaint paint = new TextPaint();
|
||||
@NonNull
|
||||
private final DragDirection direction;
|
||||
@NonNull
|
||||
private final View view;
|
||||
private final float minTextSize;
|
||||
@NonNull
|
||||
private final PointF offset = new PointF(0, 0);
|
||||
private float fixedTextHeight = 0;
|
||||
@NonNull
|
||||
private String value = "";
|
||||
private float scale;
|
||||
private int color;
|
||||
private int contrastColor;
|
||||
private float alpha;
|
||||
private boolean visible = true;
|
||||
private boolean highContrast;
|
||||
private int padding;
|
||||
|
||||
public Text(@NonNull DragDirection direction, @NonNull View view, float minTextSize) {
|
||||
this.direction = direction;
|
||||
this.view = view;
|
||||
this.minTextSize = minTextSize;
|
||||
}
|
||||
|
||||
public void init(@NonNull TextPaint base, @Nullable TypedArray array, float defScale, int defColor, float defAlpha, int defPadding) {
|
||||
if (array != null) {
|
||||
if (array.hasValue(direction.textAttr)) {
|
||||
value = Strings.nullToEmpty(array.getString(direction.textAttr));
|
||||
}
|
||||
padding = array.getDimensionPixelSize(direction.paddingAttr, defPadding);
|
||||
scale = array.getFloat(direction.scaleAttr, defScale);
|
||||
} else {
|
||||
value = "";
|
||||
scale = defScale;
|
||||
padding = defPadding;
|
||||
}
|
||||
alpha = defAlpha;
|
||||
color = defColor;
|
||||
contrastColor = makeContrastColor(color);
|
||||
initPaint(base);
|
||||
}
|
||||
|
||||
private int makeContrastColor(int color) {
|
||||
final int colorRes = isLightColor(color) ? R.color.cpp_button_text : R.color.cpp_text_inverse;
|
||||
return ContextCompat.getColor(view.getContext(), colorRes);
|
||||
}
|
||||
|
||||
public void initPaint(@NonNull TextPaint base) {
|
||||
paint.set(base);
|
||||
paint.setColor(color);
|
||||
paint.setAlpha(intAlpha());
|
||||
final Typeface typeface = base.getTypeface();
|
||||
if (typeface != null && typeface.getStyle() != Typeface.NORMAL) {
|
||||
paint.setTypeface(Typeface.create(typeface, Typeface.NORMAL));
|
||||
}
|
||||
|
||||
// pre-calculate fixed height
|
||||
paint.setTextSize(Math.max(base.getTextSize() * DEF_SCALE, minTextSize));
|
||||
paint.getTextBounds("|", 0, 1, bounds);
|
||||
fixedTextHeight = bounds.height();
|
||||
|
||||
// set real text size value
|
||||
paint.setTextSize(Math.max(base.getTextSize() * scale, minTextSize));
|
||||
|
||||
initPaintShadow();
|
||||
invalidate(true);
|
||||
}
|
||||
|
||||
private void initPaintShadow() {
|
||||
if (highContrast && needsShadow(color)) {
|
||||
paint.setShadowLayer(applyDimension(COMPLEX_UNIT_DIP, SHADOW_RADIUS_DPS, view.getResources().getDisplayMetrics()), 0, 0, BLACK);
|
||||
} else {
|
||||
paint.setShadowLayer(0, 0, 0, BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
private int intAlpha() {
|
||||
return (int) (255 * alpha);
|
||||
}
|
||||
|
||||
public void setVisible(boolean visible) {
|
||||
if (this.visible == visible) {
|
||||
return;
|
||||
}
|
||||
this.visible = visible;
|
||||
invalidate(false);
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
setColor(color, alpha);
|
||||
}
|
||||
|
||||
public void setAlpha(float alpha) {
|
||||
setColor(color, alpha);
|
||||
}
|
||||
|
||||
public void setHighContrast(boolean highContrast) {
|
||||
if (this.highContrast == highContrast) {
|
||||
return;
|
||||
}
|
||||
this.highContrast = highContrast;
|
||||
initPaintShadow();
|
||||
invalidate(false);
|
||||
}
|
||||
|
||||
public void setColor(int color, float alpha) {
|
||||
if (this.color == color && this.alpha == alpha) {
|
||||
return;
|
||||
}
|
||||
this.color = color;
|
||||
this.contrastColor = makeContrastColor(color);
|
||||
this.alpha = alpha;
|
||||
paint.setColor(color);
|
||||
paint.setAlpha(intAlpha());
|
||||
initPaintShadow();
|
||||
invalidate(false);
|
||||
}
|
||||
|
||||
private void invalidate(boolean remeasure) {
|
||||
Check.isNotNull(view);
|
||||
view.invalidate();
|
||||
if (remeasure) {
|
||||
offset.set(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
if (!hasValue()) {
|
||||
return;
|
||||
}
|
||||
if (offset.x == 0 && offset.y == 0) {
|
||||
calculatePosition();
|
||||
}
|
||||
if (highContrast) {
|
||||
paint.setColor(contrastColor);
|
||||
paint.setAlpha(255);
|
||||
}
|
||||
final int width = view.getWidth();
|
||||
final int height = view.getHeight();
|
||||
switch (direction) {
|
||||
case up:
|
||||
canvas.drawText(value, width + offset.x, offset.y, paint);
|
||||
break;
|
||||
case down:
|
||||
canvas.drawText(value, width + offset.x, height + offset.y, paint);
|
||||
break;
|
||||
case left:
|
||||
canvas.drawText(value, offset.x, height / 2 + offset.y, paint);
|
||||
break;
|
||||
case right:
|
||||
canvas.drawText(value, width + offset.x, height / 2 + offset.y, paint);
|
||||
break;
|
||||
}
|
||||
if (highContrast) {
|
||||
paint.setColor(color);
|
||||
paint.setAlpha(intAlpha());
|
||||
}
|
||||
}
|
||||
|
||||
private void calculatePosition() {
|
||||
paint.getTextBounds(value, 0, value.length(), bounds);
|
||||
|
||||
final int paddingLeft = padding;
|
||||
final int paddingRight = padding;
|
||||
final int paddingTop = padding;
|
||||
final int paddingBottom = padding;
|
||||
|
||||
switch (direction) {
|
||||
case up:
|
||||
case down:
|
||||
offset.x = -paddingLeft - bounds.width() - bounds.left;
|
||||
if (direction == DragDirection.up) {
|
||||
offset.y = paddingTop + fixedTextHeight;
|
||||
} else {
|
||||
offset.y = -paddingBottom;
|
||||
}
|
||||
break;
|
||||
case left:
|
||||
case right:
|
||||
if (direction == DragDirection.left) {
|
||||
offset.x = paddingLeft;
|
||||
} else {
|
||||
offset.x = -paddingRight - bounds.width();
|
||||
}
|
||||
offset.y = (paddingTop - paddingBottom) / 2 + fixedTextHeight / 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getValue() {
|
||||
return visible ? value : "";
|
||||
}
|
||||
|
||||
public void setValue(@NonNull String value) {
|
||||
if (TextUtils.equals(this.value, value)) {
|
||||
return;
|
||||
}
|
||||
this.value = value;
|
||||
invalidate(true);
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return visible && !TextUtils.isEmpty(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isLightColor(@ColorInt int color) {
|
||||
return ColorUtils.calculateLuminance(color) > 0.5f;
|
||||
}
|
||||
|
||||
public static boolean needsShadow(@ColorInt int color) {
|
||||
return isLightColor(color);
|
||||
}
|
||||
}
|
@@ -1,64 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
|
||||
public final class Drag {
|
||||
|
||||
private Drag() {
|
||||
}
|
||||
|
||||
public static float distance(@NonNull PointF start,
|
||||
@NonNull PointF end) {
|
||||
return norm(end.x - start.x, end.y - start.y);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static PointF subtract(@NonNull PointF p1, @NonNull PointF p2) {
|
||||
return new PointF(p1.x - p2.x, p1.y - p2.y);
|
||||
}
|
||||
|
||||
public static boolean hasDirectionText(@NonNull View view, @NonNull DragDirection direction) {
|
||||
if (view instanceof DirectionDragView) {
|
||||
return ((DirectionDragView) view).getText(direction).hasValue();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static PointF sum(@NonNull PointF p1, @NonNull PointF p2) {
|
||||
return new PointF(p1.x + p2.x, p1.y + p2.y);
|
||||
}
|
||||
|
||||
public static float norm(@NonNull PointF point) {
|
||||
return norm(point.x, point.y);
|
||||
}
|
||||
|
||||
private static float norm(float x, float y) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
return (float) Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
|
||||
}
|
||||
|
||||
public static float getAngle(@NonNull PointF start,
|
||||
@NonNull PointF axisEnd,
|
||||
@NonNull PointF end,
|
||||
@Nullable boolean[] right) {
|
||||
final PointF axisVector = subtract(axisEnd, start);
|
||||
final PointF vector = subtract(end, start);
|
||||
|
||||
double a_2 = Math.pow(distance(vector, axisVector), 2);
|
||||
double b = norm(vector);
|
||||
double b_2 = Math.pow(b, 2);
|
||||
double c = norm(axisVector);
|
||||
double c_2 = Math.pow(c, 2);
|
||||
|
||||
if (right != null) {
|
||||
right[0] = axisVector.x * vector.y - axisVector.y * vector.x < 0;
|
||||
}
|
||||
|
||||
return (float) Math.acos((-a_2 + b_2 + c_2) / (2 * b * c));
|
||||
}
|
||||
|
||||
}
|
@@ -1,53 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.Button;
|
||||
|
||||
public abstract class DragButton extends Button implements DragView {
|
||||
@NonNull
|
||||
private final DragGestureDetector dragDetector = new DragGestureDetector(this);
|
||||
|
||||
public DragButton(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public DragButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public DragButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public DragButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (dragDetector.onTouchEvent(event)) {
|
||||
final MotionEvent cancelEvent = DragGestureDetector.makeCancelEvent(event);
|
||||
super.onTouchEvent(cancelEvent);
|
||||
cancelEvent.recycle();
|
||||
return true;
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnDragListener(@Nullable DragListener listener) {
|
||||
dragDetector.setListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVibrateOnDrag(boolean vibrateOnDrag) {
|
||||
dragDetector.setVibrateOnDrag(vibrateOnDrag);
|
||||
}
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.support.annotation.StyleableRes;
|
||||
|
||||
import org.solovyev.android.calculator.R;
|
||||
|
||||
public enum DragDirection {
|
||||
|
||||
up(180f - 45f, 180f - 0f, R.styleable.DirectionText_directionTextUp, R.styleable.DirectionText_directionTextScaleUp, R.styleable.DirectionText_directionTextPaddingUp),
|
||||
down(0f, 45f, R.styleable.DirectionText_directionTextDown, R.styleable.DirectionText_directionTextScaleDown, R.styleable.DirectionText_directionTextPaddingDown),
|
||||
left(90f - 45f, 90f + 45f, R.styleable.DirectionText_directionTextLeft, R.styleable.DirectionText_directionTextScaleLeft, R.styleable.DirectionText_directionTextPaddingLeft),
|
||||
right(90f - 45f, 90f + 45f, R.styleable.DirectionText_directionTextRight, R.styleable.DirectionText_directionTextScaleRight, R.styleable.DirectionText_directionTextPaddingRight);
|
||||
|
||||
final float angleFrom;
|
||||
final float angleTo;
|
||||
@StyleableRes
|
||||
final int textAttr;
|
||||
@StyleableRes
|
||||
final int scaleAttr;
|
||||
@StyleableRes
|
||||
final int paddingAttr;
|
||||
|
||||
DragDirection(float angleFrom, float angleTo, @StyleableRes int textAttr, @StyleableRes int scaleAttr, @StyleableRes int paddingAttr) {
|
||||
this.angleFrom = angleFrom;
|
||||
this.angleTo = angleTo;
|
||||
this.textAttr = textAttr;
|
||||
this.scaleAttr = scaleAttr;
|
||||
this.paddingAttr = paddingAttr;
|
||||
}
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class DragEvent {
|
||||
|
||||
@NonNull
|
||||
public final PointF start;
|
||||
|
||||
@NonNull
|
||||
public final PointF end;
|
||||
|
||||
@NonNull
|
||||
public final MotionEvent motionEvent;
|
||||
|
||||
public DragEvent(@NonNull PointF start, @NonNull MotionEvent motionEvent) {
|
||||
this.start = start;
|
||||
this.end = new PointF(motionEvent.getX(), motionEvent.getY());
|
||||
this.motionEvent = motionEvent;
|
||||
}
|
||||
}
|
@@ -1,77 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import org.solovyev.android.Check;
|
||||
|
||||
import static android.view.HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING;
|
||||
import static android.view.HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING;
|
||||
import static android.view.HapticFeedbackConstants.KEYBOARD_TAP;
|
||||
|
||||
public class DragGestureDetector {
|
||||
|
||||
@NonNull
|
||||
private final View view;
|
||||
@Nullable
|
||||
private DragListener listener;
|
||||
@Nullable
|
||||
private PointF start;
|
||||
private boolean vibrateOnDrag = true;
|
||||
|
||||
public DragGestureDetector(@NonNull View view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static MotionEvent makeCancelEvent(@NonNull MotionEvent original) {
|
||||
final MotionEvent event = MotionEvent.obtain(original);
|
||||
event.setAction(MotionEvent.ACTION_CANCEL);
|
||||
return event;
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(@NonNull MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
startTracking(event);
|
||||
return false;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_UP:
|
||||
return stopTracking(event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean stopTracking(@NonNull MotionEvent event) {
|
||||
if (start == null || listener == null) {
|
||||
start = null;
|
||||
return false;
|
||||
}
|
||||
if (!listener.onDrag(view, new DragEvent(start, event))) {
|
||||
start = null;
|
||||
return false;
|
||||
}
|
||||
start = null;
|
||||
if (vibrateOnDrag) {
|
||||
view.performHapticFeedback(KEYBOARD_TAP, FLAG_IGNORE_GLOBAL_SETTING | FLAG_IGNORE_VIEW_SETTING);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setVibrateOnDrag(boolean vibrateOnDrag) {
|
||||
this.vibrateOnDrag = vibrateOnDrag;
|
||||
}
|
||||
|
||||
private void startTracking(@NonNull MotionEvent event) {
|
||||
Check.isNull(start);
|
||||
start = new PointF(event.getX(), event.getY());
|
||||
}
|
||||
|
||||
public void setListener(@Nullable DragListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
public abstract class DragImageButton extends ImageButton implements DragView {
|
||||
@NonNull
|
||||
private final DragGestureDetector dragDetector = new DragGestureDetector(this);
|
||||
|
||||
public DragImageButton(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public DragImageButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public DragImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public DragImageButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (dragDetector.onTouchEvent(event)) {
|
||||
final MotionEvent cancelEvent = DragGestureDetector.makeCancelEvent(event);
|
||||
super.onTouchEvent(cancelEvent);
|
||||
cancelEvent.recycle();
|
||||
return true;
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
public void setOnDragListener(@Nullable DragListener listener) {
|
||||
dragDetector.setListener(listener);
|
||||
}
|
||||
|
||||
public void setVibrateOnDrag(boolean vibrateOnDrag) {
|
||||
dragDetector.setVibrateOnDrag(vibrateOnDrag);
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
|
||||
public interface DragListener extends EventListener {
|
||||
boolean onDrag(@NonNull View view, @NonNull DragEvent event);
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
package org.solovyev.android.views.dragbutton;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
public interface DragView {
|
||||
int getId();
|
||||
void setOnDragListener(@Nullable DragListener listener);
|
||||
void setVibrateOnDrag(boolean vibrateOnDrag);
|
||||
void setHighContrast(boolean highContrast);
|
||||
}
|
@@ -65,4 +65,6 @@
|
||||
<color name="pink_900">#880e4f</color>
|
||||
<color name="yellow_100">#ffff8d</color>
|
||||
|
||||
<color name="drag_text_inverse">@color/cpp_text_inverse</color>
|
||||
<color name="drag_button_text">@color/cpp_button_text</color>
|
||||
</resources>
|
@@ -7,9 +7,6 @@
|
||||
<dimen name="cpp_widget_margin">6dp</dimen>
|
||||
<dimen name="min_expanded_height_lock_screen">300dp</dimen>
|
||||
<dimen name="cpp_button_padding">1px</dimen>
|
||||
<dimen name="cpp_direction_text_default_padding">2dp</dimen>
|
||||
<dimen name="cpp_direction_text_min_size">9dp</dimen>
|
||||
<item name="cpp_direction_text_scale" format="float" type="dimen">0.35</item>
|
||||
<dimen name="cpp_button_corner">1dp</dimen>
|
||||
|
||||
<dimen name="cpp_keyboard_button_direction_text_padding_updown">4dp</dimen>
|
||||
|
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="DirectionText">
|
||||
<attr name="directionTextColor" format="color"/>
|
||||
<attr name="directionTextAlpha" format="float"/>
|
||||
<attr name="directionTextUp" format="string"/>
|
||||
<attr name="directionTextDown" format="string"/>
|
||||
<attr name="directionTextRight" format="string"/>
|
||||
<attr name="directionTextLeft" format="string"/>
|
||||
<attr name="directionTextScale" format="float"/>
|
||||
<attr name="directionTextPadding" format="dimension"/>
|
||||
<attr name="directionTextPaddingUp" format="dimension"/>
|
||||
<attr name="directionTextPaddingDown" format="dimension"/>
|
||||
<attr name="directionTextPaddingRight" format="dimension"/>
|
||||
<attr name="directionTextPaddingLeft" format="dimension"/>
|
||||
<attr name="directionTextScaleUp" format="float"/>
|
||||
<attr name="directionTextScaleDown" format="float"/>
|
||||
<attr name="directionTextScaleRight" format="float"/>
|
||||
<attr name="directionTextScaleLeft" format="float"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="drag_min_distance">15dp</dimen>
|
||||
</resources>
|
@@ -35,7 +35,7 @@
|
||||
<item name="android:textColor">@color/cpp_button_text</item>
|
||||
<item name="android:scaleType">centerInside</item>
|
||||
<item name="directionTextColor">@color/cpp_button_text</item>
|
||||
<item name="directionTextScale">@dimen/cpp_direction_text_scale</item>
|
||||
<item name="directionTextScale">@dimen/drag_direction_text_scale</item>
|
||||
<item name="directionTextPaddingUp">@dimen/cpp_keyboard_button_direction_text_padding_updown</item>
|
||||
<item name="directionTextPaddingDown">@dimen/cpp_keyboard_button_direction_text_padding_updown</item>
|
||||
<item name="directionTextPaddingRight">@dimen/cpp_keyboard_button_direction_text_padding_updown</item>
|
||||
|
Reference in New Issue
Block a user