diff --git a/app/src/main/java/ru/ok/technopolis/basketball/CustomView/FieldView.java b/app/src/main/java/ru/ok/technopolis/basketball/CustomView/FieldView.java new file mode 100644 index 0000000..0bf8953 --- /dev/null +++ b/app/src/main/java/ru/ok/technopolis/basketball/CustomView/FieldView.java @@ -0,0 +1,76 @@ +package ru.ok.technopolis.basketball.CustomView; + +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.support.annotation.Nullable; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.view.View; + +import ru.ok.technopolis.basketball.R; + +public class FieldView extends View { + + private Float currentX; + private Float currentY; + private Paint dotPaint = new Paint(); + private Path dotPath = new Path(); + private int pointRadius; + private static final int DEFAULT_POINT_COLOR = Color.BLACK; + public FieldView(Context context) { + super(context); + } + + public FieldView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + int pointStrokeWidthFromAttr = Utils.pxFromDP(displayMetrics, R.dimen.default_point_stroke_width); + int pointRadiusFromAttr = Utils.pxFromDP(displayMetrics, R.dimen.default_point_radius); + int pointColorFromAttr = DEFAULT_POINT_COLOR; + if (attrs != null) { + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FieldView); + pointStrokeWidthFromAttr = typedArray.getDimensionPixelOffset(R.styleable.FieldView_pointStrokeWidth, pointStrokeWidthFromAttr); + pointRadiusFromAttr = typedArray.getDimensionPixelSize(R.styleable.FieldView_pointRadius, pointRadiusFromAttr); + pointColorFromAttr = typedArray.getColor(R.styleable.FieldView_pointColor, pointColorFromAttr); + typedArray.recycle(); + } + + dotPaint.setStyle(Paint.Style.STROKE); + dotPaint.setColor(pointColorFromAttr); + dotPaint.setStrokeWidth(pointStrokeWidthFromAttr); + pointRadius = pointRadiusFromAttr; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onDraw(Canvas canvas) { + if (currentX == null || currentY == null) { + canvas.drawPath(dotPath, dotPaint); + return; + } + dotPath.addCircle(currentX, currentY, pointRadius, Path.Direction.CW); + canvas.drawPath(dotPath, dotPaint); + } + + public void drawDot(float x, float y) { + currentX = x; + currentY = y; + invalidate(); + } + + public void clearField() { + dotPath.reset(); + currentX = null; + currentY = null; + invalidate(); + } +} diff --git a/app/src/main/java/ru/ok/technopolis/basketball/CustomView/ScoreView.java b/app/src/main/java/ru/ok/technopolis/basketball/CustomView/ScoreView.java new file mode 100644 index 0000000..a0f348d --- /dev/null +++ b/app/src/main/java/ru/ok/technopolis/basketball/CustomView/ScoreView.java @@ -0,0 +1,77 @@ +package ru.ok.technopolis.basketball.CustomView; + +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.support.annotation.Nullable; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; + +import ru.ok.technopolis.basketball.R; + +public class ScoreView extends View { + + private static final int DEFAULT_MARK_COLOR = Color.BLACK; + private Path markPath = new Path(); + private Paint markPaint = new Paint(); + + public ScoreView(Context context) { + super(context); + } + + public ScoreView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + int markStrokeWidthFromAttr = Utils.pxFromDP(displayMetrics, R.dimen.default_point_stroke_width); + int markColorFromAttr = DEFAULT_MARK_COLOR; + if (attrs != null) { + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ScoreView); + markStrokeWidthFromAttr = typedArray.getDimensionPixelSize(R.styleable.ScoreView_markStrokeWidth, markStrokeWidthFromAttr); + markColorFromAttr = typedArray.getColor(R.styleable.ScoreView_markColor, markColorFromAttr); + typedArray.recycle(); + } + markPaint.setStyle(Paint.Style.STROKE); + markPaint.setColor(markColorFromAttr); + markPaint.setStrokeWidth(markStrokeWidthFromAttr); + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawPath(markPath, markPaint); + } + + public void setMark(int mark) { + resetMark(); + int leftPadding = getPaddingLeft(); + int rightPadding = getPaddingRight(); + int topPadding = getPaddingTop(); + int bottomPadding = getPaddingBottom(); + int width = getWidth() / 5 - leftPadding - rightPadding; + int xStart = width / 2 + leftPadding; + int yStart = getHeight() - bottomPadding; + int xEnd; + int yEnd; + for (int i = 0; i < mark; i++) { + markPath.moveTo(xStart, yStart); + xEnd = xStart + width / 2; + yEnd = yStart - getHeight() + topPadding + bottomPadding; + markPath.lineTo(xEnd, yEnd); + markPath.moveTo(xStart, yStart); + xEnd = xStart - width / 2; + yEnd = yStart - getHeight() / 2 + bottomPadding; + markPath.lineTo(xEnd, yEnd); + xStart += (width + leftPadding + rightPadding); + } + invalidate(); + } + + public void resetMark() { + markPath.reset(); + invalidate(); + } +} diff --git a/app/src/main/java/ru/ok/technopolis/basketball/CustomView/Utils.java b/app/src/main/java/ru/ok/technopolis/basketball/CustomView/Utils.java new file mode 100644 index 0000000..f86231c --- /dev/null +++ b/app/src/main/java/ru/ok/technopolis/basketball/CustomView/Utils.java @@ -0,0 +1,10 @@ +package ru.ok.technopolis.basketball.CustomView; + +import android.util.DisplayMetrics; +import android.util.TypedValue; + +public final class Utils { + public static int pxFromDP(DisplayMetrics displayMetrics, int res){ + return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, res, displayMetrics) + 0.5f); + } +} diff --git a/app/src/main/java/ru/ok/technopolis/basketball/MainActivity.java b/app/src/main/java/ru/ok/technopolis/basketball/MainActivity.java index 8f71141..8e1d42f 100644 --- a/app/src/main/java/ru/ok/technopolis/basketball/MainActivity.java +++ b/app/src/main/java/ru/ok/technopolis/basketball/MainActivity.java @@ -1,13 +1,135 @@ package ru.ok.technopolis.basketball; -import android.support.v7.app.AppCompatActivity; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.content.Context; import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.animation.LinearInterpolator; +import android.widget.Toast; + +import ru.ok.technopolis.basketball.CustomView.FieldView; +import ru.ok.technopolis.basketball.CustomView.ScoreView; -public class MainActivity extends AppCompatActivity { +public class MainActivity extends AppCompatActivity implements View.OnTouchListener { + private View ball; + private FieldView fieldView; + private ScoreView scoreView; + private View hoop; + private View player; + private VelocityTracker velocityTracker; + private Point startPoint; + private MotionOfProjectile motionOfProjectile; + private float currentX; + private float currentY; + private Context context; + private float G = 9800; + private float maxDistance; + private ValueAnimator animator; + + @SuppressLint("ClickableViewAccessibility") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + fieldView = findViewById(R.id.field_view_basketball_field); + fieldView.setOnTouchListener(this); + ball = findViewById(R.id.image_ball); + float density = getResources().getDisplayMetrics().density; + hoop = findViewById(R.id.image_hoop); + player = findViewById(R.id.image_player); + scoreView = findViewById(R.id.score_view_score); + context = this; + G = G / density; + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + velocityTracker = VelocityTracker.obtain(); + break; + case MotionEvent.ACTION_UP: + fieldView.clearField(); + scoreView.resetMark(); + velocityTracker.computeCurrentVelocity(1000); + calculateInitialCondition(event.getX(), event.getY(), velocityTracker.getXVelocity(), velocityTracker.getYVelocity()); + initialiseAnimation(); + velocityTracker.recycle(); + break; + case MotionEvent.ACTION_MOVE: + velocityTracker.addMovement(event); + break; + case MotionEvent.ACTION_CANCEL: + break; + } + ball.setTranslationX(event.getX() - ball.getLeft() - ball.getWidth() / 2); + ball.setTranslationY(event.getY() - ball.getTop() - ball.getHeight() / 2); + return true; + } + + void calculateInitialCondition(float x0, float y0, float vx0, float vy0) { + startPoint = new Point(x0, y0, vx0, vy0); + startPoint.changeVerticalAxisTo(fieldView.getHeight()); + motionOfProjectile = new MotionOfProjectile(startPoint, G); + } + + void initialiseAnimation() { + if (isScored(startPoint.getX(), fieldView.getHeight() - startPoint.getY())) { + Toast.makeText(context, R.string.cheater, Toast.LENGTH_SHORT).show(); + return; + } + float timeStart = 0; + animator = ValueAnimator.ofFloat(timeStart, motionOfProjectile.getTimeFlight()); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + currentX = motionOfProjectile.getXFromTime((float) animation.getAnimatedValue()); + currentY = fieldView.getHeight() - motionOfProjectile.getYFromTime((float) animation.getAnimatedValue()); + ball.setX(currentX - ball.getWidth() / 2); + ball.setY(currentY - ball.getHeight() / 2); + if (isScored(currentX, currentY)) { + Toast.makeText(context, R.string.goll, Toast.LENGTH_SHORT).show(); + maxDistance = Point.getDistance(player.getWidth(), fieldView.getHeight() - player.getHeight(), currentX, currentY); + startPoint.changeVerticalAxisTo(fieldView.getHeight()); + scoreView.setMark(calculateScore(maxDistance, startPoint.getDistance(currentX, currentY))); + + } + fieldView.drawDot(currentX, currentY); + if (animation.getAnimatedValue().equals(motionOfProjectile.getTimeFlight())) { + Toast.makeText(context, R.string.not_goll, Toast.LENGTH_SHORT).show(); + } + } + }); + animator.setInterpolator(new LinearInterpolator()); + animator.setDuration(2000); + animator.start(); + } + + private boolean isScored(float x, float y) { + if (x > (hoop.getLeft() + hoop.getWidth() / 2 - hoop.getWidth() / 6) && x < (hoop.getLeft() + hoop.getWidth() / 2 + hoop.getWidth() / 6)) { + if (y > (hoop.getTop() + hoop.getHeight() / 3) && y < (hoop.getTop() + hoop.getHeight() / 2 + hoop.getHeight() / 3)) { + return true; + } + } + return false; + } + + @Override + protected void onPause() { + super.onPause(); + if (animator != null){ + animator.cancel(); + } + } + + private int calculateScore(float maxDist, float dist) { + int score = (int) (dist / (maxDist / 4)) + 1; + return score > 5 ? 5 : score; } } diff --git a/app/src/main/java/ru/ok/technopolis/basketball/MotionOfProjectile.java b/app/src/main/java/ru/ok/technopolis/basketball/MotionOfProjectile.java new file mode 100644 index 0000000..1d392f5 --- /dev/null +++ b/app/src/main/java/ru/ok/technopolis/basketball/MotionOfProjectile.java @@ -0,0 +1,26 @@ +package ru.ok.technopolis.basketball; + +public class MotionOfProjectile { + + private Point point; + private float G; + + MotionOfProjectile(Point point, float G) { + this.point = point; + this.G = G; + } + + public float getTimeFlight() { + float t1 = (float) (-point.getVy() + Math.sqrt(point.getVy() * point.getVy() + 2 * G * point.getY())) / (-G); + float t2 = (float) (-point.getVy() - Math.sqrt(point.getVy() * point.getVy() + 2 * G * point.getY())) / (-G); + return t1 > t2 ? t1 : t2; + } + + public float getXFromTime(float t) { + return point.getX() + point.getVx() * t; + } + + public float getYFromTime(float t) { + return (point.getY() + point.getVy() * t - G * t * t / 2); + } +} diff --git a/app/src/main/java/ru/ok/technopolis/basketball/Point.java b/app/src/main/java/ru/ok/technopolis/basketball/Point.java new file mode 100644 index 0000000..0543150 --- /dev/null +++ b/app/src/main/java/ru/ok/technopolis/basketball/Point.java @@ -0,0 +1,53 @@ +package ru.ok.technopolis.basketball; + +public class Point { + + private float x; + private float y; + private float vx; + private float vy; + + public Point(float coordinate) { + this(coordinate, coordinate); + } + + public Point(float x, float y) { + this(x, y, 0, 0); + } + + public Point(float x, float y, float vx, float vy) { + this.x = x; + this.y = y; + this.vx = vx; + this.vy = vy; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public float getVx() { + return vx; + } + + public float getVy() { + return vy; + } + + public float getDistance(float x, float y) { + return (float) Math.sqrt((this.x - x) * (this.x - x) + (this.y - y) * (this.y - y)); + } + + public static float getDistance(float x1, float y1, float x2, float y2) { + return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + } + + public void changeVerticalAxisTo(float y0) { + y = y0 - y; + vy = -vy; + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 037bef1..34e6033 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,10 +1,49 @@ - + - \ No newline at end of file + + + + + + + + diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..f1d94f4 --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml new file mode 100644 index 0000000..8f62931 --- /dev/null +++ b/app/src/main/res/values/dimen.xml @@ -0,0 +1,5 @@ + + + 5dp + 5dp + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa967e1..bd0712d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,6 @@ basketball_homework + Забил гол! + Не забил. + Играй честно. diff --git a/build.gradle b/build.gradle index e11a5b3..02199bb 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:3.4.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d8eb277..31e6ac7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Mar 24 00:28:03 MSK 2019 +#Tue May 21 19:39:42 MSK 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip