diff --git a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartAnimationListener.java b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartAnimationListener.java index 29ab82fc..ec87bf74 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartAnimationListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartAnimationListener.java @@ -3,12 +3,11 @@ /** * Listener used to listen for chart animation start and stop events. Implementations of this interface can be used for * all types of chart animations(data, viewport, PieChart rotation). - * */ public interface ChartAnimationListener { - public void onAnimationStarted(); + public void onAnimationStarted(); - public void onAnimationFinished(); + public void onAnimationFinished(); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimator.java b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimator.java index e4208c27..d2ee6d1e 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimator.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimator.java @@ -2,14 +2,14 @@ public interface ChartDataAnimator { - public static final long DEFAULT_ANIMATION_DURATION = 500; + public static final long DEFAULT_ANIMATION_DURATION = 500; - public void startAnimation(long duration); + public void startAnimation(long duration); - public void cancelAnimation(); + public void cancelAnimation(); - public boolean isAnimationStarted(); + public boolean isAnimationStarted(); - public void setChartAnimationListener(ChartAnimationListener animationListener); + public void setChartAnimationListener(ChartAnimationListener animationListener); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimatorV14.java b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimatorV14.java index 4b4da91d..e8148cf1 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimatorV14.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimatorV14.java @@ -1,76 +1,77 @@ package lecho.lib.hellocharts.animation; -import lecho.lib.hellocharts.view.Chart; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.SuppressLint; +import lecho.lib.hellocharts.view.Chart; + @SuppressLint("NewApi") public class ChartDataAnimatorV14 implements ChartDataAnimator, AnimatorListener, AnimatorUpdateListener { - private ValueAnimator animator; - private final Chart chart; - private ChartAnimationListener animationListener = new DummyChartAnimationListener(); + private ValueAnimator animator; + private final Chart chart; + private ChartAnimationListener animationListener = new DummyChartAnimationListener(); - public ChartDataAnimatorV14(Chart chart) { - this.chart = chart; - animator = ValueAnimator.ofFloat(0.0f, 1.0f); - animator.addListener(this); - animator.addUpdateListener(this); - } + public ChartDataAnimatorV14(Chart chart) { + this.chart = chart; + animator = ValueAnimator.ofFloat(0.0f, 1.0f); + animator.addListener(this); + animator.addUpdateListener(this); + } - @Override - public void startAnimation(long duration) { - if (duration >= 0) { - animator.setDuration(duration); - } else { - animator.setDuration(DEFAULT_ANIMATION_DURATION); - } - animator.start(); - } + @Override + public void startAnimation(long duration) { + if (duration >= 0) { + animator.setDuration(duration); + } else { + animator.setDuration(DEFAULT_ANIMATION_DURATION); + } + animator.start(); + } - @Override - public void cancelAnimation() { - animator.cancel(); - } + @Override + public void cancelAnimation() { + animator.cancel(); + } - @Override - public void onAnimationUpdate(ValueAnimator animation) { - chart.animationDataUpdate(animation.getAnimatedFraction()); - } + @Override + public void onAnimationUpdate(ValueAnimator animation) { + chart.animationDataUpdate(animation.getAnimatedFraction()); + } - @Override - public void onAnimationCancel(Animator animation) { - } + @Override + public void onAnimationCancel(Animator animation) { + } - @Override - public void onAnimationEnd(Animator animation) { - chart.animationDataFinished(); - animationListener.onAnimationFinished(); - } + @Override + public void onAnimationEnd(Animator animation) { + chart.animationDataFinished(); + animationListener.onAnimationFinished(); + } - @Override - public void onAnimationRepeat(Animator animation) { - } + @Override + public void onAnimationRepeat(Animator animation) { + } - @Override - public void onAnimationStart(Animator animation) { - animationListener.onAnimationStarted(); - } + @Override + public void onAnimationStart(Animator animation) { + animationListener.onAnimationStarted(); + } - @Override - public boolean isAnimationStarted() { - return animator.isStarted(); - } + @Override + public boolean isAnimationStarted() { + return animator.isStarted(); + } - @Override - public void setChartAnimationListener(ChartAnimationListener animationListener) { - if (null == animationListener) { - this.animationListener = new DummyChartAnimationListener(); - } else { - this.animationListener = animationListener; - } - } + @Override + public void setChartAnimationListener(ChartAnimationListener animationListener) { + if (null == animationListener) { + this.animationListener = new DummyChartAnimationListener(); + } else { + this.animationListener = animationListener; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimatorV8.java b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimatorV8.java index 560233f4..04007974 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimatorV8.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartDataAnimatorV8.java @@ -1,76 +1,77 @@ package lecho.lib.hellocharts.animation; -import lecho.lib.hellocharts.view.Chart; import android.os.Handler; import android.os.SystemClock; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; +import lecho.lib.hellocharts.view.Chart; + public class ChartDataAnimatorV8 implements ChartDataAnimator { - long start; - boolean isAnimationStarted = false; - long duration; - final Chart chart; - final Handler handler; - final Interpolator interpolator = new AccelerateDecelerateInterpolator(); - private ChartAnimationListener animationListener = new DummyChartAnimationListener(); - private final Runnable runnable = new Runnable() { + long start; + boolean isAnimationStarted = false; + long duration; + final Chart chart; + final Handler handler; + final Interpolator interpolator = new AccelerateDecelerateInterpolator(); + private ChartAnimationListener animationListener = new DummyChartAnimationListener(); + private final Runnable runnable = new Runnable() { - @Override - public void run() { - long elapsed = SystemClock.uptimeMillis() - start; - if (elapsed > duration) { - isAnimationStarted = false; - handler.removeCallbacks(runnable); - chart.animationDataFinished(); - return; - } - float scale = Math.min(interpolator.getInterpolation((float) elapsed / duration), 1); - chart.animationDataUpdate(scale); - handler.postDelayed(this, 16); + @Override + public void run() { + long elapsed = SystemClock.uptimeMillis() - start; + if (elapsed > duration) { + isAnimationStarted = false; + handler.removeCallbacks(runnable); + chart.animationDataFinished(); + return; + } + float scale = Math.min(interpolator.getInterpolation((float) elapsed / duration), 1); + chart.animationDataUpdate(scale); + handler.postDelayed(this, 16); - } - }; + } + }; - public ChartDataAnimatorV8(Chart chart) { - this.chart = chart; - this.handler = new Handler(); - } + public ChartDataAnimatorV8(Chart chart) { + this.chart = chart; + this.handler = new Handler(); + } - @Override - public void startAnimation(long duration) { - if (duration >= 0) { - this.duration = duration; - } else { - this.duration = DEFAULT_ANIMATION_DURATION; - } + @Override + public void startAnimation(long duration) { + if (duration >= 0) { + this.duration = duration; + } else { + this.duration = DEFAULT_ANIMATION_DURATION; + } - isAnimationStarted = true; - animationListener.onAnimationStarted(); - start = SystemClock.uptimeMillis(); - handler.post(runnable); - } + isAnimationStarted = true; + animationListener.onAnimationStarted(); + start = SystemClock.uptimeMillis(); + handler.post(runnable); + } - @Override - public void cancelAnimation() { - isAnimationStarted = false; - handler.removeCallbacks(runnable); - chart.animationDataFinished(); - animationListener.onAnimationFinished(); - } + @Override + public void cancelAnimation() { + isAnimationStarted = false; + handler.removeCallbacks(runnable); + chart.animationDataFinished(); + animationListener.onAnimationFinished(); + } - @Override - public boolean isAnimationStarted() { - return isAnimationStarted; - } + @Override + public boolean isAnimationStarted() { + return isAnimationStarted; + } - @Override - public void setChartAnimationListener(ChartAnimationListener animationListener) { - if (null == animationListener) { - this.animationListener = new DummyChartAnimationListener(); - } else { - this.animationListener = animationListener; - } - } + @Override + public void setChartAnimationListener(ChartAnimationListener animationListener) { + if (null == animationListener) { + this.animationListener = new DummyChartAnimationListener(); + } else { + this.animationListener = animationListener; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimator.java b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimator.java index a9fb0fca..3a8858bd 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimator.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimator.java @@ -4,16 +4,16 @@ public interface ChartViewportAnimator { - public static final int FAST_ANIMATION_DURATION = 300; + public static final int FAST_ANIMATION_DURATION = 300; - public void startAnimation(Viewport startViewport, Viewport targetViewport); + public void startAnimation(Viewport startViewport, Viewport targetViewport); - public void startAnimation(Viewport startViewport, Viewport targetViewport, long duration); + public void startAnimation(Viewport startViewport, Viewport targetViewport, long duration); - public void cancelAnimation(); + public void cancelAnimation(); - public boolean isAnimationStarted(); + public boolean isAnimationStarted(); - public void setChartAnimationListener(ChartAnimationListener animationListener); + public void setChartAnimationListener(ChartAnimationListener animationListener); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimatorV14.java b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimatorV14.java index 7ce6e44f..64429cba 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimatorV14.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimatorV14.java @@ -1,94 +1,95 @@ package lecho.lib.hellocharts.animation; -import lecho.lib.hellocharts.model.Viewport; -import lecho.lib.hellocharts.view.Chart; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.SuppressLint; +import lecho.lib.hellocharts.model.Viewport; +import lecho.lib.hellocharts.view.Chart; + @SuppressLint("NewApi") public class ChartViewportAnimatorV14 implements ChartViewportAnimator, AnimatorListener, AnimatorUpdateListener { - private ValueAnimator animator; - private final Chart chart; - private Viewport startViewport = new Viewport(); - private Viewport targetViewport = new Viewport(); - private Viewport newViewport = new Viewport(); - private ChartAnimationListener animationListener = new DummyChartAnimationListener(); + private ValueAnimator animator; + private final Chart chart; + private Viewport startViewport = new Viewport(); + private Viewport targetViewport = new Viewport(); + private Viewport newViewport = new Viewport(); + private ChartAnimationListener animationListener = new DummyChartAnimationListener(); - public ChartViewportAnimatorV14(Chart chart) { - this.chart = chart; - animator = ValueAnimator.ofFloat(0.0f, 1.0f); - animator.addListener(this); - animator.addUpdateListener(this); - animator.setDuration(FAST_ANIMATION_DURATION); - } + public ChartViewportAnimatorV14(Chart chart) { + this.chart = chart; + animator = ValueAnimator.ofFloat(0.0f, 1.0f); + animator.addListener(this); + animator.addUpdateListener(this); + animator.setDuration(FAST_ANIMATION_DURATION); + } - @Override - public void startAnimation(Viewport startViewport, Viewport targetViewport) { - this.startViewport.set(startViewport); - this.targetViewport.set(targetViewport); - animator.setDuration(FAST_ANIMATION_DURATION); - animator.start(); - } + @Override + public void startAnimation(Viewport startViewport, Viewport targetViewport) { + this.startViewport.set(startViewport); + this.targetViewport.set(targetViewport); + animator.setDuration(FAST_ANIMATION_DURATION); + animator.start(); + } - @Override - public void startAnimation(Viewport startViewport, Viewport targetViewport, long duration) { - this.startViewport.set(startViewport); - this.targetViewport.set(targetViewport); - animator.setDuration(duration); - animator.start(); - } + @Override + public void startAnimation(Viewport startViewport, Viewport targetViewport, long duration) { + this.startViewport.set(startViewport); + this.targetViewport.set(targetViewport); + animator.setDuration(duration); + animator.start(); + } - @Override - public void cancelAnimation() { - animator.cancel(); - } + @Override + public void cancelAnimation() { + animator.cancel(); + } - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float scale = animation.getAnimatedFraction(); - float diffLeft = (targetViewport.left - startViewport.left) * scale; - float diffTop = (targetViewport.top - startViewport.top) * scale; - float diffRight = (targetViewport.right - startViewport.right) * scale; - float diffBottom = (targetViewport.bottom - startViewport.bottom) * scale; - newViewport.set(startViewport.left + diffLeft, startViewport.top + diffTop, startViewport.right + diffRight, - startViewport.bottom + diffBottom); - chart.setCurrentViewport(newViewport); - } + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float scale = animation.getAnimatedFraction(); + float diffLeft = (targetViewport.left - startViewport.left) * scale; + float diffTop = (targetViewport.top - startViewport.top) * scale; + float diffRight = (targetViewport.right - startViewport.right) * scale; + float diffBottom = (targetViewport.bottom - startViewport.bottom) * scale; + newViewport.set(startViewport.left + diffLeft, startViewport.top + diffTop, startViewport.right + diffRight, + startViewport.bottom + diffBottom); + chart.setCurrentViewport(newViewport); + } - @Override - public void onAnimationCancel(Animator animation) { - } + @Override + public void onAnimationCancel(Animator animation) { + } - @Override - public void onAnimationEnd(Animator animation) { - chart.setCurrentViewport(targetViewport); - animationListener.onAnimationFinished(); - } + @Override + public void onAnimationEnd(Animator animation) { + chart.setCurrentViewport(targetViewport); + animationListener.onAnimationFinished(); + } - @Override - public void onAnimationRepeat(Animator animation) { - } + @Override + public void onAnimationRepeat(Animator animation) { + } - @Override - public void onAnimationStart(Animator animation) { - animationListener.onAnimationStarted(); - } + @Override + public void onAnimationStart(Animator animation) { + animationListener.onAnimationStarted(); + } - @Override - public boolean isAnimationStarted() { - return animator.isStarted(); - } + @Override + public boolean isAnimationStarted() { + return animator.isStarted(); + } - @Override - public void setChartAnimationListener(ChartAnimationListener animationListener) { - if (null == animationListener) { - this.animationListener = new DummyChartAnimationListener(); - } else { - this.animationListener = animationListener; - } - } + @Override + public void setChartAnimationListener(ChartAnimationListener animationListener) { + if (null == animationListener) { + this.animationListener = new DummyChartAnimationListener(); + } else { + this.animationListener = animationListener; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimatorV8.java b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimatorV8.java index ab1590c6..5226ec6d 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimatorV8.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/animation/ChartViewportAnimatorV8.java @@ -10,91 +10,91 @@ public class ChartViewportAnimatorV8 implements ChartViewportAnimator { - final Chart chart; - final Handler handler; - final Interpolator interpolator = new AccelerateDecelerateInterpolator(); - long start; - boolean isAnimationStarted = false; - private Viewport startViewport = new Viewport(); - private Viewport targetViewport = new Viewport(); - private Viewport newViewport = new Viewport(); - private long duration; - private ChartAnimationListener animationListener = new DummyChartAnimationListener(); + final Chart chart; + final Handler handler; + final Interpolator interpolator = new AccelerateDecelerateInterpolator(); + long start; + boolean isAnimationStarted = false; + private Viewport startViewport = new Viewport(); + private Viewport targetViewport = new Viewport(); + private Viewport newViewport = new Viewport(); + private long duration; + private ChartAnimationListener animationListener = new DummyChartAnimationListener(); - public ChartViewportAnimatorV8(Chart chart) { - this.chart = chart; - this.duration = FAST_ANIMATION_DURATION; - this.handler = new Handler(); - } + public ChartViewportAnimatorV8(Chart chart) { + this.chart = chart; + this.duration = FAST_ANIMATION_DURATION; + this.handler = new Handler(); + } - private final Runnable runnable = new Runnable() { + private final Runnable runnable = new Runnable() { - @Override - public void run() { - long elapsed = SystemClock.uptimeMillis() - start; - if (elapsed > duration) { - isAnimationStarted = false; - handler.removeCallbacks(runnable); - chart.setCurrentViewport(targetViewport); - animationListener.onAnimationFinished(); - return; - } - float scale = Math.min(interpolator.getInterpolation((float) elapsed / duration), 1); - float diffLeft = (targetViewport.left - startViewport.left) * scale; - float diffTop = (targetViewport.top - startViewport.top) * scale; - float diffRight = (targetViewport.right - startViewport.right) * scale; - float diffBottom = (targetViewport.bottom - startViewport.bottom) * scale; - newViewport.set(startViewport.left + diffLeft, startViewport.top + diffTop, - startViewport.right + diffRight, startViewport.bottom + diffBottom); - chart.setCurrentViewport(newViewport); + @Override + public void run() { + long elapsed = SystemClock.uptimeMillis() - start; + if (elapsed > duration) { + isAnimationStarted = false; + handler.removeCallbacks(runnable); + chart.setCurrentViewport(targetViewport); + animationListener.onAnimationFinished(); + return; + } + float scale = Math.min(interpolator.getInterpolation((float) elapsed / duration), 1); + float diffLeft = (targetViewport.left - startViewport.left) * scale; + float diffTop = (targetViewport.top - startViewport.top) * scale; + float diffRight = (targetViewport.right - startViewport.right) * scale; + float diffBottom = (targetViewport.bottom - startViewport.bottom) * scale; + newViewport.set(startViewport.left + diffLeft, startViewport.top + diffTop, + startViewport.right + diffRight, startViewport.bottom + diffBottom); + chart.setCurrentViewport(newViewport); - handler.postDelayed(this, 16); - } - }; + handler.postDelayed(this, 16); + } + }; - @Override - public void startAnimation(Viewport startViewport, Viewport targetViewport) { - this.startViewport.set(startViewport); - this.targetViewport.set(targetViewport); - duration = FAST_ANIMATION_DURATION; - isAnimationStarted = true; - animationListener.onAnimationStarted(); - start = SystemClock.uptimeMillis(); - handler.post(runnable); - } + @Override + public void startAnimation(Viewport startViewport, Viewport targetViewport) { + this.startViewport.set(startViewport); + this.targetViewport.set(targetViewport); + duration = FAST_ANIMATION_DURATION; + isAnimationStarted = true; + animationListener.onAnimationStarted(); + start = SystemClock.uptimeMillis(); + handler.post(runnable); + } - @Override - public void startAnimation(Viewport startViewport, Viewport targetViewport, long duration) { - this.startViewport.set(startViewport); - this.targetViewport.set(targetViewport); - this.duration = duration; - isAnimationStarted = true; - animationListener.onAnimationStarted(); - start = SystemClock.uptimeMillis(); - handler.post(runnable); - } + @Override + public void startAnimation(Viewport startViewport, Viewport targetViewport, long duration) { + this.startViewport.set(startViewport); + this.targetViewport.set(targetViewport); + this.duration = duration; + isAnimationStarted = true; + animationListener.onAnimationStarted(); + start = SystemClock.uptimeMillis(); + handler.post(runnable); + } - @Override - public void cancelAnimation() { - isAnimationStarted = false; - handler.removeCallbacks(runnable); - chart.setCurrentViewport(targetViewport); - animationListener.onAnimationFinished(); - } + @Override + public void cancelAnimation() { + isAnimationStarted = false; + handler.removeCallbacks(runnable); + chart.setCurrentViewport(targetViewport); + animationListener.onAnimationFinished(); + } - @Override - public boolean isAnimationStarted() { - return isAnimationStarted; - } + @Override + public boolean isAnimationStarted() { + return isAnimationStarted; + } - @Override - public void setChartAnimationListener(ChartAnimationListener animationListener) { - if (null == animationListener) { - this.animationListener = new DummyChartAnimationListener(); - } else { - this.animationListener = animationListener; - } - } + @Override + public void setChartAnimationListener(ChartAnimationListener animationListener) { + if (null == animationListener) { + this.animationListener = new DummyChartAnimationListener(); + } else { + this.animationListener = animationListener; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/animation/DummyChartAnimationListener.java b/hellocharts-library/src/lecho/lib/hellocharts/animation/DummyChartAnimationListener.java index c0bef371..5f50c439 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/animation/DummyChartAnimationListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/animation/DummyChartAnimationListener.java @@ -1,19 +1,17 @@ package lecho.lib.hellocharts.animation; -import lecho.lib.hellocharts.animation.ChartAnimationListener; - public class DummyChartAnimationListener implements ChartAnimationListener { - @Override - public void onAnimationStarted() { - // do nothing + @Override + public void onAnimationStarted() { + // do nothing - } + } - @Override - public void onAnimationFinished() { - // do nothing + @Override + public void onAnimationFinished() { + // do nothing - } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimator.java b/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimator.java index fb566b64..2f0da033 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimator.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimator.java @@ -2,14 +2,14 @@ public interface PieChartRotationAnimator { - public static final int FAST_ANIMATION_DURATION = 200; + public static final int FAST_ANIMATION_DURATION = 200; - public void startAnimation(float startAngle, float angleToRotate); + public void startAnimation(float startAngle, float angleToRotate); - public void cancelAnimation(); + public void cancelAnimation(); - public boolean isAnimationStarted(); + public boolean isAnimationStarted(); - public void setChartAnimationListener(ChartAnimationListener animationListener); + public void setChartAnimationListener(ChartAnimationListener animationListener); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimatorV14.java b/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimatorV14.java index 54993ea8..f5eacffa 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimatorV14.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimatorV14.java @@ -1,83 +1,84 @@ package lecho.lib.hellocharts.animation; -import lecho.lib.hellocharts.view.PieChartView; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.SuppressLint; +import lecho.lib.hellocharts.view.PieChartView; + @SuppressLint("NewApi") public class PieChartRotationAnimatorV14 implements PieChartRotationAnimator, AnimatorListener, AnimatorUpdateListener { - private ValueAnimator animator; - private final PieChartView chart; - private float startRotation = 0; - private float targetRotation = 0; - private ChartAnimationListener animationListener = new DummyChartAnimationListener(); + private ValueAnimator animator; + private final PieChartView chart; + private float startRotation = 0; + private float targetRotation = 0; + private ChartAnimationListener animationListener = new DummyChartAnimationListener(); - public PieChartRotationAnimatorV14(PieChartView chart) { - this(chart, FAST_ANIMATION_DURATION); - } + public PieChartRotationAnimatorV14(PieChartView chart) { + this(chart, FAST_ANIMATION_DURATION); + } - public PieChartRotationAnimatorV14(PieChartView chart, long duration) { - this.chart = chart; - animator = ValueAnimator.ofFloat(0.0f, 1.0f); - animator.setDuration(duration); - animator.addListener(this); - animator.addUpdateListener(this); - } + public PieChartRotationAnimatorV14(PieChartView chart, long duration) { + this.chart = chart; + animator = ValueAnimator.ofFloat(0.0f, 1.0f); + animator.setDuration(duration); + animator.addListener(this); + animator.addUpdateListener(this); + } - @Override - public void startAnimation(float startRotation, float targetRotation) { - this.startRotation = (startRotation % 360 + 360) % 360; - this.targetRotation = (targetRotation % 360 + 360) % 360; - animator.start(); - } + @Override + public void startAnimation(float startRotation, float targetRotation) { + this.startRotation = (startRotation % 360 + 360) % 360; + this.targetRotation = (targetRotation % 360 + 360) % 360; + animator.start(); + } - @Override - public void cancelAnimation() { - animator.cancel(); - } + @Override + public void cancelAnimation() { + animator.cancel(); + } - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float scale = animation.getAnimatedFraction(); - float rotation = startRotation + (targetRotation - startRotation) * scale; - rotation = (rotation % 360 + 360) % 360; - chart.setChartRotation((int) rotation, false); - } + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float scale = animation.getAnimatedFraction(); + float rotation = startRotation + (targetRotation - startRotation) * scale; + rotation = (rotation % 360 + 360) % 360; + chart.setChartRotation((int) rotation, false); + } - @Override - public void onAnimationCancel(Animator animation) { - } + @Override + public void onAnimationCancel(Animator animation) { + } - @Override - public void onAnimationEnd(Animator animation) { - chart.setChartRotation((int) targetRotation, false); - animationListener.onAnimationFinished(); - } + @Override + public void onAnimationEnd(Animator animation) { + chart.setChartRotation((int) targetRotation, false); + animationListener.onAnimationFinished(); + } - @Override - public void onAnimationRepeat(Animator animation) { - } + @Override + public void onAnimationRepeat(Animator animation) { + } - @Override - public void onAnimationStart(Animator animation) { - animationListener.onAnimationStarted(); - } + @Override + public void onAnimationStart(Animator animation) { + animationListener.onAnimationStarted(); + } - @Override - public boolean isAnimationStarted() { - return animator.isStarted(); - } + @Override + public boolean isAnimationStarted() { + return animator.isStarted(); + } - @Override - public void setChartAnimationListener(ChartAnimationListener animationListener) { - if (null == animationListener) { - this.animationListener = new DummyChartAnimationListener(); - } else { - this.animationListener = animationListener; - } - } + @Override + public void setChartAnimationListener(ChartAnimationListener animationListener) { + if (null == animationListener) { + this.animationListener = new DummyChartAnimationListener(); + } else { + this.animationListener = animationListener; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimatorV8.java b/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimatorV8.java index df984e02..524670c5 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimatorV8.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/animation/PieChartRotationAnimatorV8.java @@ -1,81 +1,82 @@ package lecho.lib.hellocharts.animation; -import lecho.lib.hellocharts.view.PieChartView; import android.os.Handler; import android.os.SystemClock; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; +import lecho.lib.hellocharts.view.PieChartView; + public class PieChartRotationAnimatorV8 implements PieChartRotationAnimator { - long start; - boolean isAnimationStarted = false; - final PieChartView chart; - private float startRotation = 0; - private float targetRotation = 0; - final long duration; - final Handler handler; - final Interpolator interpolator = new AccelerateDecelerateInterpolator(); - private ChartAnimationListener animationListener = new DummyChartAnimationListener(); - private final Runnable runnable = new Runnable() { + long start; + boolean isAnimationStarted = false; + final PieChartView chart; + private float startRotation = 0; + private float targetRotation = 0; + final long duration; + final Handler handler; + final Interpolator interpolator = new AccelerateDecelerateInterpolator(); + private ChartAnimationListener animationListener = new DummyChartAnimationListener(); + private final Runnable runnable = new Runnable() { - @Override - public void run() { - long elapsed = SystemClock.uptimeMillis() - start; - if (elapsed > duration) { - isAnimationStarted = false; - handler.removeCallbacks(runnable); - chart.setChartRotation((int) targetRotation, false); - animationListener.onAnimationFinished(); - return; - } - float scale = Math.min(interpolator.getInterpolation((float) elapsed / duration), 1); - float rotation = startRotation + (targetRotation - startRotation) * scale; - rotation = (rotation % 360 + 360) % 360; - chart.setChartRotation((int) rotation, false); - handler.postDelayed(this, 16); - } - }; + @Override + public void run() { + long elapsed = SystemClock.uptimeMillis() - start; + if (elapsed > duration) { + isAnimationStarted = false; + handler.removeCallbacks(runnable); + chart.setChartRotation((int) targetRotation, false); + animationListener.onAnimationFinished(); + return; + } + float scale = Math.min(interpolator.getInterpolation((float) elapsed / duration), 1); + float rotation = startRotation + (targetRotation - startRotation) * scale; + rotation = (rotation % 360 + 360) % 360; + chart.setChartRotation((int) rotation, false); + handler.postDelayed(this, 16); + } + }; - public PieChartRotationAnimatorV8(PieChartView chart) { - this(chart, FAST_ANIMATION_DURATION); - } + public PieChartRotationAnimatorV8(PieChartView chart) { + this(chart, FAST_ANIMATION_DURATION); + } - public PieChartRotationAnimatorV8(PieChartView chart, long duration) { - this.chart = chart; - this.duration = duration; - this.handler = new Handler(); - } + public PieChartRotationAnimatorV8(PieChartView chart, long duration) { + this.chart = chart; + this.duration = duration; + this.handler = new Handler(); + } - @Override - public void startAnimation(float startRotation, float targetRotation) { - this.startRotation = (startRotation % 360 + 360) % 360; - this.targetRotation = (targetRotation % 360 + 360) % 360; - isAnimationStarted = true; - animationListener.onAnimationStarted(); - start = SystemClock.uptimeMillis(); - handler.post(runnable); - } + @Override + public void startAnimation(float startRotation, float targetRotation) { + this.startRotation = (startRotation % 360 + 360) % 360; + this.targetRotation = (targetRotation % 360 + 360) % 360; + isAnimationStarted = true; + animationListener.onAnimationStarted(); + start = SystemClock.uptimeMillis(); + handler.post(runnable); + } - @Override - public void cancelAnimation() { - isAnimationStarted = false; - handler.removeCallbacks(runnable); - chart.setChartRotation((int) targetRotation, false); - animationListener.onAnimationFinished(); - } + @Override + public void cancelAnimation() { + isAnimationStarted = false; + handler.removeCallbacks(runnable); + chart.setChartRotation((int) targetRotation, false); + animationListener.onAnimationFinished(); + } - @Override - public boolean isAnimationStarted() { - return isAnimationStarted; - } + @Override + public boolean isAnimationStarted() { + return isAnimationStarted; + } - @Override - public void setChartAnimationListener(ChartAnimationListener animationListener) { - if (null == animationListener) { - this.animationListener = new DummyChartAnimationListener(); - } else { - this.animationListener = animationListener; - } - } + @Override + public void setChartAnimationListener(ChartAnimationListener animationListener) { + if (null == animationListener) { + this.animationListener = new DummyChartAnimationListener(); + } else { + this.animationListener = animationListener; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/computator/ChartComputator.java b/hellocharts-library/src/lecho/lib/hellocharts/computator/ChartComputator.java index 0eaea61d..821770ff 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/computator/ChartComputator.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/computator/ChartComputator.java @@ -7,344 +7,343 @@ import lecho.lib.hellocharts.listener.DummyVieportChangeListener; import lecho.lib.hellocharts.listener.ViewportChangeListener; import lecho.lib.hellocharts.model.Viewport; -import lecho.lib.hellocharts.view.Chart; /** * Computes raw points coordinates(in pixels), holds content area dimensions and chart viewport. */ public class ChartComputator { - /** - * Maximum chart zoom. - */ - protected static final float DEFAULT_MAXIMUM_ZOOM = 20f; - protected float maxZoom = DEFAULT_MAXIMUM_ZOOM; - protected int chartWidth; - protected int chartHeight; - //contentRectMinusAllMargins <= contentRectMinusAxesMargins <= maxContentRect - protected Rect contentRectMinusAllMargins = new Rect(); - protected Rect contentRectMinusAxesMargins = new Rect(); - protected Rect maxContentRect = new Rect(); - /** - * This rectangle represents the currently visible chart values ranges. The currently visible chart X values are - * from this rectangle's left to its right. The currently visible chart Y values are from this rectangle's top to - * its bottom. - */ - protected Viewport currentViewport = new Viewport(); - protected Viewport maxViewport = new Viewport(); - protected float minViewportWidth; - protected float minViewportHeight; - /** - * Warning! Viewport listener is disabled for all charts beside preview charts to avoid additional method calls - * during animations. - */ - protected ViewportChangeListener viewportChangeListener = new DummyVieportChangeListener(); - - /** - * Calculates available width and height. Should be called when chart dimensions change. ContentRect is relative to - * chart view not the device's screen. - */ - public void setContentRect(int width, int height, int paddingLeft, int paddingTop, int paddingRight, - int paddingBottom) { - chartWidth = width; - chartHeight = height; - maxContentRect.set(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom); - contentRectMinusAxesMargins.set(maxContentRect); - contentRectMinusAllMargins.set(maxContentRect); - } - - public void resetContentRect(){ - contentRectMinusAxesMargins.set(maxContentRect); - contentRectMinusAllMargins.set(maxContentRect); - } - - public void insetContentRect(int deltaLeft, int deltaTop, int deltaRight, int deltaBottom) { - contentRectMinusAxesMargins.left = contentRectMinusAxesMargins.left + deltaLeft; - contentRectMinusAxesMargins.top = contentRectMinusAxesMargins.top + deltaTop; - contentRectMinusAxesMargins.right = contentRectMinusAxesMargins.right - deltaRight; - contentRectMinusAxesMargins.bottom = contentRectMinusAxesMargins.bottom - deltaBottom; - - insetContentRectByInternalMargins(deltaLeft, deltaTop, deltaRight, deltaBottom); - } - - public void insetContentRectByInternalMargins(int deltaLeft, int deltaTop, int deltaRight, int deltaBottom) { - contentRectMinusAllMargins.left = contentRectMinusAllMargins.left + deltaLeft; - contentRectMinusAllMargins.top = contentRectMinusAllMargins.top + deltaTop; - contentRectMinusAllMargins.right = contentRectMinusAllMargins.right - deltaRight; - contentRectMinusAllMargins.bottom = contentRectMinusAllMargins.bottom - deltaBottom; - } - - /** - * Checks if new viewport doesn't exceed max available viewport. - */ - public void constrainViewport(float left, float top, float right, float bottom) { - - if (right - left < minViewportWidth) { - // Minimum width - constrain horizontal zoom! - right = left + minViewportWidth; - if (left < maxViewport.left) { - left = maxViewport.left; - right = left + minViewportWidth; - } else if (right > maxViewport.right) { - right = maxViewport.right; - left = right - minViewportWidth; - } - } - - if (top - bottom < minViewportHeight) { - // Minimum height - constrain vertical zoom! - bottom = top - minViewportHeight; - if (top > maxViewport.top) { - top = maxViewport.top; - bottom = top - minViewportHeight; - } else if (bottom < maxViewport.bottom) { - bottom = maxViewport.bottom; - top = bottom + minViewportHeight; - } - } - - currentViewport.left = Math.max(maxViewport.left, left); - currentViewport.top = Math.min(maxViewport.top, top); - currentViewport.right = Math.min(maxViewport.right, right); - currentViewport.bottom = Math.max(maxViewport.bottom, bottom); - - viewportChangeListener.onViewportChanged(currentViewport); - } - - /** - * Sets the current viewport (defined by {@link #currentViewport}) to the given X and Y positions. - */ - public void setViewportTopLeft(float left, float top) { - /** - * Constrains within the scroll range. The scroll range is simply the viewport extremes (AXIS_X_MAX, - * etc.) minus - * the viewport size. For example, if the extrema were 0 and 10, and the viewport size was 2, the scroll range - * would be 0 to 8. - */ - - final float curWidth = currentViewport.width(); - final float curHeight = currentViewport.height(); - - left = Math.max(maxViewport.left, Math.min(left, maxViewport.right - curWidth)); - top = Math.max(maxViewport.bottom + curHeight, Math.min(top, maxViewport.top)); - constrainViewport(left, top, left + curWidth, top - curHeight); - } - - /** - * Translates chart value into raw pixel value. Returned value is absolute pixel X coordinate. If this method - * return - * 0 that means left most pixel of the screen. - */ - public float computeRawX(float valueX) { - // TODO: (contentRectMinusAllMargins.width() / currentViewport.width()) can be recalculated only when viewport - // change. - final float pixelOffset = (valueX - currentViewport.left) * (contentRectMinusAllMargins.width() / - currentViewport.width()); - return contentRectMinusAllMargins.left + pixelOffset; - } - - /** - * Translates chart value into raw pixel value. Returned value is absolute pixel Y coordinate. If this method - * return - * 0 that means top most pixel of the screen. - */ - public float computeRawY(float valueY) { - final float pixelOffset = (valueY - currentViewport.bottom) * (contentRectMinusAllMargins.height() / - currentViewport.height()); - return contentRectMinusAllMargins.bottom - pixelOffset; - } - - /** - * Translates viewport distance int pixel distance for X coordinates. - */ - public float computeRawDistanceX(float distance) { - return distance * (contentRectMinusAllMargins.width() / currentViewport.width()); - } - - /** - * Translates viewport distance int pixel distance for X coordinates. - */ - public float computeRawDistanceY(float distance) { - return distance * (contentRectMinusAllMargins.height() / currentViewport.height()); - } - - /** - * Finds the chart point (i.e. within the chart's domain and range) represented by the given pixel coordinates, if - * that pixel is within the chart region described by {@link #contentRectMinusAllMargins}. If the point is found, - * the "dest" - * argument is set to the point and this function returns true. Otherwise, this function returns false and - * "dest" is - * unchanged. - */ - public boolean rawPixelsToDataPoint(float x, float y, PointF dest) { - if (!contentRectMinusAllMargins.contains((int) x, (int) y)) { - return false; - } - dest.set(currentViewport.left + (x - contentRectMinusAllMargins.left) * currentViewport.width() / - contentRectMinusAllMargins.width(), - currentViewport.bottom + (y - contentRectMinusAllMargins.bottom) * currentViewport.height() / - -contentRectMinusAllMargins.height()); - return true; - } - - /** - * Computes the current scrollable surface size, in pixels. For example, if the entire chart area is visible, this - * is simply the current size of {@link #contentRectMinusAllMargins}. If the chart is zoomed in 200% in both - * directions, the - * returned size will be twice as large horizontally and vertically. - */ - public void computeScrollSurfaceSize(Point out) { - out.set((int) (maxViewport.width() * contentRectMinusAllMargins.width() / currentViewport.width()), - (int) (maxViewport.height() * contentRectMinusAllMargins.height() / currentViewport.height())); - } - - /** - * Check if given coordinates lies inside contentRectMinusAllMargins. - */ - public boolean isWithinContentRect(float x, float y, float precision) { - if (x >= contentRectMinusAllMargins.left - precision && x <= contentRectMinusAllMargins.right + precision) { - if (y <= contentRectMinusAllMargins.bottom + precision && y >= contentRectMinusAllMargins.top - - precision) { - return true; - } - } - return false; - } - - /** - * Returns content rectangle in pixels. - * - * @see #setContentRect(int, int, int, int, int, int) - */ - public Rect getContentRectMinusAllMargins() { - return contentRectMinusAllMargins; - } - - /** - * Returns content rectangle with chart internal margins, for example for LineChart contentRectMinusAxesMargins is - * bigger - * than contentRectMinusAllMargins by point radius, thanks to that points are not cut on edges. - * - * @see #setContentRect(int, int, int, int, int, int) - */ - public Rect getContentRectMinusAxesMargins() { - return contentRectMinusAxesMargins; - } - - /** - * Returns current chart viewport, returned object is mutable but should not be modified. - * - * @return - */ - public Viewport getCurrentViewport() { - return currentViewport; - } - - /** - * Set current viewport to the same values as viewport passed in parameter. This method use deep copy so parameter - * can be safely modified later. Current viewport must be equal or smaller than maximum viewport. - * - * @param viewport - */ - public void setCurrentViewport(Viewport viewport) { - constrainViewport(viewport.left, viewport.top, viewport.right, viewport.bottom); - } - - /** - * Set new values for curent viewport, that will change what part of chart is visible. Current viewport must be - * equal or smaller than maximum viewport. - */ - public void setCurrentViewport(float left, float top, float right, float bottom) { - constrainViewport(left, top, right, bottom); - } - - /** - * Returns maximum viewport - values ranges extremes. - */ - public Viewport getMaximumViewport() { - return maxViewport; - } - - /** - * Set maximum viewport to the same values as viewport passed in parameter. This method use deep copy so parameter - * can be safely modified later. - * - * @param maxViewport - */ - public void setMaxViewport(Viewport maxViewport) { - setMaxViewport(maxViewport.left, maxViewport.top, maxViewport.right, maxViewport.bottom); - } - - /** - * Set new values for maximum viewport, that will change what part of chart is visible. - */ - public void setMaxViewport(float left, float top, float right, float bottom) { - this.maxViewport.set(left, top, right, bottom); - computeMinimumWidthAndHeight(); - } - - /** - * Returns viewport for visible part of chart, for most charts it is equal to current viewport. - * - * @return - */ - public Viewport getVisibleViewport() { - return currentViewport; - } - - public void setVisibleViewport(Viewport visibleViewport) { - setCurrentViewport(visibleViewport); - } - - public float getMinimumViewportWidth() { - return minViewportWidth; - } - - public float getMinimumViewportHeight() { - return minViewportHeight; - } - - public void setViewportChangeListener(ViewportChangeListener viewportChangeListener) { - if (null == viewportChangeListener) { - this.viewportChangeListener = new DummyVieportChangeListener(); - } else { - this.viewportChangeListener = viewportChangeListener; - } - } - - public int getChartWidth() { - return chartWidth; - } - - public int getChartHeight() { - return chartHeight; - } - - public float getMaxZoom() { - return maxZoom; - } - - /** - * Set maximum zoom level, default is 20. - * - * @param maxZoom - */ - public void setMaxZoom(float maxZoom) { - if (maxZoom < 1) { - maxZoom = 1; - } - - this.maxZoom = maxZoom; - - computeMinimumWidthAndHeight(); - - setCurrentViewport(currentViewport); - - } - - private void computeMinimumWidthAndHeight() { - minViewportWidth = this.maxViewport.width() / maxZoom; - minViewportHeight = this.maxViewport.height() / maxZoom; - } + /** + * Maximum chart zoom. + */ + protected static final float DEFAULT_MAXIMUM_ZOOM = 20f; + protected float maxZoom = DEFAULT_MAXIMUM_ZOOM; + protected int chartWidth; + protected int chartHeight; + //contentRectMinusAllMargins <= contentRectMinusAxesMargins <= maxContentRect + protected Rect contentRectMinusAllMargins = new Rect(); + protected Rect contentRectMinusAxesMargins = new Rect(); + protected Rect maxContentRect = new Rect(); + /** + * This rectangle represents the currently visible chart values ranges. The currently visible chart X values are + * from this rectangle's left to its right. The currently visible chart Y values are from this rectangle's top to + * its bottom. + */ + protected Viewport currentViewport = new Viewport(); + protected Viewport maxViewport = new Viewport(); + protected float minViewportWidth; + protected float minViewportHeight; + /** + * Warning! Viewport listener is disabled for all charts beside preview charts to avoid additional method calls + * during animations. + */ + protected ViewportChangeListener viewportChangeListener = new DummyVieportChangeListener(); + + /** + * Calculates available width and height. Should be called when chart dimensions change. ContentRect is relative to + * chart view not the device's screen. + */ + public void setContentRect(int width, int height, int paddingLeft, int paddingTop, int paddingRight, + int paddingBottom) { + chartWidth = width; + chartHeight = height; + maxContentRect.set(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom); + contentRectMinusAxesMargins.set(maxContentRect); + contentRectMinusAllMargins.set(maxContentRect); + } + + public void resetContentRect() { + contentRectMinusAxesMargins.set(maxContentRect); + contentRectMinusAllMargins.set(maxContentRect); + } + + public void insetContentRect(int deltaLeft, int deltaTop, int deltaRight, int deltaBottom) { + contentRectMinusAxesMargins.left = contentRectMinusAxesMargins.left + deltaLeft; + contentRectMinusAxesMargins.top = contentRectMinusAxesMargins.top + deltaTop; + contentRectMinusAxesMargins.right = contentRectMinusAxesMargins.right - deltaRight; + contentRectMinusAxesMargins.bottom = contentRectMinusAxesMargins.bottom - deltaBottom; + + insetContentRectByInternalMargins(deltaLeft, deltaTop, deltaRight, deltaBottom); + } + + public void insetContentRectByInternalMargins(int deltaLeft, int deltaTop, int deltaRight, int deltaBottom) { + contentRectMinusAllMargins.left = contentRectMinusAllMargins.left + deltaLeft; + contentRectMinusAllMargins.top = contentRectMinusAllMargins.top + deltaTop; + contentRectMinusAllMargins.right = contentRectMinusAllMargins.right - deltaRight; + contentRectMinusAllMargins.bottom = contentRectMinusAllMargins.bottom - deltaBottom; + } + + /** + * Checks if new viewport doesn't exceed max available viewport. + */ + public void constrainViewport(float left, float top, float right, float bottom) { + + if (right - left < minViewportWidth) { + // Minimum width - constrain horizontal zoom! + right = left + minViewportWidth; + if (left < maxViewport.left) { + left = maxViewport.left; + right = left + minViewportWidth; + } else if (right > maxViewport.right) { + right = maxViewport.right; + left = right - minViewportWidth; + } + } + + if (top - bottom < minViewportHeight) { + // Minimum height - constrain vertical zoom! + bottom = top - minViewportHeight; + if (top > maxViewport.top) { + top = maxViewport.top; + bottom = top - minViewportHeight; + } else if (bottom < maxViewport.bottom) { + bottom = maxViewport.bottom; + top = bottom + minViewportHeight; + } + } + + currentViewport.left = Math.max(maxViewport.left, left); + currentViewport.top = Math.min(maxViewport.top, top); + currentViewport.right = Math.min(maxViewport.right, right); + currentViewport.bottom = Math.max(maxViewport.bottom, bottom); + + viewportChangeListener.onViewportChanged(currentViewport); + } + + /** + * Sets the current viewport (defined by {@link #currentViewport}) to the given X and Y positions. + */ + public void setViewportTopLeft(float left, float top) { + /** + * Constrains within the scroll range. The scroll range is simply the viewport extremes (AXIS_X_MAX, + * etc.) minus + * the viewport size. For example, if the extrema were 0 and 10, and the viewport size was 2, the scroll range + * would be 0 to 8. + */ + + final float curWidth = currentViewport.width(); + final float curHeight = currentViewport.height(); + + left = Math.max(maxViewport.left, Math.min(left, maxViewport.right - curWidth)); + top = Math.max(maxViewport.bottom + curHeight, Math.min(top, maxViewport.top)); + constrainViewport(left, top, left + curWidth, top - curHeight); + } + + /** + * Translates chart value into raw pixel value. Returned value is absolute pixel X coordinate. If this method + * return + * 0 that means left most pixel of the screen. + */ + public float computeRawX(float valueX) { + // TODO: (contentRectMinusAllMargins.width() / currentViewport.width()) can be recalculated only when viewport + // change. + final float pixelOffset = (valueX - currentViewport.left) * (contentRectMinusAllMargins.width() / + currentViewport.width()); + return contentRectMinusAllMargins.left + pixelOffset; + } + + /** + * Translates chart value into raw pixel value. Returned value is absolute pixel Y coordinate. If this method + * return + * 0 that means top most pixel of the screen. + */ + public float computeRawY(float valueY) { + final float pixelOffset = (valueY - currentViewport.bottom) * (contentRectMinusAllMargins.height() / + currentViewport.height()); + return contentRectMinusAllMargins.bottom - pixelOffset; + } + + /** + * Translates viewport distance int pixel distance for X coordinates. + */ + public float computeRawDistanceX(float distance) { + return distance * (contentRectMinusAllMargins.width() / currentViewport.width()); + } + + /** + * Translates viewport distance int pixel distance for X coordinates. + */ + public float computeRawDistanceY(float distance) { + return distance * (contentRectMinusAllMargins.height() / currentViewport.height()); + } + + /** + * Finds the chart point (i.e. within the chart's domain and range) represented by the given pixel coordinates, if + * that pixel is within the chart region described by {@link #contentRectMinusAllMargins}. If the point is found, + * the "dest" + * argument is set to the point and this function returns true. Otherwise, this function returns false and + * "dest" is + * unchanged. + */ + public boolean rawPixelsToDataPoint(float x, float y, PointF dest) { + if (!contentRectMinusAllMargins.contains((int) x, (int) y)) { + return false; + } + dest.set(currentViewport.left + (x - contentRectMinusAllMargins.left) * currentViewport.width() / + contentRectMinusAllMargins.width(), + currentViewport.bottom + (y - contentRectMinusAllMargins.bottom) * currentViewport.height() / + -contentRectMinusAllMargins.height()); + return true; + } + + /** + * Computes the current scrollable surface size, in pixels. For example, if the entire chart area is visible, this + * is simply the current size of {@link #contentRectMinusAllMargins}. If the chart is zoomed in 200% in both + * directions, the + * returned size will be twice as large horizontally and vertically. + */ + public void computeScrollSurfaceSize(Point out) { + out.set((int) (maxViewport.width() * contentRectMinusAllMargins.width() / currentViewport.width()), + (int) (maxViewport.height() * contentRectMinusAllMargins.height() / currentViewport.height())); + } + + /** + * Check if given coordinates lies inside contentRectMinusAllMargins. + */ + public boolean isWithinContentRect(float x, float y, float precision) { + if (x >= contentRectMinusAllMargins.left - precision && x <= contentRectMinusAllMargins.right + precision) { + if (y <= contentRectMinusAllMargins.bottom + precision && y >= contentRectMinusAllMargins.top - + precision) { + return true; + } + } + return false; + } + + /** + * Returns content rectangle in pixels. + * + * @see #setContentRect(int, int, int, int, int, int) + */ + public Rect getContentRectMinusAllMargins() { + return contentRectMinusAllMargins; + } + + /** + * Returns content rectangle with chart internal margins, for example for LineChart contentRectMinusAxesMargins is + * bigger + * than contentRectMinusAllMargins by point radius, thanks to that points are not cut on edges. + * + * @see #setContentRect(int, int, int, int, int, int) + */ + public Rect getContentRectMinusAxesMargins() { + return contentRectMinusAxesMargins; + } + + /** + * Returns current chart viewport, returned object is mutable but should not be modified. + * + * @return + */ + public Viewport getCurrentViewport() { + return currentViewport; + } + + /** + * Set current viewport to the same values as viewport passed in parameter. This method use deep copy so parameter + * can be safely modified later. Current viewport must be equal or smaller than maximum viewport. + * + * @param viewport + */ + public void setCurrentViewport(Viewport viewport) { + constrainViewport(viewport.left, viewport.top, viewport.right, viewport.bottom); + } + + /** + * Set new values for curent viewport, that will change what part of chart is visible. Current viewport must be + * equal or smaller than maximum viewport. + */ + public void setCurrentViewport(float left, float top, float right, float bottom) { + constrainViewport(left, top, right, bottom); + } + + /** + * Returns maximum viewport - values ranges extremes. + */ + public Viewport getMaximumViewport() { + return maxViewport; + } + + /** + * Set maximum viewport to the same values as viewport passed in parameter. This method use deep copy so parameter + * can be safely modified later. + * + * @param maxViewport + */ + public void setMaxViewport(Viewport maxViewport) { + setMaxViewport(maxViewport.left, maxViewport.top, maxViewport.right, maxViewport.bottom); + } + + /** + * Set new values for maximum viewport, that will change what part of chart is visible. + */ + public void setMaxViewport(float left, float top, float right, float bottom) { + this.maxViewport.set(left, top, right, bottom); + computeMinimumWidthAndHeight(); + } + + /** + * Returns viewport for visible part of chart, for most charts it is equal to current viewport. + * + * @return + */ + public Viewport getVisibleViewport() { + return currentViewport; + } + + public void setVisibleViewport(Viewport visibleViewport) { + setCurrentViewport(visibleViewport); + } + + public float getMinimumViewportWidth() { + return minViewportWidth; + } + + public float getMinimumViewportHeight() { + return minViewportHeight; + } + + public void setViewportChangeListener(ViewportChangeListener viewportChangeListener) { + if (null == viewportChangeListener) { + this.viewportChangeListener = new DummyVieportChangeListener(); + } else { + this.viewportChangeListener = viewportChangeListener; + } + } + + public int getChartWidth() { + return chartWidth; + } + + public int getChartHeight() { + return chartHeight; + } + + public float getMaxZoom() { + return maxZoom; + } + + /** + * Set maximum zoom level, default is 20. + * + * @param maxZoom + */ + public void setMaxZoom(float maxZoom) { + if (maxZoom < 1) { + maxZoom = 1; + } + + this.maxZoom = maxZoom; + + computeMinimumWidthAndHeight(); + + setCurrentViewport(currentViewport); + + } + + private void computeMinimumWidthAndHeight() { + minViewportWidth = this.maxViewport.width() / maxZoom; + minViewportHeight = this.maxViewport.height() / maxZoom; + } } \ No newline at end of file diff --git a/hellocharts-library/src/lecho/lib/hellocharts/computator/PreviewChartComputator.java b/hellocharts-library/src/lecho/lib/hellocharts/computator/PreviewChartComputator.java index 605aceeb..5dd2d6c0 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/computator/PreviewChartComputator.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/computator/PreviewChartComputator.java @@ -1,6 +1,5 @@ package lecho.lib.hellocharts.computator; -import lecho.lib.hellocharts.computator.ChartComputator; import lecho.lib.hellocharts.model.Viewport; /** @@ -9,27 +8,27 @@ */ public class PreviewChartComputator extends ChartComputator { - public float computeRawX(float valueX) { - final float pixelOffset = (valueX - maxViewport.left) * (contentRectMinusAllMargins.width() / maxViewport.width()); - return contentRectMinusAllMargins.left + pixelOffset; - } + public float computeRawX(float valueX) { + final float pixelOffset = (valueX - maxViewport.left) * (contentRectMinusAllMargins.width() / maxViewport.width()); + return contentRectMinusAllMargins.left + pixelOffset; + } - public float computeRawY(float valueY) { - final float pixelOffset = (valueY - maxViewport.bottom) * (contentRectMinusAllMargins.height() / maxViewport.height()); - return contentRectMinusAllMargins.bottom - pixelOffset; - } + public float computeRawY(float valueY) { + final float pixelOffset = (valueY - maxViewport.bottom) * (contentRectMinusAllMargins.height() / maxViewport.height()); + return contentRectMinusAllMargins.bottom - pixelOffset; + } - public Viewport getVisibleViewport() { - return maxViewport; - } + public Viewport getVisibleViewport() { + return maxViewport; + } - public void setVisibleViewport(Viewport visibleViewport) { - setMaxViewport(visibleViewport); - } + public void setVisibleViewport(Viewport visibleViewport) { + setMaxViewport(visibleViewport); + } - public void constrainViewport(float left, float top, float right, float bottom) { - super.constrainViewport(left, top, right, bottom); - viewportChangeListener.onViewportChanged(currentViewport); - } + public void constrainViewport(float left, float top, float right, float bottom) { + super.constrainViewport(left, top, right, bottom); + viewportChangeListener.onViewportChanged(currentViewport); + } } \ No newline at end of file diff --git a/hellocharts-library/src/lecho/lib/hellocharts/formatter/AxisValueFormatter.java b/hellocharts-library/src/lecho/lib/hellocharts/formatter/AxisValueFormatter.java index 6c50387b..3fb28847 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/formatter/AxisValueFormatter.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/formatter/AxisValueFormatter.java @@ -5,18 +5,18 @@ public interface AxisValueFormatter { - /** - * Formats AxisValue for manual(custom) axis. Result is stored in (output) formattedValue array. Method - * returns number of chars of formatted value. The formatted value starts at index [formattedValue.length - - * charsNumber] and ends at index [formattedValue.length-1]. - */ - public int formatValueForManualAxis(char[] formattedValue, AxisValue axisValue); + /** + * Formats AxisValue for manual(custom) axis. Result is stored in (output) formattedValue array. Method + * returns number of chars of formatted value. The formatted value starts at index [formattedValue.length - + * charsNumber] and ends at index [formattedValue.length-1]. + */ + public int formatValueForManualAxis(char[] formattedValue, AxisValue axisValue); - /** - * Used only for auto-generated axes. If you are not going to use your implementation for aut-generated axes you can - * skip implementation of this method and just return 0. SFormats values with given number of digits after - * decimal separator. Result is stored in given array. Method returns number of chars for formatted value. The - * formatted value starts at index [formattedValue.length - charsNumber] and ends at index [formattedValue.length-1]. - */ - public int formatValueForAutoGeneratedAxis(char[] formattedValue, float value, int autoDecimalDigits); + /** + * Used only for auto-generated axes. If you are not going to use your implementation for aut-generated axes you can + * skip implementation of this method and just return 0. SFormats values with given number of digits after + * decimal separator. Result is stored in given array. Method returns number of chars for formatted value. The + * formatted value starts at index [formattedValue.length - charsNumber] and ends at index [formattedValue.length-1]. + */ + public int formatValueForAutoGeneratedAxis(char[] formattedValue, float value, int autoDecimalDigits); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/formatter/BubbleChartValueFormatter.java b/hellocharts-library/src/lecho/lib/hellocharts/formatter/BubbleChartValueFormatter.java index a9177f57..23404983 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/formatter/BubbleChartValueFormatter.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/formatter/BubbleChartValueFormatter.java @@ -4,5 +4,5 @@ public interface BubbleChartValueFormatter { - public int formatChartValue(char[] formattedValue, BubbleValue value); + public int formatChartValue(char[] formattedValue, BubbleValue value); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/formatter/ColumnChartValueFormatter.java b/hellocharts-library/src/lecho/lib/hellocharts/formatter/ColumnChartValueFormatter.java index e38abff1..c95d8c63 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/formatter/ColumnChartValueFormatter.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/formatter/ColumnChartValueFormatter.java @@ -4,6 +4,6 @@ public interface ColumnChartValueFormatter { - public int formatChartValue(char[] formattedValue, SubcolumnValue value); + public int formatChartValue(char[] formattedValue, SubcolumnValue value); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/formatter/LineChartValueFormatter.java b/hellocharts-library/src/lecho/lib/hellocharts/formatter/LineChartValueFormatter.java index 0ef7befd..42864a1f 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/formatter/LineChartValueFormatter.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/formatter/LineChartValueFormatter.java @@ -5,5 +5,5 @@ public interface LineChartValueFormatter { - public int formatChartValue(char[] formattedValue, PointValue value); + public int formatChartValue(char[] formattedValue, PointValue value); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/formatter/PieChartValueFormatter.java b/hellocharts-library/src/lecho/lib/hellocharts/formatter/PieChartValueFormatter.java index 165ca406..28e0009c 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/formatter/PieChartValueFormatter.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/formatter/PieChartValueFormatter.java @@ -4,5 +4,5 @@ public interface PieChartValueFormatter { - public int formatChartValue(char[] formattedValue, SliceValue value); + public int formatChartValue(char[] formattedValue, SliceValue value); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleAxisValueFormatter.java b/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleAxisValueFormatter.java index ee7f4715..f706ffff 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleAxisValueFormatter.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleAxisValueFormatter.java @@ -4,65 +4,65 @@ public class SimpleAxisValueFormatter implements AxisValueFormatter { - private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper(); - - public SimpleAxisValueFormatter() { - valueFormatterHelper.determineDecimalSeparator(); - } - - public SimpleAxisValueFormatter(int decimalDigitsNumber) { - this(); - valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); - } - - @Override - public int formatValueForManualAxis(char[] formattedValue, AxisValue axisValue) { - final int charsNumber = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, - axisValue.getValue(), axisValue.getLabelAsChars()); - return charsNumber; - } - - @Override - public int formatValueForAutoGeneratedAxis(char[] formattedValue, float value, int autoDecimalDigits) { - final int charsNumber = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, - value, autoDecimalDigits); - return charsNumber; - } - - public int getDecimalDigitsNumber() { - return valueFormatterHelper.getDecimalDigitsNumber(); - } - - public SimpleAxisValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) { - valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); - return this; - } - - public char[] getAppendedText() { - return valueFormatterHelper.getAppendedText(); - } - - public SimpleAxisValueFormatter setAppendedText(char[] appendedText) { - valueFormatterHelper.setAppendedText(appendedText); - return this; - } - - public char[] getPrependedText() { - return valueFormatterHelper.getPrependedText(); - } - - public SimpleAxisValueFormatter setPrependedText(char[] prependedText) { - valueFormatterHelper.setPrependedText(prependedText); - return this; - } - - public char getDecimalSeparator() { - return valueFormatterHelper.getDecimalSeparator(); - } - - public SimpleAxisValueFormatter setDecimalSeparator(char decimalSeparator) { - valueFormatterHelper.setDecimalSeparator(decimalSeparator); - return this; - } + private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper(); + + public SimpleAxisValueFormatter() { + valueFormatterHelper.determineDecimalSeparator(); + } + + public SimpleAxisValueFormatter(int decimalDigitsNumber) { + this(); + valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); + } + + @Override + public int formatValueForManualAxis(char[] formattedValue, AxisValue axisValue) { + final int charsNumber = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, + axisValue.getValue(), axisValue.getLabelAsChars()); + return charsNumber; + } + + @Override + public int formatValueForAutoGeneratedAxis(char[] formattedValue, float value, int autoDecimalDigits) { + final int charsNumber = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, + value, autoDecimalDigits); + return charsNumber; + } + + public int getDecimalDigitsNumber() { + return valueFormatterHelper.getDecimalDigitsNumber(); + } + + public SimpleAxisValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) { + valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); + return this; + } + + public char[] getAppendedText() { + return valueFormatterHelper.getAppendedText(); + } + + public SimpleAxisValueFormatter setAppendedText(char[] appendedText) { + valueFormatterHelper.setAppendedText(appendedText); + return this; + } + + public char[] getPrependedText() { + return valueFormatterHelper.getPrependedText(); + } + + public SimpleAxisValueFormatter setPrependedText(char[] prependedText) { + valueFormatterHelper.setPrependedText(prependedText); + return this; + } + + public char getDecimalSeparator() { + return valueFormatterHelper.getDecimalSeparator(); + } + + public SimpleAxisValueFormatter setDecimalSeparator(char decimalSeparator) { + valueFormatterHelper.setDecimalSeparator(decimalSeparator); + return this; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleBubbleChartValueFormatter.java b/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleBubbleChartValueFormatter.java index 5e40ff4f..2dfa317e 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleBubbleChartValueFormatter.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleBubbleChartValueFormatter.java @@ -5,55 +5,55 @@ public class SimpleBubbleChartValueFormatter implements BubbleChartValueFormatter { - private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper(); - - public SimpleBubbleChartValueFormatter() { - valueFormatterHelper.determineDecimalSeparator(); - } - - public SimpleBubbleChartValueFormatter(int decimalDigitsNumber) { - this(); - valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); - } - - @Override - public int formatChartValue(char[] formattedValue, BubbleValue value) { - return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getZ(), value.getLabelAsChars()); - } - - public int getDecimalDigitsNumber() { - return valueFormatterHelper.getDecimalDigitsNumber(); - } - - public SimpleBubbleChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) { - valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); - return this; - } - - public char[] getAppendedText() { - return valueFormatterHelper.getAppendedText(); - } - - public SimpleBubbleChartValueFormatter setAppendedText(char[] appendedText) { - valueFormatterHelper.setAppendedText(appendedText); - return this; - } - - public char[] getPrependedText() { - return valueFormatterHelper.getPrependedText(); - } - - public SimpleBubbleChartValueFormatter setPrependedText(char[] prependedText) { - valueFormatterHelper.setPrependedText(prependedText); - return this; - } - - public char getDecimalSeparator() { - return valueFormatterHelper.getDecimalSeparator(); - } - - public SimpleBubbleChartValueFormatter setDecimalSeparator(char decimalSeparator) { - valueFormatterHelper.setDecimalSeparator(decimalSeparator); - return this; - } + private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper(); + + public SimpleBubbleChartValueFormatter() { + valueFormatterHelper.determineDecimalSeparator(); + } + + public SimpleBubbleChartValueFormatter(int decimalDigitsNumber) { + this(); + valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); + } + + @Override + public int formatChartValue(char[] formattedValue, BubbleValue value) { + return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getZ(), value.getLabelAsChars()); + } + + public int getDecimalDigitsNumber() { + return valueFormatterHelper.getDecimalDigitsNumber(); + } + + public SimpleBubbleChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) { + valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); + return this; + } + + public char[] getAppendedText() { + return valueFormatterHelper.getAppendedText(); + } + + public SimpleBubbleChartValueFormatter setAppendedText(char[] appendedText) { + valueFormatterHelper.setAppendedText(appendedText); + return this; + } + + public char[] getPrependedText() { + return valueFormatterHelper.getPrependedText(); + } + + public SimpleBubbleChartValueFormatter setPrependedText(char[] prependedText) { + valueFormatterHelper.setPrependedText(prependedText); + return this; + } + + public char getDecimalSeparator() { + return valueFormatterHelper.getDecimalSeparator(); + } + + public SimpleBubbleChartValueFormatter setDecimalSeparator(char decimalSeparator) { + valueFormatterHelper.setDecimalSeparator(decimalSeparator); + return this; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleColumnChartValueFormatter.java b/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleColumnChartValueFormatter.java index 9e069ba7..fe8c4a29 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleColumnChartValueFormatter.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleColumnChartValueFormatter.java @@ -4,55 +4,55 @@ public class SimpleColumnChartValueFormatter implements ColumnChartValueFormatter { - private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper(); - - public SimpleColumnChartValueFormatter() { - valueFormatterHelper.determineDecimalSeparator(); - } - - public SimpleColumnChartValueFormatter(int decimalDigitsNumber) { - this(); - valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); - } - - @Override - public int formatChartValue(char[] formattedValue, SubcolumnValue value) { - return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getValue(), value.getLabelAsChars()); - } - - public int getDecimalDigitsNumber() { - return valueFormatterHelper.getDecimalDigitsNumber(); - } - - public SimpleColumnChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) { - valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); - return this; - } - - public char[] getAppendedText() { - return valueFormatterHelper.getAppendedText(); - } - - public SimpleColumnChartValueFormatter setAppendedText(char[] appendedText) { - valueFormatterHelper.setAppendedText(appendedText); - return this; - } - - public char[] getPrependedText() { - return valueFormatterHelper.getPrependedText(); - } - - public SimpleColumnChartValueFormatter setPrependedText(char[] prependedText) { - valueFormatterHelper.setPrependedText(prependedText); - return this; - } - - public char getDecimalSeparator() { - return valueFormatterHelper.getDecimalSeparator(); - } - - public SimpleColumnChartValueFormatter setDecimalSeparator(char decimalSeparator) { - valueFormatterHelper.setDecimalSeparator(decimalSeparator); - return this; - } + private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper(); + + public SimpleColumnChartValueFormatter() { + valueFormatterHelper.determineDecimalSeparator(); + } + + public SimpleColumnChartValueFormatter(int decimalDigitsNumber) { + this(); + valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); + } + + @Override + public int formatChartValue(char[] formattedValue, SubcolumnValue value) { + return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getValue(), value.getLabelAsChars()); + } + + public int getDecimalDigitsNumber() { + return valueFormatterHelper.getDecimalDigitsNumber(); + } + + public SimpleColumnChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) { + valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); + return this; + } + + public char[] getAppendedText() { + return valueFormatterHelper.getAppendedText(); + } + + public SimpleColumnChartValueFormatter setAppendedText(char[] appendedText) { + valueFormatterHelper.setAppendedText(appendedText); + return this; + } + + public char[] getPrependedText() { + return valueFormatterHelper.getPrependedText(); + } + + public SimpleColumnChartValueFormatter setPrependedText(char[] prependedText) { + valueFormatterHelper.setPrependedText(prependedText); + return this; + } + + public char getDecimalSeparator() { + return valueFormatterHelper.getDecimalSeparator(); + } + + public SimpleColumnChartValueFormatter setDecimalSeparator(char decimalSeparator) { + valueFormatterHelper.setDecimalSeparator(decimalSeparator); + return this; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleLineChartValueFormatter.java b/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleLineChartValueFormatter.java index 75963d30..5900ac39 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleLineChartValueFormatter.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimpleLineChartValueFormatter.java @@ -4,55 +4,55 @@ public class SimpleLineChartValueFormatter implements LineChartValueFormatter { - private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper(); - - public SimpleLineChartValueFormatter() { - valueFormatterHelper.determineDecimalSeparator(); - } - - public SimpleLineChartValueFormatter(int decimalDigitsNumber) { - this(); - valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); - } - - @Override - public int formatChartValue(char[] formattedValue, PointValue value) { - return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getY(), value.getLabelAsChars()); - } - - public int getDecimalDigitsNumber() { - return valueFormatterHelper.getDecimalDigitsNumber(); - } - - public SimpleLineChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) { - valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); - return this; - } - - public char[] getAppendedText() { - return valueFormatterHelper.getAppendedText(); - } - - public SimpleLineChartValueFormatter setAppendedText(char[] appendedText) { - valueFormatterHelper.setAppendedText(appendedText); - return this; - } - - public char[] getPrependedText() { - return valueFormatterHelper.getPrependedText(); - } - - public SimpleLineChartValueFormatter setPrependedText(char[] prependedText) { - valueFormatterHelper.setPrependedText(prependedText); - return this; - } - - public char getDecimalSeparator() { - return valueFormatterHelper.getDecimalSeparator(); - } - - public SimpleLineChartValueFormatter setDecimalSeparator(char decimalSeparator) { - valueFormatterHelper.setDecimalSeparator(decimalSeparator); - return this; - } + private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper(); + + public SimpleLineChartValueFormatter() { + valueFormatterHelper.determineDecimalSeparator(); + } + + public SimpleLineChartValueFormatter(int decimalDigitsNumber) { + this(); + valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); + } + + @Override + public int formatChartValue(char[] formattedValue, PointValue value) { + return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getY(), value.getLabelAsChars()); + } + + public int getDecimalDigitsNumber() { + return valueFormatterHelper.getDecimalDigitsNumber(); + } + + public SimpleLineChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) { + valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); + return this; + } + + public char[] getAppendedText() { + return valueFormatterHelper.getAppendedText(); + } + + public SimpleLineChartValueFormatter setAppendedText(char[] appendedText) { + valueFormatterHelper.setAppendedText(appendedText); + return this; + } + + public char[] getPrependedText() { + return valueFormatterHelper.getPrependedText(); + } + + public SimpleLineChartValueFormatter setPrependedText(char[] prependedText) { + valueFormatterHelper.setPrependedText(prependedText); + return this; + } + + public char getDecimalSeparator() { + return valueFormatterHelper.getDecimalSeparator(); + } + + public SimpleLineChartValueFormatter setDecimalSeparator(char decimalSeparator) { + valueFormatterHelper.setDecimalSeparator(decimalSeparator); + return this; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimplePieChartValueFormatter.java b/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimplePieChartValueFormatter.java index d27e2eac..e0b04362 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimplePieChartValueFormatter.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/formatter/SimplePieChartValueFormatter.java @@ -5,55 +5,55 @@ public class SimplePieChartValueFormatter implements PieChartValueFormatter { - private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper(); - - public SimplePieChartValueFormatter() { - valueFormatterHelper.determineDecimalSeparator(); - } - - public SimplePieChartValueFormatter(int decimalDigitsNumber) { - this(); - valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); - } - - @Override - public int formatChartValue(char[] formattedValue, SliceValue value) { - return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getValue(), value.getLabelAsChars()); - } - - public int getDecimalDigitsNumber() { - return valueFormatterHelper.getDecimalDigitsNumber(); - } - - public SimplePieChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) { - valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); - return this; - } - - public char[] getAppendedText() { - return valueFormatterHelper.getAppendedText(); - } - - public SimplePieChartValueFormatter setAppendedText(char[] appendedText) { - valueFormatterHelper.setAppendedText(appendedText); - return this; - } - - public char[] getPrependedText() { - return valueFormatterHelper.getPrependedText(); - } - - public SimplePieChartValueFormatter setPrependedText(char[] prependedText) { - valueFormatterHelper.setPrependedText(prependedText); - return this; - } - - public char getDecimalSeparator() { - return valueFormatterHelper.getDecimalSeparator(); - } - - public SimplePieChartValueFormatter setDecimalSeparator(char decimalSeparator) { - valueFormatterHelper.setDecimalSeparator(decimalSeparator); - return this; - } + private ValueFormatterHelper valueFormatterHelper = new ValueFormatterHelper(); + + public SimplePieChartValueFormatter() { + valueFormatterHelper.determineDecimalSeparator(); + } + + public SimplePieChartValueFormatter(int decimalDigitsNumber) { + this(); + valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); + } + + @Override + public int formatChartValue(char[] formattedValue, SliceValue value) { + return valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.getValue(), value.getLabelAsChars()); + } + + public int getDecimalDigitsNumber() { + return valueFormatterHelper.getDecimalDigitsNumber(); + } + + public SimplePieChartValueFormatter setDecimalDigitsNumber(int decimalDigitsNumber) { + valueFormatterHelper.setDecimalDigitsNumber(decimalDigitsNumber); + return this; + } + + public char[] getAppendedText() { + return valueFormatterHelper.getAppendedText(); + } + + public SimplePieChartValueFormatter setAppendedText(char[] appendedText) { + valueFormatterHelper.setAppendedText(appendedText); + return this; + } + + public char[] getPrependedText() { + return valueFormatterHelper.getPrependedText(); + } + + public SimplePieChartValueFormatter setPrependedText(char[] prependedText) { + valueFormatterHelper.setPrependedText(prependedText); + return this; + } + + public char getDecimalSeparator() { + return valueFormatterHelper.getDecimalSeparator(); + } + + public SimplePieChartValueFormatter setDecimalSeparator(char decimalSeparator) { + valueFormatterHelper.setDecimalSeparator(decimalSeparator); + return this; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/formatter/ValueFormatterHelper.java b/hellocharts-library/src/lecho/lib/hellocharts/formatter/ValueFormatterHelper.java index e2a65fcd..f9e5aeb3 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/formatter/ValueFormatterHelper.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/formatter/ValueFormatterHelper.java @@ -6,128 +6,128 @@ import lecho.lib.hellocharts.util.FloatUtils; public class ValueFormatterHelper { - public static final int DEFAULT_DIGITS_NUMBER = 0; - - private int decimalDigitsNumber = Integer.MIN_VALUE; - private char[] appendedText = new char[0]; - private char[] prependedText = new char[0]; - private char decimalSeparator = '.'; - - public void determineDecimalSeparator() { - NumberFormat numberFormat = NumberFormat.getInstance(); - if (numberFormat instanceof DecimalFormat) { - decimalSeparator = ((DecimalFormat) numberFormat).getDecimalFormatSymbols().getDecimalSeparator(); - } - } - - public int getDecimalDigitsNumber() { - return decimalDigitsNumber; - } - - public ValueFormatterHelper setDecimalDigitsNumber(int decimalDigitsNumber) { - this.decimalDigitsNumber = decimalDigitsNumber; - return this; - } - - public char[] getAppendedText() { - return appendedText; - } - - public ValueFormatterHelper setAppendedText(char[] appendedText) { - if (null != appendedText) { - this.appendedText = appendedText; - } - return this; - } - - public char[] getPrependedText() { - return prependedText; - } - - public ValueFormatterHelper setPrependedText(char[] prependedText) { - if (null != prependedText) { - this.prependedText = prependedText; - } - return this; - } - - public char getDecimalSeparator() { - return decimalSeparator; - } - - public ValueFormatterHelper setDecimalSeparator(char decimalSeparator) { - char nullChar = '\0'; - if (nullChar != decimalSeparator) { - this.decimalSeparator = decimalSeparator; - } - return this; - } - - /** - * Formats float value. Result is stored in (output) formattedValue array. Method - * returns number of chars of formatted value. The formatted value starts at index [formattedValue.length - - * charsNumber] and ends at index [formattedValue.length-1]. - * Note: If label is not null it will be used as formattedValue instead of float value. - * Note: Parameter defaultDigitsNumber is used only if you didn't change decimalDigintsNumber value using - * method {@link #setDecimalDigitsNumber(int)}. - */ - public int formatFloatValueWithPrependedAndAppendedText(char[] formattedValue, float value, int defaultDigitsNumber, char[] label) { - if (null != label) { - // If custom label is not null use only name characters as formatted value. - // Copy label into formatted value array. - System.arraycopy(label, 0, formattedValue, formattedValue.length - label.length, label.length); - return label.length; - } - - final int appliedDigitsNumber = getAppliedDecimalDigitsNumber(defaultDigitsNumber); - final int charsNumber = formatFloatValue(formattedValue, value, appliedDigitsNumber); - appendText(formattedValue); - prependText(formattedValue, charsNumber); - return charsNumber + getPrependedText().length + getAppendedText().length; - } - - /** - * @see #formatFloatValueWithPrependedAndAppendedText(char[], float, int, char[]) - */ - public int formatFloatValueWithPrependedAndAppendedText(char[] formattedValue, float value, char[] label) { - return formatFloatValueWithPrependedAndAppendedText(formattedValue, value, DEFAULT_DIGITS_NUMBER, label); - } - - /** - * @see #formatFloatValueWithPrependedAndAppendedText(char[], float, int, char[]) - */ - public int formatFloatValueWithPrependedAndAppendedText(char[] formattedValue, float value, int defaultDigitsNumber) { - return formatFloatValueWithPrependedAndAppendedText(formattedValue, value, defaultDigitsNumber, null); - } - - public int formatFloatValue(char[] formattedValue, float value, int decimalDigitsNumber) { - return FloatUtils.formatFloat(formattedValue, value, formattedValue.length - appendedText.length, decimalDigitsNumber, - decimalSeparator); - } - - public void appendText(char[] formattedValue) { - if (appendedText.length > 0) { - System.arraycopy(appendedText, 0, formattedValue, formattedValue.length - appendedText.length, - appendedText.length); - } - } - - public void prependText(char[] formattedValue, int charsNumber) { - if (prependedText.length > 0) { - System.arraycopy(prependedText, 0, formattedValue, formattedValue.length - charsNumber - appendedText.length - - prependedText.length, prependedText.length); - } - } - - public int getAppliedDecimalDigitsNumber(int defaultDigitsNumber) { - final int appliedDecimalDigitsNumber; - if (decimalDigitsNumber < 0) { - //When decimalDigitsNumber < 0 that means that user didn't set that value and defaultDigitsNumber should be used. - appliedDecimalDigitsNumber = defaultDigitsNumber; - } else { - appliedDecimalDigitsNumber = decimalDigitsNumber; - } - return appliedDecimalDigitsNumber; - } + public static final int DEFAULT_DIGITS_NUMBER = 0; + + private int decimalDigitsNumber = Integer.MIN_VALUE; + private char[] appendedText = new char[0]; + private char[] prependedText = new char[0]; + private char decimalSeparator = '.'; + + public void determineDecimalSeparator() { + NumberFormat numberFormat = NumberFormat.getInstance(); + if (numberFormat instanceof DecimalFormat) { + decimalSeparator = ((DecimalFormat) numberFormat).getDecimalFormatSymbols().getDecimalSeparator(); + } + } + + public int getDecimalDigitsNumber() { + return decimalDigitsNumber; + } + + public ValueFormatterHelper setDecimalDigitsNumber(int decimalDigitsNumber) { + this.decimalDigitsNumber = decimalDigitsNumber; + return this; + } + + public char[] getAppendedText() { + return appendedText; + } + + public ValueFormatterHelper setAppendedText(char[] appendedText) { + if (null != appendedText) { + this.appendedText = appendedText; + } + return this; + } + + public char[] getPrependedText() { + return prependedText; + } + + public ValueFormatterHelper setPrependedText(char[] prependedText) { + if (null != prependedText) { + this.prependedText = prependedText; + } + return this; + } + + public char getDecimalSeparator() { + return decimalSeparator; + } + + public ValueFormatterHelper setDecimalSeparator(char decimalSeparator) { + char nullChar = '\0'; + if (nullChar != decimalSeparator) { + this.decimalSeparator = decimalSeparator; + } + return this; + } + + /** + * Formats float value. Result is stored in (output) formattedValue array. Method + * returns number of chars of formatted value. The formatted value starts at index [formattedValue.length - + * charsNumber] and ends at index [formattedValue.length-1]. + * Note: If label is not null it will be used as formattedValue instead of float value. + * Note: Parameter defaultDigitsNumber is used only if you didn't change decimalDigintsNumber value using + * method {@link #setDecimalDigitsNumber(int)}. + */ + public int formatFloatValueWithPrependedAndAppendedText(char[] formattedValue, float value, int defaultDigitsNumber, char[] label) { + if (null != label) { + // If custom label is not null use only name characters as formatted value. + // Copy label into formatted value array. + System.arraycopy(label, 0, formattedValue, formattedValue.length - label.length, label.length); + return label.length; + } + + final int appliedDigitsNumber = getAppliedDecimalDigitsNumber(defaultDigitsNumber); + final int charsNumber = formatFloatValue(formattedValue, value, appliedDigitsNumber); + appendText(formattedValue); + prependText(formattedValue, charsNumber); + return charsNumber + getPrependedText().length + getAppendedText().length; + } + + /** + * @see #formatFloatValueWithPrependedAndAppendedText(char[], float, int, char[]) + */ + public int formatFloatValueWithPrependedAndAppendedText(char[] formattedValue, float value, char[] label) { + return formatFloatValueWithPrependedAndAppendedText(formattedValue, value, DEFAULT_DIGITS_NUMBER, label); + } + + /** + * @see #formatFloatValueWithPrependedAndAppendedText(char[], float, int, char[]) + */ + public int formatFloatValueWithPrependedAndAppendedText(char[] formattedValue, float value, int defaultDigitsNumber) { + return formatFloatValueWithPrependedAndAppendedText(formattedValue, value, defaultDigitsNumber, null); + } + + public int formatFloatValue(char[] formattedValue, float value, int decimalDigitsNumber) { + return FloatUtils.formatFloat(formattedValue, value, formattedValue.length - appendedText.length, decimalDigitsNumber, + decimalSeparator); + } + + public void appendText(char[] formattedValue) { + if (appendedText.length > 0) { + System.arraycopy(appendedText, 0, formattedValue, formattedValue.length - appendedText.length, + appendedText.length); + } + } + + public void prependText(char[] formattedValue, int charsNumber) { + if (prependedText.length > 0) { + System.arraycopy(prependedText, 0, formattedValue, formattedValue.length - charsNumber - appendedText.length + - prependedText.length, prependedText.length); + } + } + + public int getAppliedDecimalDigitsNumber(int defaultDigitsNumber) { + final int appliedDecimalDigitsNumber; + if (decimalDigitsNumber < 0) { + //When decimalDigitsNumber < 0 that means that user didn't set that value and defaultDigitsNumber should be used. + appliedDecimalDigitsNumber = defaultDigitsNumber; + } else { + appliedDecimalDigitsNumber = decimalDigitsNumber; + } + return appliedDecimalDigitsNumber; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartScroller.java b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartScroller.java index 5891378c..726a57b5 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartScroller.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartScroller.java @@ -13,115 +13,115 @@ */ public class ChartScroller { - private Viewport scrollerStartViewport = new Viewport(); // Used only for zooms and flings - private Point surfaceSizeBuffer = new Point();// Used for scroll and flings - private ScrollerCompat scroller; + private Viewport scrollerStartViewport = new Viewport(); // Used only for zooms and flings + private Point surfaceSizeBuffer = new Point();// Used for scroll and flings + private ScrollerCompat scroller; - public ChartScroller(Context context) { - scroller = ScrollerCompat.create(context); - } + public ChartScroller(Context context) { + scroller = ScrollerCompat.create(context); + } - public boolean startScroll(ChartComputator computator) { - scroller.abortAnimation(); - scrollerStartViewport.set(computator.getCurrentViewport()); - return true; - } + public boolean startScroll(ChartComputator computator) { + scroller.abortAnimation(); + scrollerStartViewport.set(computator.getCurrentViewport()); + return true; + } - public boolean scroll(ChartComputator computator, float distanceX, float distanceY, ScrollResult scrollResult) { + public boolean scroll(ChartComputator computator, float distanceX, float distanceY, ScrollResult scrollResult) { - // Scrolling uses math based on the viewport (as opposed to math using pixels). Pixel offset is the offset in - // screen pixels, while viewport offset is the offset within the current viewport. For additional - // information on - // surface sizes and pixel offsets, see the docs for {@link computeScrollSurfaceSize()}. For additional - // information about the viewport, see the comments for {@link mCurrentViewport}. + // Scrolling uses math based on the viewport (as opposed to math using pixels). Pixel offset is the offset in + // screen pixels, while viewport offset is the offset within the current viewport. For additional + // information on + // surface sizes and pixel offsets, see the docs for {@link computeScrollSurfaceSize()}. For additional + // information about the viewport, see the comments for {@link mCurrentViewport}. - final Viewport maxViewport = computator.getMaximumViewport(); - final Viewport visibleViewport = computator.getVisibleViewport(); - final Viewport currentViewport = computator.getCurrentViewport(); - final Rect contentRect = computator.getContentRectMinusAllMargins(); + final Viewport maxViewport = computator.getMaximumViewport(); + final Viewport visibleViewport = computator.getVisibleViewport(); + final Viewport currentViewport = computator.getCurrentViewport(); + final Rect contentRect = computator.getContentRectMinusAllMargins(); - final boolean canScrollLeft = currentViewport.left > maxViewport.left; - final boolean canScrollRight = currentViewport.right < maxViewport.right; - final boolean canScrollTop = currentViewport.top < maxViewport.top; - final boolean canScrollBottom = currentViewport.bottom > maxViewport.bottom; + final boolean canScrollLeft = currentViewport.left > maxViewport.left; + final boolean canScrollRight = currentViewport.right < maxViewport.right; + final boolean canScrollTop = currentViewport.top < maxViewport.top; + final boolean canScrollBottom = currentViewport.bottom > maxViewport.bottom; - boolean canScrollX = false; - boolean canScrollY = false; + boolean canScrollX = false; + boolean canScrollY = false; - if (canScrollLeft && distanceX <= 0) { - canScrollX = true; - } else if (canScrollRight && distanceX >= 0) { - canScrollX = true; - } + if (canScrollLeft && distanceX <= 0) { + canScrollX = true; + } else if (canScrollRight && distanceX >= 0) { + canScrollX = true; + } - if (canScrollTop && distanceY <= 0) { - canScrollY = true; - } else if (canScrollBottom && distanceY >= 0) { - canScrollY = true; - } + if (canScrollTop && distanceY <= 0) { + canScrollY = true; + } else if (canScrollBottom && distanceY >= 0) { + canScrollY = true; + } - if (canScrollX || canScrollY) { + if (canScrollX || canScrollY) { - computator.computeScrollSurfaceSize(surfaceSizeBuffer); + computator.computeScrollSurfaceSize(surfaceSizeBuffer); - float viewportOffsetX = distanceX * visibleViewport.width() / contentRect.width(); - float viewportOffsetY = -distanceY * visibleViewport.height() / contentRect.height(); + float viewportOffsetX = distanceX * visibleViewport.width() / contentRect.width(); + float viewportOffsetY = -distanceY * visibleViewport.height() / contentRect.height(); - computator - .setViewportTopLeft(currentViewport.left + viewportOffsetX, currentViewport.top + viewportOffsetY); - } + computator + .setViewportTopLeft(currentViewport.left + viewportOffsetX, currentViewport.top + viewportOffsetY); + } - scrollResult.canScrollX = canScrollX; - scrollResult.canScrollY = canScrollY; + scrollResult.canScrollX = canScrollX; + scrollResult.canScrollY = canScrollY; - return canScrollX || canScrollY; - } + return canScrollX || canScrollY; + } - public boolean computeScrollOffset(ChartComputator computator) { - if (scroller.computeScrollOffset()) { - // The scroller isn't finished, meaning a fling or programmatic pan operation is - // currently active. + public boolean computeScrollOffset(ChartComputator computator) { + if (scroller.computeScrollOffset()) { + // The scroller isn't finished, meaning a fling or programmatic pan operation is + // currently active. - final Viewport maxViewport = computator.getMaximumViewport(); + final Viewport maxViewport = computator.getMaximumViewport(); - computator.computeScrollSurfaceSize(surfaceSizeBuffer); + computator.computeScrollSurfaceSize(surfaceSizeBuffer); - final float currXRange = maxViewport.left + maxViewport.width() * scroller.getCurrX() / - surfaceSizeBuffer.x; - final float currYRange = maxViewport.top - maxViewport.height() * scroller.getCurrY() / - surfaceSizeBuffer.y; + final float currXRange = maxViewport.left + maxViewport.width() * scroller.getCurrX() / + surfaceSizeBuffer.x; + final float currYRange = maxViewport.top - maxViewport.height() * scroller.getCurrY() / + surfaceSizeBuffer.y; - computator.setViewportTopLeft(currXRange, currYRange); + computator.setViewportTopLeft(currXRange, currYRange); - return true; - } + return true; + } - return false; - } + return false; + } - public boolean fling(int velocityX, int velocityY, ChartComputator computator) { - // Flings use math in pixels (as opposed to math based on the viewport). - computator.computeScrollSurfaceSize(surfaceSizeBuffer); - scrollerStartViewport.set(computator.getCurrentViewport()); + public boolean fling(int velocityX, int velocityY, ChartComputator computator) { + // Flings use math in pixels (as opposed to math based on the viewport). + computator.computeScrollSurfaceSize(surfaceSizeBuffer); + scrollerStartViewport.set(computator.getCurrentViewport()); - int startX = (int) (surfaceSizeBuffer.x * (scrollerStartViewport.left - computator.getMaximumViewport().left) - / computator.getMaximumViewport().width()); - int startY = (int) (surfaceSizeBuffer.y * (computator.getMaximumViewport().top - scrollerStartViewport.top) / - computator.getMaximumViewport().height()); + int startX = (int) (surfaceSizeBuffer.x * (scrollerStartViewport.left - computator.getMaximumViewport().left) + / computator.getMaximumViewport().width()); + int startY = (int) (surfaceSizeBuffer.y * (computator.getMaximumViewport().top - scrollerStartViewport.top) / + computator.getMaximumViewport().height()); - // TODO probably should be mScroller.forceFinish but ScrollerCompat doesn't have that method. - scroller.abortAnimation(); + // TODO probably should be mScroller.forceFinish but ScrollerCompat doesn't have that method. + scroller.abortAnimation(); - final int width = computator.getContentRectMinusAllMargins().width(); - final int height = computator.getContentRectMinusAllMargins().height(); - scroller.fling(startX, startY, velocityX, velocityY, 0, surfaceSizeBuffer.x - width + 1, 0, - surfaceSizeBuffer.y - height + 1); - return true; - } + final int width = computator.getContentRectMinusAllMargins().width(); + final int height = computator.getContentRectMinusAllMargins().height(); + scroller.fling(startX, startY, velocityX, velocityY, 0, surfaceSizeBuffer.x - width + 1, 0, + surfaceSizeBuffer.y - height + 1); + return true; + } - public static class ScrollResult { - public boolean canScrollX; - public boolean canScrollY; - } + public static class ScrollResult { + public boolean canScrollX; + public boolean canScrollY; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartTouchHandler.java b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartTouchHandler.java index c5f3721f..772a3236 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartTouchHandler.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartTouchHandler.java @@ -16,317 +16,317 @@ * Default touch handler for most charts. Handles value touch, scroll, fling and zoom. */ public class ChartTouchHandler { - protected GestureDetector gestureDetector; - protected ScaleGestureDetector scaleGestureDetector; - protected ChartScroller chartScroller; - protected ChartZoomer chartZoomer; - protected Chart chart; - protected ChartComputator computator; - protected ChartRenderer renderer; - - protected boolean isZoomEnabled = true; - protected boolean isScrollEnabled = true; - protected boolean isValueTouchEnabled = true; - protected boolean isValueSelectionEnabled = false; - - /** - * Used only for selection mode to avoid calling listener multiple times for the same selection. Small thing but it - * is more intuitive this way. - */ - protected SelectedValue selectionModeOldValue = new SelectedValue(); - - protected SelectedValue selectedValue = new SelectedValue(); - protected SelectedValue oldSelectedValue = new SelectedValue(); - - /** - * ViewParent to disallow touch events interception if chart is within scroll container. - */ - protected ViewParent viewParent; - - /** - * Type of scroll of container, horizontal or vertical. - */ - protected ContainerScrollType containerScrollType; - - public ChartTouchHandler(Context context, Chart chart) { - this.chart = chart; - this.computator = chart.getChartComputator(); - this.renderer = chart.getChartRenderer(); - gestureDetector = new GestureDetector(context, new ChartGestureListener()); - scaleGestureDetector = new ScaleGestureDetector(context, new ChartScaleGestureListener()); - chartScroller = new ChartScroller(context); - chartZoomer = new ChartZoomer(context, ZoomType.HORIZONTAL_AND_VERTICAL); - } - - public void resetTouchHandler() { - this.computator = chart.getChartComputator(); - this.renderer = chart.getChartRenderer(); - } - - /** - * Computes scroll and zoom using {@link ChartScroller} and {@link ChartZoomer}. This method returns true if - * scroll/zoom was computed and chart needs to be invalidated. - */ - public boolean computeScroll() { - boolean needInvalidate = false; - if (isScrollEnabled && chartScroller.computeScrollOffset(computator)) { - needInvalidate = true; - } - if (isZoomEnabled && chartZoomer.computeZoom(computator)) { - needInvalidate = true; - } - return needInvalidate; - } - - /** - * Handle chart touch event(gestures, clicks). Return true if gesture was handled and chart needs to be - * invalidated. - */ - public boolean handleTouchEvent(MotionEvent event) { - boolean needInvalidate = false; - - // TODO: detectors always return true, use class member needInvalidate instead local variable as workaround. - // This flag should be computed inside gesture listeners methods to avoid invalidation. - needInvalidate = gestureDetector.onTouchEvent(event); - - needInvalidate = scaleGestureDetector.onTouchEvent(event) || needInvalidate; - - if (isZoomEnabled && scaleGestureDetector.isInProgress()) { - // Special case: if view is inside scroll container and user is scaling disable touch interception by - // parent. - disallowParentInterceptTouchEvent(); - } - - if (isValueTouchEnabled) { - needInvalidate = computeTouch(event) || needInvalidate; - } - - return needInvalidate; - } - - /** - * Handle chart touch event(gestures, clicks). Return true if gesture was handled and chart needs to be - * invalidated. - * If viewParent and containerScrollType are not null chart can be scrolled and scaled within horizontal or - * vertical - * scroll container like ViewPager. - */ - public boolean handleTouchEvent(MotionEvent event, ViewParent viewParent, - ContainerScrollType containerScrollType) { - this.viewParent = viewParent; - this.containerScrollType = containerScrollType; - - return handleTouchEvent(event); - } - - /** - * Disallow parent view from intercepting touch events. Use it for chart that is within some scroll container i.e. - * ViewPager. - */ - private void disallowParentInterceptTouchEvent() { - if (null != viewParent) { - viewParent.requestDisallowInterceptTouchEvent(true); - } - } - - /** - * Allow parent view to intercept touch events if chart cannot be scroll horizontally or vertically according to - * the - * current value of {@link #containerScrollType}. - */ - private void allowParentInterceptTouchEvent(ScrollResult scrollResult) { - if (null != viewParent) { - if (ContainerScrollType.HORIZONTAL == containerScrollType && !scrollResult.canScrollX - && !scaleGestureDetector.isInProgress()) { - viewParent.requestDisallowInterceptTouchEvent(false); - } else if (ContainerScrollType.VERTICAL == containerScrollType && !scrollResult.canScrollY - && !scaleGestureDetector.isInProgress()) { - viewParent.requestDisallowInterceptTouchEvent(false); - } - } - } - - private boolean computeTouch(MotionEvent event) { - boolean needInvalidate = false; - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - boolean wasTouched = renderer.isTouched(); - boolean isTouched = checkTouch(event.getX(), event.getY()); - if (wasTouched != isTouched) { - needInvalidate = true; - - if (isValueSelectionEnabled) { - selectionModeOldValue.clear(); - if (wasTouched && !renderer.isTouched()) { - chart.callTouchListener(); - } - } - } - break; - case MotionEvent.ACTION_UP: - if (renderer.isTouched()) { - if (checkTouch(event.getX(), event.getY())) { - if (isValueSelectionEnabled) { - // For selection mode call listener only if selected value changed, - // that means that should be - // first(selection) click on given value. - if (!selectionModeOldValue.equals(selectedValue)) { - selectionModeOldValue.set(selectedValue); - chart.callTouchListener(); - } - } else { - chart.callTouchListener(); - renderer.clearTouch(); - } - } else { - renderer.clearTouch(); - } - needInvalidate = true; - } - break; - case MotionEvent.ACTION_MOVE: - // If value was touched and now touch point is outside of value area - clear touch and invalidate, user - // probably moved finger away from given chart value. - if (renderer.isTouched()) { - if (!checkTouch(event.getX(), event.getY())) { - renderer.clearTouch(); - needInvalidate = true; - } - } - - break; - case MotionEvent.ACTION_CANCEL: - if (renderer.isTouched()) { - renderer.clearTouch(); - needInvalidate = true; - } - break; - } - return needInvalidate; - } - - private boolean checkTouch(float touchX, float touchY) { - oldSelectedValue.set(selectedValue); - selectedValue.clear(); - - if (renderer.checkTouch(touchX, touchY)) { - selectedValue.set(renderer.getSelectedValue()); - } - - // Check if selection is still on the same value, if not return false. - if (oldSelectedValue.isSet() && selectedValue.isSet() && !oldSelectedValue.equals(selectedValue)) { - return false; - } else { - return renderer.isTouched(); - } - } - - public boolean isZoomEnabled() { - return isZoomEnabled; - } - - public void setZoomEnabled(boolean isZoomEnabled) { - this.isZoomEnabled = isZoomEnabled; - - } - - public boolean isScrollEnabled() { - return isScrollEnabled; - } - - public void setScrollEnabled(boolean isScrollEnabled) { - this.isScrollEnabled = isScrollEnabled; - } - - public ZoomType getZoomType() { - return chartZoomer.getZoomType(); - } - - public void setZoomType(ZoomType zoomType) { - chartZoomer.setZoomType(zoomType); - } - - public boolean isValueTouchEnabled() { - return isValueTouchEnabled; - } - - public void setValueTouchEnabled(boolean isValueTouchEnabled) { - this.isValueTouchEnabled = isValueTouchEnabled; - } - - public boolean isValueSelectionEnabled() { - return isValueSelectionEnabled; - } - - public void setValueSelectionEnabled(boolean isValueSelectionEnabled) { - this.isValueSelectionEnabled = isValueSelectionEnabled; - } - - protected class ChartScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { - - @Override - public boolean onScale(ScaleGestureDetector detector) { - if (isZoomEnabled) { - float scale = 2.0f - detector.getScaleFactor(); - if (Float.isInfinite(scale)) { - scale = 1; - } - return chartZoomer.scale(computator, detector.getFocusX(), detector.getFocusY(), scale); - } - - return false; - } - } - - protected class ChartGestureListener extends GestureDetector.SimpleOnGestureListener { - - protected ScrollResult scrollResult = new ScrollResult(); - - @Override - public boolean onDown(MotionEvent e) { - if (isScrollEnabled) { - - disallowParentInterceptTouchEvent(); - - return chartScroller.startScroll(computator); - } + protected GestureDetector gestureDetector; + protected ScaleGestureDetector scaleGestureDetector; + protected ChartScroller chartScroller; + protected ChartZoomer chartZoomer; + protected Chart chart; + protected ChartComputator computator; + protected ChartRenderer renderer; + + protected boolean isZoomEnabled = true; + protected boolean isScrollEnabled = true; + protected boolean isValueTouchEnabled = true; + protected boolean isValueSelectionEnabled = false; + + /** + * Used only for selection mode to avoid calling listener multiple times for the same selection. Small thing but it + * is more intuitive this way. + */ + protected SelectedValue selectionModeOldValue = new SelectedValue(); + + protected SelectedValue selectedValue = new SelectedValue(); + protected SelectedValue oldSelectedValue = new SelectedValue(); + + /** + * ViewParent to disallow touch events interception if chart is within scroll container. + */ + protected ViewParent viewParent; + + /** + * Type of scroll of container, horizontal or vertical. + */ + protected ContainerScrollType containerScrollType; + + public ChartTouchHandler(Context context, Chart chart) { + this.chart = chart; + this.computator = chart.getChartComputator(); + this.renderer = chart.getChartRenderer(); + gestureDetector = new GestureDetector(context, new ChartGestureListener()); + scaleGestureDetector = new ScaleGestureDetector(context, new ChartScaleGestureListener()); + chartScroller = new ChartScroller(context); + chartZoomer = new ChartZoomer(context, ZoomType.HORIZONTAL_AND_VERTICAL); + } + + public void resetTouchHandler() { + this.computator = chart.getChartComputator(); + this.renderer = chart.getChartRenderer(); + } + + /** + * Computes scroll and zoom using {@link ChartScroller} and {@link ChartZoomer}. This method returns true if + * scroll/zoom was computed and chart needs to be invalidated. + */ + public boolean computeScroll() { + boolean needInvalidate = false; + if (isScrollEnabled && chartScroller.computeScrollOffset(computator)) { + needInvalidate = true; + } + if (isZoomEnabled && chartZoomer.computeZoom(computator)) { + needInvalidate = true; + } + return needInvalidate; + } + + /** + * Handle chart touch event(gestures, clicks). Return true if gesture was handled and chart needs to be + * invalidated. + */ + public boolean handleTouchEvent(MotionEvent event) { + boolean needInvalidate = false; + + // TODO: detectors always return true, use class member needInvalidate instead local variable as workaround. + // This flag should be computed inside gesture listeners methods to avoid invalidation. + needInvalidate = gestureDetector.onTouchEvent(event); + + needInvalidate = scaleGestureDetector.onTouchEvent(event) || needInvalidate; + + if (isZoomEnabled && scaleGestureDetector.isInProgress()) { + // Special case: if view is inside scroll container and user is scaling disable touch interception by + // parent. + disallowParentInterceptTouchEvent(); + } + + if (isValueTouchEnabled) { + needInvalidate = computeTouch(event) || needInvalidate; + } + + return needInvalidate; + } + + /** + * Handle chart touch event(gestures, clicks). Return true if gesture was handled and chart needs to be + * invalidated. + * If viewParent and containerScrollType are not null chart can be scrolled and scaled within horizontal or + * vertical + * scroll container like ViewPager. + */ + public boolean handleTouchEvent(MotionEvent event, ViewParent viewParent, + ContainerScrollType containerScrollType) { + this.viewParent = viewParent; + this.containerScrollType = containerScrollType; + + return handleTouchEvent(event); + } + + /** + * Disallow parent view from intercepting touch events. Use it for chart that is within some scroll container i.e. + * ViewPager. + */ + private void disallowParentInterceptTouchEvent() { + if (null != viewParent) { + viewParent.requestDisallowInterceptTouchEvent(true); + } + } + + /** + * Allow parent view to intercept touch events if chart cannot be scroll horizontally or vertically according to + * the + * current value of {@link #containerScrollType}. + */ + private void allowParentInterceptTouchEvent(ScrollResult scrollResult) { + if (null != viewParent) { + if (ContainerScrollType.HORIZONTAL == containerScrollType && !scrollResult.canScrollX + && !scaleGestureDetector.isInProgress()) { + viewParent.requestDisallowInterceptTouchEvent(false); + } else if (ContainerScrollType.VERTICAL == containerScrollType && !scrollResult.canScrollY + && !scaleGestureDetector.isInProgress()) { + viewParent.requestDisallowInterceptTouchEvent(false); + } + } + } + + private boolean computeTouch(MotionEvent event) { + boolean needInvalidate = false; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + boolean wasTouched = renderer.isTouched(); + boolean isTouched = checkTouch(event.getX(), event.getY()); + if (wasTouched != isTouched) { + needInvalidate = true; + + if (isValueSelectionEnabled) { + selectionModeOldValue.clear(); + if (wasTouched && !renderer.isTouched()) { + chart.callTouchListener(); + } + } + } + break; + case MotionEvent.ACTION_UP: + if (renderer.isTouched()) { + if (checkTouch(event.getX(), event.getY())) { + if (isValueSelectionEnabled) { + // For selection mode call listener only if selected value changed, + // that means that should be + // first(selection) click on given value. + if (!selectionModeOldValue.equals(selectedValue)) { + selectionModeOldValue.set(selectedValue); + chart.callTouchListener(); + } + } else { + chart.callTouchListener(); + renderer.clearTouch(); + } + } else { + renderer.clearTouch(); + } + needInvalidate = true; + } + break; + case MotionEvent.ACTION_MOVE: + // If value was touched and now touch point is outside of value area - clear touch and invalidate, user + // probably moved finger away from given chart value. + if (renderer.isTouched()) { + if (!checkTouch(event.getX(), event.getY())) { + renderer.clearTouch(); + needInvalidate = true; + } + } + + break; + case MotionEvent.ACTION_CANCEL: + if (renderer.isTouched()) { + renderer.clearTouch(); + needInvalidate = true; + } + break; + } + return needInvalidate; + } + + private boolean checkTouch(float touchX, float touchY) { + oldSelectedValue.set(selectedValue); + selectedValue.clear(); + + if (renderer.checkTouch(touchX, touchY)) { + selectedValue.set(renderer.getSelectedValue()); + } + + // Check if selection is still on the same value, if not return false. + if (oldSelectedValue.isSet() && selectedValue.isSet() && !oldSelectedValue.equals(selectedValue)) { + return false; + } else { + return renderer.isTouched(); + } + } + + public boolean isZoomEnabled() { + return isZoomEnabled; + } + + public void setZoomEnabled(boolean isZoomEnabled) { + this.isZoomEnabled = isZoomEnabled; + + } + + public boolean isScrollEnabled() { + return isScrollEnabled; + } + + public void setScrollEnabled(boolean isScrollEnabled) { + this.isScrollEnabled = isScrollEnabled; + } + + public ZoomType getZoomType() { + return chartZoomer.getZoomType(); + } + + public void setZoomType(ZoomType zoomType) { + chartZoomer.setZoomType(zoomType); + } + + public boolean isValueTouchEnabled() { + return isValueTouchEnabled; + } + + public void setValueTouchEnabled(boolean isValueTouchEnabled) { + this.isValueTouchEnabled = isValueTouchEnabled; + } + + public boolean isValueSelectionEnabled() { + return isValueSelectionEnabled; + } + + public void setValueSelectionEnabled(boolean isValueSelectionEnabled) { + this.isValueSelectionEnabled = isValueSelectionEnabled; + } + + protected class ChartScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + + @Override + public boolean onScale(ScaleGestureDetector detector) { + if (isZoomEnabled) { + float scale = 2.0f - detector.getScaleFactor(); + if (Float.isInfinite(scale)) { + scale = 1; + } + return chartZoomer.scale(computator, detector.getFocusX(), detector.getFocusY(), scale); + } + + return false; + } + } + + protected class ChartGestureListener extends GestureDetector.SimpleOnGestureListener { + + protected ScrollResult scrollResult = new ScrollResult(); + + @Override + public boolean onDown(MotionEvent e) { + if (isScrollEnabled) { + + disallowParentInterceptTouchEvent(); + + return chartScroller.startScroll(computator); + } - return false; + return false; - } + } - @Override - public boolean onDoubleTap(MotionEvent e) { - if (isZoomEnabled) { - return chartZoomer.startZoom(e, computator); - } + @Override + public boolean onDoubleTap(MotionEvent e) { + if (isZoomEnabled) { + return chartZoomer.startZoom(e, computator); + } - return false; - } + return false; + } - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (isScrollEnabled) { - boolean canScroll = chartScroller - .scroll(computator, distanceX, distanceY, scrollResult); + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + if (isScrollEnabled) { + boolean canScroll = chartScroller + .scroll(computator, distanceX, distanceY, scrollResult); - allowParentInterceptTouchEvent(scrollResult); + allowParentInterceptTouchEvent(scrollResult); - return canScroll; - } + return canScroll; + } - return false; + return false; - } + } - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if (isScrollEnabled) { - return chartScroller.fling((int) -velocityX, (int) -velocityY, computator); - } + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (isScrollEnabled) { + return chartScroller.fling((int) -velocityX, (int) -velocityY, computator); + } - return false; - } - } + return false; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartZoomer.java b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartZoomer.java index 7b7d557d..b61cf251 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartZoomer.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ChartZoomer.java @@ -11,86 +11,86 @@ * Encapsulates zooming functionality. */ public class ChartZoomer { - public static final float ZOOM_AMOUNT = 0.25f; - private ZoomerCompat zoomer; - private ZoomType zoomType; - private PointF zoomFocalPoint = new PointF();// Used for double tap zoom - private PointF viewportFocus = new PointF(); - private Viewport scrollerStartViewport = new Viewport(); // Used only for zooms and flings + public static final float ZOOM_AMOUNT = 0.25f; + private ZoomerCompat zoomer; + private ZoomType zoomType; + private PointF zoomFocalPoint = new PointF();// Used for double tap zoom + private PointF viewportFocus = new PointF(); + private Viewport scrollerStartViewport = new Viewport(); // Used only for zooms and flings - public ChartZoomer(Context context, ZoomType zoomType) { - zoomer = new ZoomerCompat(context); - this.zoomType = zoomType; - } + public ChartZoomer(Context context, ZoomType zoomType) { + zoomer = new ZoomerCompat(context); + this.zoomType = zoomType; + } - public boolean startZoom(MotionEvent e, ChartComputator computator) { - zoomer.forceFinished(true); - scrollerStartViewport.set(computator.getCurrentViewport()); - if (!computator.rawPixelsToDataPoint(e.getX(), e.getY(), zoomFocalPoint)) { - // Focus point is not within content area. - return false; - } - zoomer.startZoom(ZOOM_AMOUNT); - return true; - } + public boolean startZoom(MotionEvent e, ChartComputator computator) { + zoomer.forceFinished(true); + scrollerStartViewport.set(computator.getCurrentViewport()); + if (!computator.rawPixelsToDataPoint(e.getX(), e.getY(), zoomFocalPoint)) { + // Focus point is not within content area. + return false; + } + zoomer.startZoom(ZOOM_AMOUNT); + return true; + } - public boolean computeZoom(ChartComputator computator) { - if (zoomer.computeZoom()) { - // Performs the zoom since a zoom is in progress. - final float newWidth = (1.0f - zoomer.getCurrZoom()) * scrollerStartViewport.width(); - final float newHeight = (1.0f - zoomer.getCurrZoom()) * scrollerStartViewport.height(); - final float pointWithinViewportX = (zoomFocalPoint.x - scrollerStartViewport.left) - / scrollerStartViewport.width(); - final float pointWithinViewportY = (zoomFocalPoint.y - scrollerStartViewport.bottom) - / scrollerStartViewport.height(); + public boolean computeZoom(ChartComputator computator) { + if (zoomer.computeZoom()) { + // Performs the zoom since a zoom is in progress. + final float newWidth = (1.0f - zoomer.getCurrZoom()) * scrollerStartViewport.width(); + final float newHeight = (1.0f - zoomer.getCurrZoom()) * scrollerStartViewport.height(); + final float pointWithinViewportX = (zoomFocalPoint.x - scrollerStartViewport.left) + / scrollerStartViewport.width(); + final float pointWithinViewportY = (zoomFocalPoint.y - scrollerStartViewport.bottom) + / scrollerStartViewport.height(); - float left = zoomFocalPoint.x - newWidth * pointWithinViewportX; - float top = zoomFocalPoint.y + newHeight * (1 - pointWithinViewportY); - float right = zoomFocalPoint.x + newWidth * (1 - pointWithinViewportX); - float bottom = zoomFocalPoint.y - newHeight * pointWithinViewportY; - setCurrentViewport(computator, left, top, right, bottom); - return true; - } - return false; - } + float left = zoomFocalPoint.x - newWidth * pointWithinViewportX; + float top = zoomFocalPoint.y + newHeight * (1 - pointWithinViewportY); + float right = zoomFocalPoint.x + newWidth * (1 - pointWithinViewportX); + float bottom = zoomFocalPoint.y - newHeight * pointWithinViewportY; + setCurrentViewport(computator, left, top, right, bottom); + return true; + } + return false; + } - public boolean scale(ChartComputator computator, float focusX, float focusY, float scale) { - /** - * Smaller viewport means bigger zoom so for zoomIn scale should have value <1, for zoomOout >1 - */ - final float newWidth = scale * computator.getCurrentViewport().width(); - final float newHeight = scale * computator.getCurrentViewport().height(); - if (!computator.rawPixelsToDataPoint(focusX, focusY, viewportFocus)) { - // Focus point is not within content area. - return false; - } + public boolean scale(ChartComputator computator, float focusX, float focusY, float scale) { + /** + * Smaller viewport means bigger zoom so for zoomIn scale should have value <1, for zoomOout >1 + */ + final float newWidth = scale * computator.getCurrentViewport().width(); + final float newHeight = scale * computator.getCurrentViewport().height(); + if (!computator.rawPixelsToDataPoint(focusX, focusY, viewportFocus)) { + // Focus point is not within content area. + return false; + } - float left = viewportFocus.x - (focusX - computator.getContentRectMinusAllMargins().left) - * (newWidth / computator.getContentRectMinusAllMargins().width()); - float top = viewportFocus.y + (focusY - computator.getContentRectMinusAllMargins().top) - * (newHeight / computator.getContentRectMinusAllMargins().height()); - float right = left + newWidth; - float bottom = top - newHeight; - setCurrentViewport(computator, left, top, right, bottom); - return true; - } + float left = viewportFocus.x - (focusX - computator.getContentRectMinusAllMargins().left) + * (newWidth / computator.getContentRectMinusAllMargins().width()); + float top = viewportFocus.y + (focusY - computator.getContentRectMinusAllMargins().top) + * (newHeight / computator.getContentRectMinusAllMargins().height()); + float right = left + newWidth; + float bottom = top - newHeight; + setCurrentViewport(computator, left, top, right, bottom); + return true; + } - private void setCurrentViewport(ChartComputator computator, float left, float top, float right, float bottom) { - Viewport currentViewport = computator.getCurrentViewport(); - if (ZoomType.HORIZONTAL_AND_VERTICAL == zoomType) { - computator.setCurrentViewport(left, top, right, bottom); - } else if (ZoomType.HORIZONTAL == zoomType) { - computator.setCurrentViewport(left, currentViewport.top, right, currentViewport.bottom); - } else if (ZoomType.VERTICAL == zoomType) { - computator.setCurrentViewport(currentViewport.left, top, currentViewport.right, bottom); - } - } + private void setCurrentViewport(ChartComputator computator, float left, float top, float right, float bottom) { + Viewport currentViewport = computator.getCurrentViewport(); + if (ZoomType.HORIZONTAL_AND_VERTICAL == zoomType) { + computator.setCurrentViewport(left, top, right, bottom); + } else if (ZoomType.HORIZONTAL == zoomType) { + computator.setCurrentViewport(left, currentViewport.top, right, currentViewport.bottom); + } else if (ZoomType.VERTICAL == zoomType) { + computator.setCurrentViewport(currentViewport.left, top, currentViewport.right, bottom); + } + } - public ZoomType getZoomType() { - return zoomType; - } + public ZoomType getZoomType() { + return zoomType; + } - public void setZoomType(ZoomType zoomType) { - this.zoomType = zoomType; - } + public void setZoomType(ZoomType zoomType) { + this.zoomType = zoomType; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ContainerScrollType.java b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ContainerScrollType.java index b9f818a1..726c861e 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ContainerScrollType.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ContainerScrollType.java @@ -2,8 +2,7 @@ /** * Enum used to inform chart in which type of container it exists. - * */ public enum ContainerScrollType { - HORIZONTAL, VERTICAL + HORIZONTAL, VERTICAL } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/gesture/PieChartTouchHandler.java b/hellocharts-library/src/lecho/lib/hellocharts/gesture/PieChartTouchHandler.java index 750793b5..2407cab6 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/gesture/PieChartTouchHandler.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/gesture/PieChartTouchHandler.java @@ -1,6 +1,5 @@ package lecho.lib.hellocharts.gesture; -import lecho.lib.hellocharts.view.PieChartView; import android.content.Context; import android.graphics.RectF; import android.support.v4.widget.ScrollerCompat; @@ -8,153 +7,150 @@ import android.view.MotionEvent; import android.view.ScaleGestureDetector; +import lecho.lib.hellocharts.view.PieChartView; + /** * Touch handler for PieChart. It doesn't handle zoom and scroll like default ChartTouchHandler. Instead it uses * Scroller(ScrollerCompat) directly to compute PieChart rotation when user scroll. ChartScroller and ChartZoomer are * not really used here. - * */ public class PieChartTouchHandler extends ChartTouchHandler { - /** - * The initial fling velocity is divided by this amount. - */ - public static final int FLING_VELOCITY_DOWNSCALE = 4; - /** - * PieChartTouchHandler uses its own instance of Scroller. - */ - protected ScrollerCompat scroller; - /** - * Reference to PieChartView to use some methods specific for that kind of chart. - */ - protected PieChartView pieChart; - - private boolean isRotationEnabled = true; - - public PieChartTouchHandler(Context context, PieChartView chart) { - super(context, chart); - pieChart = (PieChartView) chart; - scroller = ScrollerCompat.create(context); - gestureDetector = new GestureDetector(context, new ChartGestureListener()); - scaleGestureDetector = new ScaleGestureDetector(context, new ChartScaleGestureListener()); - isZoomEnabled = false;// Zoom is not supported by PieChart. - } - - @Override - public boolean computeScroll() { - if (!isRotationEnabled) { - return false; - } - if (scroller.computeScrollOffset()) { - pieChart.setChartRotation(scroller.getCurrY(), false); - // pieChart.setChartRotation() will invalidate view so no need to return true; - } - return false; - } - - @Override - public boolean handleTouchEvent(MotionEvent event) { - boolean needInvalidate = super.handleTouchEvent(event); - - if (isRotationEnabled) { - needInvalidate = gestureDetector.onTouchEvent(event) || needInvalidate; - } - return needInvalidate; - } - - public boolean isRotationEnabled() { - return isRotationEnabled; - } - - public void setRotationEnabled(boolean isRotationEnabled) { - this.isRotationEnabled = isRotationEnabled; - } - - private class ChartScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { - - @Override - public boolean onScale(ScaleGestureDetector detector) { - // No scale for PieChart. - return false; - } - } - - private class ChartGestureListener extends GestureDetector.SimpleOnGestureListener { - @Override - public boolean onDown(MotionEvent e) { - if (isRotationEnabled) { - scroller.abortAnimation(); - return true; - } - - return false; - - } - - @Override - public boolean onDoubleTap(MotionEvent e) { - return false; - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (isRotationEnabled) { - // Set the pie rotation directly. - final RectF circleOval = pieChart.getCircleOval(); - final float centerX = circleOval.centerX(); - final float centerY = circleOval.centerY(); - float scrollTheta = vectorToScalarScroll(distanceX, distanceY, e2.getX() - centerX, e2.getY() - centerY); - pieChart.setChartRotation(pieChart.getChartRotation() - (int) scrollTheta / FLING_VELOCITY_DOWNSCALE, - false); - return true; - } - - return false; - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if (isRotationEnabled) { - // Set up the Scroller for a fling - final RectF circleOval = pieChart.getCircleOval(); - final float centerX = circleOval.centerX(); - final float centerY = circleOval.centerY(); - float scrollTheta = vectorToScalarScroll(velocityX, velocityY, e2.getX() - centerX, e2.getY() - centerY); - scroller.abortAnimation(); - scroller.fling(0, (int) pieChart.getChartRotation(), 0, (int) scrollTheta / FLING_VELOCITY_DOWNSCALE, - 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE); - return true; - } - - return false; - } - - /** - * Helper method for translating (x,y) scroll vectors into scalar rotation of the pie. - * - * @param dx - * The x component of the current scroll vector. - * @param dy - * The y component of the current scroll vector. - * @param x - * The x position of the current touch, relative to the pie center. - * @param y - * The y position of the current touch, relative to the pie center. - * @return The scalar representing the change in angular position for this scroll. - */ - private float vectorToScalarScroll(float dx, float dy, float x, float y) { - // get the length of the vector - float l = (float) Math.sqrt(dx * dx + dy * dy); - - // decide if the scalar should be negative or positive by finding - // the dot product of the vector perpendicular to (x,y). - float crossX = -y; - float crossY = x; - - float dot = (crossX * dx + crossY * dy); - float sign = Math.signum(dot); - - return l * sign; - } - } + /** + * The initial fling velocity is divided by this amount. + */ + public static final int FLING_VELOCITY_DOWNSCALE = 4; + /** + * PieChartTouchHandler uses its own instance of Scroller. + */ + protected ScrollerCompat scroller; + /** + * Reference to PieChartView to use some methods specific for that kind of chart. + */ + protected PieChartView pieChart; + + private boolean isRotationEnabled = true; + + public PieChartTouchHandler(Context context, PieChartView chart) { + super(context, chart); + pieChart = (PieChartView) chart; + scroller = ScrollerCompat.create(context); + gestureDetector = new GestureDetector(context, new ChartGestureListener()); + scaleGestureDetector = new ScaleGestureDetector(context, new ChartScaleGestureListener()); + isZoomEnabled = false;// Zoom is not supported by PieChart. + } + + @Override + public boolean computeScroll() { + if (!isRotationEnabled) { + return false; + } + if (scroller.computeScrollOffset()) { + pieChart.setChartRotation(scroller.getCurrY(), false); + // pieChart.setChartRotation() will invalidate view so no need to return true; + } + return false; + } + + @Override + public boolean handleTouchEvent(MotionEvent event) { + boolean needInvalidate = super.handleTouchEvent(event); + + if (isRotationEnabled) { + needInvalidate = gestureDetector.onTouchEvent(event) || needInvalidate; + } + return needInvalidate; + } + + public boolean isRotationEnabled() { + return isRotationEnabled; + } + + public void setRotationEnabled(boolean isRotationEnabled) { + this.isRotationEnabled = isRotationEnabled; + } + + private class ChartScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + + @Override + public boolean onScale(ScaleGestureDetector detector) { + // No scale for PieChart. + return false; + } + } + + private class ChartGestureListener extends GestureDetector.SimpleOnGestureListener { + @Override + public boolean onDown(MotionEvent e) { + if (isRotationEnabled) { + scroller.abortAnimation(); + return true; + } + + return false; + + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + return false; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + if (isRotationEnabled) { + // Set the pie rotation directly. + final RectF circleOval = pieChart.getCircleOval(); + final float centerX = circleOval.centerX(); + final float centerY = circleOval.centerY(); + float scrollTheta = vectorToScalarScroll(distanceX, distanceY, e2.getX() - centerX, e2.getY() - centerY); + pieChart.setChartRotation(pieChart.getChartRotation() - (int) scrollTheta / FLING_VELOCITY_DOWNSCALE, + false); + return true; + } + + return false; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (isRotationEnabled) { + // Set up the Scroller for a fling + final RectF circleOval = pieChart.getCircleOval(); + final float centerX = circleOval.centerX(); + final float centerY = circleOval.centerY(); + float scrollTheta = vectorToScalarScroll(velocityX, velocityY, e2.getX() - centerX, e2.getY() - centerY); + scroller.abortAnimation(); + scroller.fling(0, (int) pieChart.getChartRotation(), 0, (int) scrollTheta / FLING_VELOCITY_DOWNSCALE, + 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE); + return true; + } + + return false; + } + + /** + * Helper method for translating (x,y) scroll vectors into scalar rotation of the pie. + * + * @param dx The x component of the current scroll vector. + * @param dy The y component of the current scroll vector. + * @param x The x position of the current touch, relative to the pie center. + * @param y The y position of the current touch, relative to the pie center. + * @return The scalar representing the change in angular position for this scroll. + */ + private float vectorToScalarScroll(float dx, float dy, float x, float y) { + // get the length of the vector + float l = (float) Math.sqrt(dx * dx + dy * dy); + + // decide if the scalar should be negative or positive by finding + // the dot product of the vector perpendicular to (x,y). + float crossX = -y; + float crossY = x; + + float dot = (crossX * dx + crossY * dy); + float sign = Math.signum(dot); + + return l * sign; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/gesture/PreviewChartTouchHandler.java b/hellocharts-library/src/lecho/lib/hellocharts/gesture/PreviewChartTouchHandler.java index 81fd6fba..0d9f711c 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/gesture/PreviewChartTouchHandler.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/gesture/PreviewChartTouchHandler.java @@ -1,54 +1,54 @@ package lecho.lib.hellocharts.gesture; -import lecho.lib.hellocharts.view.Chart; import android.content.Context; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; +import lecho.lib.hellocharts.view.Chart; + /** * Touch Handler for preview charts. It scroll and zoom only preview area, not all preview chart data. - * */ public class PreviewChartTouchHandler extends ChartTouchHandler { - public PreviewChartTouchHandler(Context context, Chart chart) { - super(context, chart); - gestureDetector = new GestureDetector(context, new PreviewChartGestureListener()); - scaleGestureDetector = new ScaleGestureDetector(context, new ChartScaleGestureListener()); - - // Disable value touch and selection mode, by default not needed for preview chart. - isValueTouchEnabled = false; - isValueSelectionEnabled = false; - } - - protected class ChartScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { - - @Override - public boolean onScale(ScaleGestureDetector detector) { - if (isZoomEnabled) { - float scale = detector.getCurrentSpan() / detector.getPreviousSpan(); - if (Float.isInfinite(scale)) { - scale = 1; - } - return chartZoomer.scale(computator, detector.getFocusX(), detector.getFocusY(), scale); - } - - return false; - } - } - - protected class PreviewChartGestureListener extends ChartGestureListener { - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - return super.onScroll(e1, e2, -distanceX, -distanceY); - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - return super.onFling(e1, e2, -velocityX, -velocityY); - } - } + public PreviewChartTouchHandler(Context context, Chart chart) { + super(context, chart); + gestureDetector = new GestureDetector(context, new PreviewChartGestureListener()); + scaleGestureDetector = new ScaleGestureDetector(context, new ChartScaleGestureListener()); + + // Disable value touch and selection mode, by default not needed for preview chart. + isValueTouchEnabled = false; + isValueSelectionEnabled = false; + } + + protected class ChartScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + + @Override + public boolean onScale(ScaleGestureDetector detector) { + if (isZoomEnabled) { + float scale = detector.getCurrentSpan() / detector.getPreviousSpan(); + if (Float.isInfinite(scale)) { + scale = 1; + } + return chartZoomer.scale(computator, detector.getFocusX(), detector.getFocusY(), scale); + } + + return false; + } + } + + protected class PreviewChartGestureListener extends ChartGestureListener { + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + return super.onScroll(e1, e2, -distanceX, -distanceY); + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + return super.onFling(e1, e2, -velocityX, -velocityY); + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ZoomType.java b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ZoomType.java index eb21413b..c162fa8c 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ZoomType.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ZoomType.java @@ -2,5 +2,5 @@ public enum ZoomType { - HORIZONTAL, VERTICAL, HORIZONTAL_AND_VERTICAL; + HORIZONTAL, VERTICAL, HORIZONTAL_AND_VERTICAL; } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ZoomerCompat.java b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ZoomerCompat.java index 5d9f4373..fc86349b 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/gesture/ZoomerCompat.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/gesture/ZoomerCompat.java @@ -25,104 +25,104 @@ * A simple class that animates double-touch zoom gestures. Functionally similar to a {@link android.widget.Scroller}. */ public class ZoomerCompat { - private static final int DEFAULT_SHORT_ANIMATION_DURATION = 200; - /** - * The interpolator, used for making zooms animate 'naturally.' - */ - private Interpolator mInterpolator; - - /** - * The total animation duration for a zoom. - */ - private long mAnimationDurationMillis; - - /** - * Whether or not the current zoom has finished. - */ - private boolean mFinished = true; - - /** - * The current zoom value; computed by {@link #computeZoom()}. - */ - private float mCurrentZoom; - - /** - * The time the zoom started, computed using {@link android.os.SystemClock#elapsedRealtime()}. - */ - private long mStartRTC; - - /** - * The destination zoom factor. - */ - private float mEndZoom; - - public ZoomerCompat(Context context) { - mInterpolator = new DecelerateInterpolator(); - // TODO: use constant - mAnimationDurationMillis = DEFAULT_SHORT_ANIMATION_DURATION; - } - - /** - * Forces the zoom finished state to the given value. Unlike {@link #abortAnimation()}, the current zoom value isn't - * set to the ending value. - * - * @see android.widget.Scroller#forceFinished(boolean) - */ - public void forceFinished(boolean finished) { - mFinished = finished; - } - - /** - * Aborts the animation, setting the current zoom value to the ending value. - * - * @see android.widget.Scroller#abortAnimation() - */ - public void abortAnimation() { - mFinished = true; - mCurrentZoom = mEndZoom; - } - - /** - * Starts a zoom from 1.0 to (1.0 + endZoom). That is, to zoom from 100% to 125%, endZoom should by 0.25f. - * - * @see android.widget.Scroller#startScroll(int, int, int, int) - */ - public void startZoom(float endZoom) { - mStartRTC = SystemClock.elapsedRealtime(); - mEndZoom = endZoom; - - mFinished = false; - mCurrentZoom = 1f; - } - - /** - * Computes the current zoom level, returning true if the zoom is still active and false if the zoom has finished. - * - * @see android.widget.Scroller#computeScrollOffset() - */ - public boolean computeZoom() { - if (mFinished) { - return false; - } - - long tRTC = SystemClock.elapsedRealtime() - mStartRTC; - if (tRTC >= mAnimationDurationMillis) { - mFinished = true; - mCurrentZoom = mEndZoom; - return false; - } - - float t = tRTC * 1f / mAnimationDurationMillis; - mCurrentZoom = mEndZoom * mInterpolator.getInterpolation(t); - return true; - } - - /** - * Returns the current zoom level. - * - * @see android.widget.Scroller#getCurrX() - */ - public float getCurrZoom() { - return mCurrentZoom; - } + private static final int DEFAULT_SHORT_ANIMATION_DURATION = 200; + /** + * The interpolator, used for making zooms animate 'naturally.' + */ + private Interpolator mInterpolator; + + /** + * The total animation duration for a zoom. + */ + private long mAnimationDurationMillis; + + /** + * Whether or not the current zoom has finished. + */ + private boolean mFinished = true; + + /** + * The current zoom value; computed by {@link #computeZoom()}. + */ + private float mCurrentZoom; + + /** + * The time the zoom started, computed using {@link android.os.SystemClock#elapsedRealtime()}. + */ + private long mStartRTC; + + /** + * The destination zoom factor. + */ + private float mEndZoom; + + public ZoomerCompat(Context context) { + mInterpolator = new DecelerateInterpolator(); + // TODO: use constant + mAnimationDurationMillis = DEFAULT_SHORT_ANIMATION_DURATION; + } + + /** + * Forces the zoom finished state to the given value. Unlike {@link #abortAnimation()}, the current zoom value isn't + * set to the ending value. + * + * @see android.widget.Scroller#forceFinished(boolean) + */ + public void forceFinished(boolean finished) { + mFinished = finished; + } + + /** + * Aborts the animation, setting the current zoom value to the ending value. + * + * @see android.widget.Scroller#abortAnimation() + */ + public void abortAnimation() { + mFinished = true; + mCurrentZoom = mEndZoom; + } + + /** + * Starts a zoom from 1.0 to (1.0 + endZoom). That is, to zoom from 100% to 125%, endZoom should by 0.25f. + * + * @see android.widget.Scroller#startScroll(int, int, int, int) + */ + public void startZoom(float endZoom) { + mStartRTC = SystemClock.elapsedRealtime(); + mEndZoom = endZoom; + + mFinished = false; + mCurrentZoom = 1f; + } + + /** + * Computes the current zoom level, returning true if the zoom is still active and false if the zoom has finished. + * + * @see android.widget.Scroller#computeScrollOffset() + */ + public boolean computeZoom() { + if (mFinished) { + return false; + } + + long tRTC = SystemClock.elapsedRealtime() - mStartRTC; + if (tRTC >= mAnimationDurationMillis) { + mFinished = true; + mCurrentZoom = mEndZoom; + return false; + } + + float t = tRTC * 1f / mAnimationDurationMillis; + mCurrentZoom = mEndZoom * mInterpolator.getInterpolation(t); + return true; + } + + /** + * Returns the current zoom level. + * + * @see android.widget.Scroller#getCurrX() + */ + public float getCurrZoom() { + return mCurrentZoom; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/BubbleChartOnValueSelectListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/BubbleChartOnValueSelectListener.java index 7915687d..14e5d3e3 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/BubbleChartOnValueSelectListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/BubbleChartOnValueSelectListener.java @@ -3,8 +3,8 @@ import lecho.lib.hellocharts.model.BubbleValue; -public interface BubbleChartOnValueSelectListener extends OnValueDeselectListener { +public interface BubbleChartOnValueSelectListener extends OnValueDeselectListener { - public void onValueSelected(int bubbleIndex, BubbleValue value); + public void onValueSelected(int bubbleIndex, BubbleValue value); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/ColumnChartOnValueSelectListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/ColumnChartOnValueSelectListener.java index 62cf9692..cba1de80 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/ColumnChartOnValueSelectListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/ColumnChartOnValueSelectListener.java @@ -5,6 +5,6 @@ public interface ColumnChartOnValueSelectListener extends OnValueDeselectListener { - public void onValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value); + public void onValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/ComboLineColumnChartOnValueSelectListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/ComboLineColumnChartOnValueSelectListener.java index 0efea46d..a44b22a7 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/ComboLineColumnChartOnValueSelectListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/ComboLineColumnChartOnValueSelectListener.java @@ -6,8 +6,8 @@ public interface ComboLineColumnChartOnValueSelectListener extends OnValueDeselectListener { - public void onColumnValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value); + public void onColumnValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value); - public void onPointValueSelected(int lineIndex, int pointIndex, PointValue value); + public void onPointValueSelected(int lineIndex, int pointIndex, PointValue value); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyBubbleChartOnValueSelectListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyBubbleChartOnValueSelectListener.java index c7e09359..dea779f9 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyBubbleChartOnValueSelectListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyBubbleChartOnValueSelectListener.java @@ -5,13 +5,13 @@ public class DummyBubbleChartOnValueSelectListener implements BubbleChartOnValueSelectListener { - @Override - public void onValueSelected(int bubbleIndex, BubbleValue value) { + @Override + public void onValueSelected(int bubbleIndex, BubbleValue value) { - } + } - @Override - public void onValueDeselected() { + @Override + public void onValueDeselected() { - } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyColumnChartOnValueSelectListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyColumnChartOnValueSelectListener.java index f54ac5fc..3d9e1031 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyColumnChartOnValueSelectListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyColumnChartOnValueSelectListener.java @@ -5,14 +5,14 @@ public class DummyColumnChartOnValueSelectListener implements ColumnChartOnValueSelectListener { - @Override - public void onValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value) { + @Override + public void onValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value) { - } + } - @Override - public void onValueDeselected() { + @Override + public void onValueDeselected() { - } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyCompoLineColumnChartOnValueSelectListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyCompoLineColumnChartOnValueSelectListener.java index f0a50cf9..ab109c01 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyCompoLineColumnChartOnValueSelectListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyCompoLineColumnChartOnValueSelectListener.java @@ -6,18 +6,18 @@ public class DummyCompoLineColumnChartOnValueSelectListener implements ComboLineColumnChartOnValueSelectListener { - @Override - public void onColumnValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value) { + @Override + public void onColumnValueSelected(int columnIndex, int subcolumnIndex, SubcolumnValue value) { - } + } - @Override - public void onPointValueSelected(int lineIndex, int pointIndex, PointValue value) { + @Override + public void onPointValueSelected(int lineIndex, int pointIndex, PointValue value) { - } + } - @Override - public void onValueDeselected() { + @Override + public void onValueDeselected() { - } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyLineChartOnValueSelectListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyLineChartOnValueSelectListener.java index aa03a112..ad3b409c 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyLineChartOnValueSelectListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyLineChartOnValueSelectListener.java @@ -5,13 +5,13 @@ public class DummyLineChartOnValueSelectListener implements LineChartOnValueSelectListener { - @Override - public void onValueSelected(int lineIndex, int pointIndex, PointValue value) { + @Override + public void onValueSelected(int lineIndex, int pointIndex, PointValue value) { - } + } - @Override - public void onValueDeselected() { + @Override + public void onValueDeselected() { - } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyPieChartOnValueSelectListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyPieChartOnValueSelectListener.java index d3c59d69..723c6bf1 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyPieChartOnValueSelectListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyPieChartOnValueSelectListener.java @@ -5,13 +5,13 @@ public class DummyPieChartOnValueSelectListener implements PieChartOnValueSelectListener { - @Override - public void onValueSelected(int arcIndex, SliceValue value) { + @Override + public void onValueSelected(int arcIndex, SliceValue value) { - } + } - @Override - public void onValueDeselected() { + @Override + public void onValueDeselected() { - } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyVieportChangeListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyVieportChangeListener.java index 9de75bea..c8293677 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyVieportChangeListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/DummyVieportChangeListener.java @@ -4,8 +4,8 @@ public class DummyVieportChangeListener implements ViewportChangeListener { - @Override - public void onViewportChanged(Viewport viewport) { - // Do nothing - } + @Override + public void onViewportChanged(Viewport viewport) { + // Do nothing + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/LineChartOnValueSelectListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/LineChartOnValueSelectListener.java index 0eebebb6..b5781c35 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/LineChartOnValueSelectListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/LineChartOnValueSelectListener.java @@ -5,6 +5,6 @@ public interface LineChartOnValueSelectListener extends OnValueDeselectListener { - public void onValueSelected(int lineIndex, int pointIndex, PointValue value); + public void onValueSelected(int lineIndex, int pointIndex, PointValue value); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/OnValueDeselectListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/OnValueDeselectListener.java index 8c57770a..4532f238 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/OnValueDeselectListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/OnValueDeselectListener.java @@ -3,9 +3,9 @@ public interface OnValueDeselectListener { - /** - * Called only in chart selection mode when user touch empty space causing value deselection. - * Note: this method is not called when selection mode is disabled. - */ - public void onValueDeselected(); + /** + * Called only in chart selection mode when user touch empty space causing value deselection. + * Note: this method is not called when selection mode is disabled. + */ + public void onValueDeselected(); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/PieChartOnValueSelectListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/PieChartOnValueSelectListener.java index a8d2cb61..ba904589 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/PieChartOnValueSelectListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/PieChartOnValueSelectListener.java @@ -5,6 +5,6 @@ public interface PieChartOnValueSelectListener extends OnValueDeselectListener { - public void onValueSelected(int arcIndex, SliceValue value); + public void onValueSelected(int arcIndex, SliceValue value); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/listener/ViewportChangeListener.java b/hellocharts-library/src/lecho/lib/hellocharts/listener/ViewportChangeListener.java index 73f0428d..b083a317 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/listener/ViewportChangeListener.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/listener/ViewportChangeListener.java @@ -6,13 +6,12 @@ * Use implementations of this listener to be notified when chart viewport changed. For now it works only for preview * charts. To make it works for other chart types you just need to uncomment last line in * {@link lecho.lib.hellocharts.computator.ChartComputator#constrainViewport(float, float, float, float)} method. - * */ public interface ViewportChangeListener { - /** - * Called when current viewport of chart changed. You should not modify that viewport. - */ - public void onViewportChanged(Viewport viewport); + /** + * Called when current viewport of chart changed. You should not modify that viewport. + */ + public void onViewportChanged(Viewport viewport); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/AbstractChartData.java b/hellocharts-library/src/lecho/lib/hellocharts/model/AbstractChartData.java index bf089e14..94ee92f8 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/AbstractChartData.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/AbstractChartData.java @@ -1,164 +1,166 @@ package lecho.lib.hellocharts.model; -import lecho.lib.hellocharts.util.ChartUtils; import android.graphics.Color; import android.graphics.Typeface; +import lecho.lib.hellocharts.util.ChartUtils; + /** * Base class for most chart data models. - * */ public abstract class AbstractChartData implements ChartData { - public static final int DEFAULT_TEXT_SIZE_SP = 12; - protected Axis axisXBottom; - protected Axis axisYLeft; - protected Axis axisXTop; - protected Axis axisYRight; - protected int valueLabelTextColor = Color.WHITE; - protected int valueLabelTextSize = DEFAULT_TEXT_SIZE_SP; - protected Typeface valueLabelTypeface; - - /** If true each value label will have background rectangle */ - protected boolean isValueLabelBackgroundEnabled = true; - - /** - * If true and {@link #isValueLabelBackgroundEnabled} is true each label will have background rectangle and that - * rectangle will be filled with color specified for given value. - */ - protected boolean isValueLabelBackgrountAuto = true; - - /** - * If {@link #isValueLabelBackgroundEnabled} is true and {@link #isValueLabelBackgrountAuto} is false each label - * will have background rectangle and that rectangle will be filled with this color. Helpful if you want all labels - * to have the same background color. - */ - protected int valueLabelBackgroundColor = ChartUtils.darkenColor(ChartUtils.DEFAULT_DARKEN_COLOR); - - public AbstractChartData() { - - } - - /** - * Copy constructor for deep copy. - * - * @param data - */ - public AbstractChartData(AbstractChartData data) { - if (null != data.axisXBottom) { - this.axisXBottom = new Axis(data.axisXBottom); - } - if (null != data.axisXTop) { - this.axisXTop = new Axis(data.axisXTop); - } - if (null != data.axisYLeft) { - this.axisYLeft = new Axis(data.axisYLeft); - } - if (null != data.axisYRight) { - this.axisYRight = new Axis(data.axisYRight); - } - this.valueLabelTextColor = data.valueLabelTextColor; - this.valueLabelTextSize = data.valueLabelTextSize; - this.valueLabelTypeface = data.valueLabelTypeface; - } - - @Override - public void setAxisXBottom(Axis axisX) { - this.axisXBottom = axisX; - } - - @Override - public Axis getAxisXBottom() { - return axisXBottom; - } - - @Override - public void setAxisYLeft(Axis axisY) { - this.axisYLeft = axisY; - } - - @Override - public Axis getAxisYLeft() { - return axisYLeft; - } - - @Override - public void setAxisXTop(Axis axisX) { - this.axisXTop = axisX; - } - - @Override - public Axis getAxisXTop() { - return axisXTop; - } - - @Override - public void setAxisYRight(Axis axisY) { - this.axisYRight = axisY; - } - - @Override - public Axis getAxisYRight() { - return axisYRight; - } - - @Override - public int getValueLabelTextColor() { - return valueLabelTextColor; - } - - @Override - public void setValueLabelsTextColor(int valueLabelTextColor) { - this.valueLabelTextColor = valueLabelTextColor; - } - - @Override - public int getValueLabelTextSize() { - return valueLabelTextSize; - } - - @Override - public void setValueLabelTextSize(int valueLabelTextSize) { - this.valueLabelTextSize = valueLabelTextSize; - } - - @Override - public Typeface getValueLabelTypeface() { - return valueLabelTypeface; - } - - @Override - public void setValueLabelTypeface(Typeface typeface) { - this.valueLabelTypeface = typeface; - } - - @Override - public boolean isValueLabelBackgroundEnabled() { - return isValueLabelBackgroundEnabled; - } - - @Override - public void setValueLabelBackgroundEnabled(boolean isValueLabelBackgroundEnabled) { - this.isValueLabelBackgroundEnabled = isValueLabelBackgroundEnabled; - } - - @Override - public boolean isValueLabelBackgroundAuto() { - return isValueLabelBackgrountAuto; - } - - @Override - public void setValueLabelBackgroundAuto(boolean isValueLabelBackgrountAuto) { - this.isValueLabelBackgrountAuto = isValueLabelBackgrountAuto; - } - - @Override - public int getValueLabelBackgroundColor() { - return valueLabelBackgroundColor; - } - - @Override - public void setValueLabelBackgroundColor(int valueLabelBackgroundColor) { - this.valueLabelBackgroundColor = valueLabelBackgroundColor; - } + public static final int DEFAULT_TEXT_SIZE_SP = 12; + protected Axis axisXBottom; + protected Axis axisYLeft; + protected Axis axisXTop; + protected Axis axisYRight; + protected int valueLabelTextColor = Color.WHITE; + protected int valueLabelTextSize = DEFAULT_TEXT_SIZE_SP; + protected Typeface valueLabelTypeface; + + /** + * If true each value label will have background rectangle + */ + protected boolean isValueLabelBackgroundEnabled = true; + + /** + * If true and {@link #isValueLabelBackgroundEnabled} is true each label will have background rectangle and that + * rectangle will be filled with color specified for given value. + */ + protected boolean isValueLabelBackgrountAuto = true; + + /** + * If {@link #isValueLabelBackgroundEnabled} is true and {@link #isValueLabelBackgrountAuto} is false each label + * will have background rectangle and that rectangle will be filled with this color. Helpful if you want all labels + * to have the same background color. + */ + protected int valueLabelBackgroundColor = ChartUtils.darkenColor(ChartUtils.DEFAULT_DARKEN_COLOR); + + public AbstractChartData() { + + } + + /** + * Copy constructor for deep copy. + * + * @param data + */ + public AbstractChartData(AbstractChartData data) { + if (null != data.axisXBottom) { + this.axisXBottom = new Axis(data.axisXBottom); + } + if (null != data.axisXTop) { + this.axisXTop = new Axis(data.axisXTop); + } + if (null != data.axisYLeft) { + this.axisYLeft = new Axis(data.axisYLeft); + } + if (null != data.axisYRight) { + this.axisYRight = new Axis(data.axisYRight); + } + this.valueLabelTextColor = data.valueLabelTextColor; + this.valueLabelTextSize = data.valueLabelTextSize; + this.valueLabelTypeface = data.valueLabelTypeface; + } + + @Override + public void setAxisXBottom(Axis axisX) { + this.axisXBottom = axisX; + } + + @Override + public Axis getAxisXBottom() { + return axisXBottom; + } + + @Override + public void setAxisYLeft(Axis axisY) { + this.axisYLeft = axisY; + } + + @Override + public Axis getAxisYLeft() { + return axisYLeft; + } + + @Override + public void setAxisXTop(Axis axisX) { + this.axisXTop = axisX; + } + + @Override + public Axis getAxisXTop() { + return axisXTop; + } + + @Override + public void setAxisYRight(Axis axisY) { + this.axisYRight = axisY; + } + + @Override + public Axis getAxisYRight() { + return axisYRight; + } + + @Override + public int getValueLabelTextColor() { + return valueLabelTextColor; + } + + @Override + public void setValueLabelsTextColor(int valueLabelTextColor) { + this.valueLabelTextColor = valueLabelTextColor; + } + + @Override + public int getValueLabelTextSize() { + return valueLabelTextSize; + } + + @Override + public void setValueLabelTextSize(int valueLabelTextSize) { + this.valueLabelTextSize = valueLabelTextSize; + } + + @Override + public Typeface getValueLabelTypeface() { + return valueLabelTypeface; + } + + @Override + public void setValueLabelTypeface(Typeface typeface) { + this.valueLabelTypeface = typeface; + } + + @Override + public boolean isValueLabelBackgroundEnabled() { + return isValueLabelBackgroundEnabled; + } + + @Override + public void setValueLabelBackgroundEnabled(boolean isValueLabelBackgroundEnabled) { + this.isValueLabelBackgroundEnabled = isValueLabelBackgroundEnabled; + } + + @Override + public boolean isValueLabelBackgroundAuto() { + return isValueLabelBackgrountAuto; + } + + @Override + public void setValueLabelBackgroundAuto(boolean isValueLabelBackgrountAuto) { + this.isValueLabelBackgrountAuto = isValueLabelBackgrountAuto; + } + + @Override + public int getValueLabelBackgroundColor() { + return valueLabelBackgroundColor; + } + + @Override + public void setValueLabelBackgroundColor(int valueLabelBackgroundColor) { + this.valueLabelBackgroundColor = valueLabelBackgroundColor; + } } \ No newline at end of file diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/Axis.java b/hellocharts-library/src/lecho/lib/hellocharts/model/Axis.java index f57ed00e..6c35135d 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/Axis.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/Axis.java @@ -1,11 +1,11 @@ package lecho.lib.hellocharts.model; -import java.util.ArrayList; -import java.util.List; - import android.graphics.Color; import android.graphics.Typeface; +import java.util.ArrayList; +import java.util.List; + import lecho.lib.hellocharts.formatter.AxisValueFormatter; import lecho.lib.hellocharts.formatter.SimpleAxisValueFormatter; import lecho.lib.hellocharts.util.ChartUtils; @@ -19,287 +19,286 @@ * name is empty and therefore not displayed. */ public class Axis { - public static final int DEFAULT_TEXT_SIZE_SP = 12; - /** - * Text size for axis labels and name. - */ - private int textSize = DEFAULT_TEXT_SIZE_SP; - public static final int DEFAULT_MAX_AXIS_LABEL_CHARS = 3; - /** - * Maximum number of characters used for this axis. Used to determine axis dimensions. - */ - private int maxLabelChars = DEFAULT_MAX_AXIS_LABEL_CHARS; - /** - * Axis values, each value will be used to calculate its label position. - */ - private List values = new ArrayList(); - /** - * Name for this axis. - */ - private String name; - /** - * If true axis will be generated to automatically fit chart ranges. * - */ - private boolean isAutoGenerated = true; - /** - * If true renderer will draw lines(grid) for this axis. - */ - private boolean hasLines = false; - /** - * If true axis labels will be drown inside chart area. - */ - private boolean isInside = false; - /** - * Axis labels and name text color. - */ - private int textColor = Color.LTGRAY; - /** - * Axis grid lines color. - */ - private int lineColor = ChartUtils.DEFAULT_DARKEN_COLOR; - /** - * Typeface for labels and name text. - */ - private Typeface typeface; - - /** - * Formatter used to format labels. - */ - private AxisValueFormatter formatter = new SimpleAxisValueFormatter(); - - /** - * If true draws a line between the labels and the graph * - */ - private boolean hasSeparationLine = true; - - private boolean hasTiltedLabels = false; - - /** - * Creates auto-generated axis without name and with default formatter. - * - */ - public Axis() { - } - - /** - * Creates axis with given values(not auto-generated) without name and with default formatter. - */ - public Axis(List values) { - setValues(values); - } - - public Axis(Axis axis) { - this.name = axis.name; - this.isAutoGenerated = axis.isAutoGenerated; - this.hasLines = axis.hasLines; - this.isInside = axis.isInside; - this.textColor = axis.textColor; - this.lineColor = axis.lineColor; - this.textSize = axis.textSize; - this.maxLabelChars = axis.maxLabelChars; - this.typeface = axis.typeface; - this.formatter = axis.formatter; - this.hasSeparationLine = axis.hasSeparationLine; - - for (AxisValue axisValue : axis.values) { - this.values.add(new AxisValue(axisValue)); - } - } - - /** - * Generates Axis with values from start to stop inclusive. - */ - public static Axis generateAxisFromRange(float start, float stop, float step) { - - List values = new ArrayList(); - for (float value = start; value <= stop; value += step) { - AxisValue axisValue = new AxisValue(value); - values.add(axisValue); - } - - Axis axis = new Axis(values); - return axis; - } - - /** - * Generates Axis with values from given list. - */ - public static Axis generateAxisFromCollection(List axisValues) { - List values = new ArrayList(); - int index = 0; - for (float value : axisValues) { - AxisValue axisValue = new AxisValue(value); - values.add(axisValue); - ++index; - } - - Axis axis = new Axis(values); - return axis; - } - - /** - * Generates Axis with values and labels from given lists, both lists must have the same size. - */ - public static Axis generateAxisFromCollection(List axisValues, List axisValuesLabels) { - if (axisValues.size() != axisValuesLabels.size()) { - throw new IllegalArgumentException("Values and labels lists must have the same size!"); - } - - List values = new ArrayList(); - int index = 0; - for (float value : axisValues) { - AxisValue axisValue = new AxisValue(value).setLabel(axisValuesLabels.get(index)); - values.add(axisValue); - ++index; - } - - Axis axis = new Axis(values); - return axis; - } - - public List getValues() { - return values; - } - - public Axis setValues(List values) { - if (null == values) { - this.values = new ArrayList(); - } else { - this.values = values; - } - - this.isAutoGenerated = false; - return this; - } - - public String getName() { - return name; - } - - public Axis setName(String name) { - this.name = name; - return this; - } - - public boolean isAutoGenerated() { - return isAutoGenerated; - } - - public Axis setAutoGenerated(boolean isAutoGenerated) { - this.isAutoGenerated = isAutoGenerated; - return this; - } - - public boolean hasLines() { - return hasLines; - } - - public Axis setHasLines(boolean hasLines) { - this.hasLines = hasLines; - return this; - } - - public int getTextColor() { - return textColor; - } - - public Axis setTextColor(int color) { - this.textColor = color; - return this; - } - - /** - * @see #setInside(boolean) - */ - public boolean isInside() { - return isInside; - } - - /** - * Set to true if you want axis values to be drawn inside chart area(axis name still will be drawn outside), by - * default this is set to false and axis is drawn outside chart area. - */ - public Axis setInside(boolean isInside) { - this.isInside = isInside; - return this; - } - - public int getLineColor() { - return lineColor; - } - - public Axis setLineColor(int lineColor) { - this.lineColor = lineColor; - return this; - } - - public int getTextSize() { - return textSize; - } - - public Axis setTextSize(int textSize) { - this.textSize = textSize; - return this; - } - - public int getMaxLabelChars() { - return maxLabelChars; - } - - /** - * Set maximum number of characters for axis labels, min 0, max 32. - */ - public Axis setMaxLabelChars(int maxLabelChars) { - if (maxLabelChars < 0) { - maxLabelChars = 0; - } else if (maxLabelChars > 32) { - maxLabelChars = 32; - } - this.maxLabelChars = maxLabelChars; - return this; - } - - public Typeface getTypeface() { - return typeface; - } - - public Axis setTypeface(Typeface typeface) { - this.typeface = typeface; - return this; - } - - public AxisValueFormatter getFormatter() { - return formatter; - } - - public Axis setFormatter(AxisValueFormatter formatter) { - if (null == formatter) { - this.formatter = new SimpleAxisValueFormatter(); - } else { - this.formatter = formatter; - } - return this; - } - - /** - * Set true if you want to draw separation line for this axis, set false to hide separation line, by default true. - */ - public Axis setHasSeparationLine(boolean hasSeparationLine) { - this.hasSeparationLine = hasSeparationLine; - return this; - } - - public boolean hasSeparationLine() { - return hasSeparationLine; - } - - public boolean hasTiltedLabels() { - return hasTiltedLabels; - } - - public Axis setHasTiltedLabels(boolean hasTiltedLabels) { - this.hasTiltedLabels = hasTiltedLabels; - return this; - } + public static final int DEFAULT_TEXT_SIZE_SP = 12; + /** + * Text size for axis labels and name. + */ + private int textSize = DEFAULT_TEXT_SIZE_SP; + public static final int DEFAULT_MAX_AXIS_LABEL_CHARS = 3; + /** + * Maximum number of characters used for this axis. Used to determine axis dimensions. + */ + private int maxLabelChars = DEFAULT_MAX_AXIS_LABEL_CHARS; + /** + * Axis values, each value will be used to calculate its label position. + */ + private List values = new ArrayList(); + /** + * Name for this axis. + */ + private String name; + /** + * If true axis will be generated to automatically fit chart ranges. * + */ + private boolean isAutoGenerated = true; + /** + * If true renderer will draw lines(grid) for this axis. + */ + private boolean hasLines = false; + /** + * If true axis labels will be drown inside chart area. + */ + private boolean isInside = false; + /** + * Axis labels and name text color. + */ + private int textColor = Color.LTGRAY; + /** + * Axis grid lines color. + */ + private int lineColor = ChartUtils.DEFAULT_DARKEN_COLOR; + /** + * Typeface for labels and name text. + */ + private Typeface typeface; + + /** + * Formatter used to format labels. + */ + private AxisValueFormatter formatter = new SimpleAxisValueFormatter(); + + /** + * If true draws a line between the labels and the graph * + */ + private boolean hasSeparationLine = true; + + private boolean hasTiltedLabels = false; + + /** + * Creates auto-generated axis without name and with default formatter. + */ + public Axis() { + } + + /** + * Creates axis with given values(not auto-generated) without name and with default formatter. + */ + public Axis(List values) { + setValues(values); + } + + public Axis(Axis axis) { + this.name = axis.name; + this.isAutoGenerated = axis.isAutoGenerated; + this.hasLines = axis.hasLines; + this.isInside = axis.isInside; + this.textColor = axis.textColor; + this.lineColor = axis.lineColor; + this.textSize = axis.textSize; + this.maxLabelChars = axis.maxLabelChars; + this.typeface = axis.typeface; + this.formatter = axis.formatter; + this.hasSeparationLine = axis.hasSeparationLine; + + for (AxisValue axisValue : axis.values) { + this.values.add(new AxisValue(axisValue)); + } + } + + /** + * Generates Axis with values from start to stop inclusive. + */ + public static Axis generateAxisFromRange(float start, float stop, float step) { + + List values = new ArrayList(); + for (float value = start; value <= stop; value += step) { + AxisValue axisValue = new AxisValue(value); + values.add(axisValue); + } + + Axis axis = new Axis(values); + return axis; + } + + /** + * Generates Axis with values from given list. + */ + public static Axis generateAxisFromCollection(List axisValues) { + List values = new ArrayList(); + int index = 0; + for (float value : axisValues) { + AxisValue axisValue = new AxisValue(value); + values.add(axisValue); + ++index; + } + + Axis axis = new Axis(values); + return axis; + } + + /** + * Generates Axis with values and labels from given lists, both lists must have the same size. + */ + public static Axis generateAxisFromCollection(List axisValues, List axisValuesLabels) { + if (axisValues.size() != axisValuesLabels.size()) { + throw new IllegalArgumentException("Values and labels lists must have the same size!"); + } + + List values = new ArrayList(); + int index = 0; + for (float value : axisValues) { + AxisValue axisValue = new AxisValue(value).setLabel(axisValuesLabels.get(index)); + values.add(axisValue); + ++index; + } + + Axis axis = new Axis(values); + return axis; + } + + public List getValues() { + return values; + } + + public Axis setValues(List values) { + if (null == values) { + this.values = new ArrayList(); + } else { + this.values = values; + } + + this.isAutoGenerated = false; + return this; + } + + public String getName() { + return name; + } + + public Axis setName(String name) { + this.name = name; + return this; + } + + public boolean isAutoGenerated() { + return isAutoGenerated; + } + + public Axis setAutoGenerated(boolean isAutoGenerated) { + this.isAutoGenerated = isAutoGenerated; + return this; + } + + public boolean hasLines() { + return hasLines; + } + + public Axis setHasLines(boolean hasLines) { + this.hasLines = hasLines; + return this; + } + + public int getTextColor() { + return textColor; + } + + public Axis setTextColor(int color) { + this.textColor = color; + return this; + } + + /** + * @see #setInside(boolean) + */ + public boolean isInside() { + return isInside; + } + + /** + * Set to true if you want axis values to be drawn inside chart area(axis name still will be drawn outside), by + * default this is set to false and axis is drawn outside chart area. + */ + public Axis setInside(boolean isInside) { + this.isInside = isInside; + return this; + } + + public int getLineColor() { + return lineColor; + } + + public Axis setLineColor(int lineColor) { + this.lineColor = lineColor; + return this; + } + + public int getTextSize() { + return textSize; + } + + public Axis setTextSize(int textSize) { + this.textSize = textSize; + return this; + } + + public int getMaxLabelChars() { + return maxLabelChars; + } + + /** + * Set maximum number of characters for axis labels, min 0, max 32. + */ + public Axis setMaxLabelChars(int maxLabelChars) { + if (maxLabelChars < 0) { + maxLabelChars = 0; + } else if (maxLabelChars > 32) { + maxLabelChars = 32; + } + this.maxLabelChars = maxLabelChars; + return this; + } + + public Typeface getTypeface() { + return typeface; + } + + public Axis setTypeface(Typeface typeface) { + this.typeface = typeface; + return this; + } + + public AxisValueFormatter getFormatter() { + return formatter; + } + + public Axis setFormatter(AxisValueFormatter formatter) { + if (null == formatter) { + this.formatter = new SimpleAxisValueFormatter(); + } else { + this.formatter = formatter; + } + return this; + } + + /** + * Set true if you want to draw separation line for this axis, set false to hide separation line, by default true. + */ + public Axis setHasSeparationLine(boolean hasSeparationLine) { + this.hasSeparationLine = hasSeparationLine; + return this; + } + + public boolean hasSeparationLine() { + return hasSeparationLine; + } + + public boolean hasTiltedLabels() { + return hasTiltedLabels; + } + + public Axis setHasTiltedLabels(boolean hasTiltedLabels) { + this.hasTiltedLabels = hasTiltedLabels; + return this; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/AxisValue.java b/hellocharts-library/src/lecho/lib/hellocharts/model/AxisValue.java index 171d927b..7b099c55 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/AxisValue.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/AxisValue.java @@ -5,65 +5,64 @@ /** * Single axis value, use it to manually set axis labels position. You can use label attribute to display text instead * of number but value formatter implementation have to handle it. - * */ public class AxisValue { - private float value; - private char[] label; - - public AxisValue(float value) { - setValue(value); - } - - @Deprecated - public AxisValue(float value, char[] label) { - this.value = value; - this.label = label; - } - - public AxisValue(AxisValue axisValue) { - this.value = axisValue.value; - this.label = axisValue.label; - } - - public float getValue() { - return value; - } - - public AxisValue setValue(float value) { - this.value = value; - return this; - } - - @Deprecated - public char[] getLabel() { - return label; - } - - public char[] getLabelAsChars(){ - return label; - } - - /** - * Set custom label for this axis value. - * - * @param label - */ - @Deprecated - public AxisValue setLabel(char[] label) { - this.label = label; - return this; - } - - /** - * Set custom label for this axis value. - * - * @param label - */ - public AxisValue setLabel(String label) { - this.label = label.toCharArray(); - return this; - } + private float value; + private char[] label; + + public AxisValue(float value) { + setValue(value); + } + + @Deprecated + public AxisValue(float value, char[] label) { + this.value = value; + this.label = label; + } + + public AxisValue(AxisValue axisValue) { + this.value = axisValue.value; + this.label = axisValue.label; + } + + public float getValue() { + return value; + } + + public AxisValue setValue(float value) { + this.value = value; + return this; + } + + @Deprecated + public char[] getLabel() { + return label; + } + + public char[] getLabelAsChars() { + return label; + } + + /** + * Set custom label for this axis value. + * + * @param label + */ + @Deprecated + public AxisValue setLabel(char[] label) { + this.label = label; + return this; + } + + /** + * Set custom label for this axis value. + * + * @param label + */ + public AxisValue setLabel(String label) { + this.label = label.toCharArray(); + return this; + } @Override public boolean equals(Object o) { diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/BubbleChartData.java b/hellocharts-library/src/lecho/lib/hellocharts/model/BubbleChartData.java index e5140d9d..3182e1d9 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/BubbleChartData.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/BubbleChartData.java @@ -11,152 +11,152 @@ * Data for BubbleChart. */ public class BubbleChartData extends AbstractChartData { - public static final int DEFAULT_MIN_BUBBLE_RADIUS_DP = 6; - public static final float DEFAULT_BUBBLE_SCALE = 1f; - private BubbleChartValueFormatter formatter = new SimpleBubbleChartValueFormatter(); - private boolean hasLabels = false; - private boolean hasLabelsOnlyForSelected = false; - private int minBubbleRadius = DEFAULT_MIN_BUBBLE_RADIUS_DP; - private float bubbleScale = DEFAULT_BUBBLE_SCALE; - // TODO: consider Collections.emptyList() - private List values = new ArrayList(); - - public BubbleChartData() { - } - - public BubbleChartData(List values) { - setValues(values); - } - - /** - * Copy constructor for deep copy. - */ - public BubbleChartData(BubbleChartData data) { - super(data); - this.formatter = data.formatter; - this.hasLabels = data.hasLabels; - this.hasLabelsOnlyForSelected = data.hasLabelsOnlyForSelected; - this.minBubbleRadius = data.minBubbleRadius; - this.bubbleScale = data.bubbleScale; - - for (BubbleValue bubbleValue : data.getValues()) { - this.values.add(new BubbleValue(bubbleValue)); - } - } - - @Override - public void update(float scale) { - for (BubbleValue value : values) { - value.update(scale); - } - } - - @Override - public void finish() { - for (BubbleValue value : values) { - value.finish(); - } - } - - public List getValues() { - return values; - } - - public BubbleChartData setValues(List values) { - if (null == values) { - this.values = new ArrayList(); - } else { - this.values = values; - } - return this; - } - - public boolean hasLabels() { - return hasLabels; - } - - public BubbleChartData setHasLabels(boolean hasLabels) { - this.hasLabels = hasLabels; - if (hasLabels) { - hasLabelsOnlyForSelected = false; - } - return this; - } - - /** - * @see #setHasLabelsOnlyForSelected(boolean) - */ - public boolean hasLabelsOnlyForSelected() { - return hasLabelsOnlyForSelected; - } - - /** - * Set true if you want to show value labels only for selected value, works best when chart has - * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}. - */ - public BubbleChartData setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) { - this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected; - if (hasLabelsOnlyForSelected) { - this.hasLabels = false; - } - return this; - } - - /** - * Returns minimal bubble radius in dp. - * - * @see #setMinBubbleRadius(int) - */ - public int getMinBubbleRadius() { - return minBubbleRadius; - } - - /** - * Set minimal bubble radius in dp, helpful when you want small bubbles(bubbles with very small z values compared to - * other bubbles) to be visible on chart, default 6dp - */ - public void setMinBubbleRadius(int minBubbleRadius) { - this.minBubbleRadius = minBubbleRadius; - } - - /** - * Returns bubble scale which is used to adjust bubble size. - * - * @see #setBubbleScale(float) - */ - public float getBubbleScale() { - return bubbleScale; - } - - /** - * Set bubble scale which is used to adjust bubble size. If you want smaller bubbles set scale {@code <0, 1>}, - * if you want bigger bubbles set scale greater than 1, default is 1.0f. - */ - public void setBubbleScale(float bubbleScale) { - this.bubbleScale = bubbleScale; - } - - public BubbleChartValueFormatter getFormatter() { - return formatter; - } - - public BubbleChartData setFormatter(BubbleChartValueFormatter formatter) { - if (null != formatter) { - this.formatter = formatter; - } - return this; - } - - public static BubbleChartData generateDummyData() { - final int numValues = 4; - BubbleChartData data = new BubbleChartData(); - List values = new ArrayList(numValues); - values.add(new BubbleValue(0, 20, 15000)); - values.add(new BubbleValue(3, 22, 20000)); - values.add(new BubbleValue(5, 25, 5000)); - values.add(new BubbleValue(7, 30, 30000)); - values.add(new BubbleValue(11, 22, 10)); - data.setValues(values); - return data; - } + public static final int DEFAULT_MIN_BUBBLE_RADIUS_DP = 6; + public static final float DEFAULT_BUBBLE_SCALE = 1f; + private BubbleChartValueFormatter formatter = new SimpleBubbleChartValueFormatter(); + private boolean hasLabels = false; + private boolean hasLabelsOnlyForSelected = false; + private int minBubbleRadius = DEFAULT_MIN_BUBBLE_RADIUS_DP; + private float bubbleScale = DEFAULT_BUBBLE_SCALE; + // TODO: consider Collections.emptyList() + private List values = new ArrayList(); + + public BubbleChartData() { + } + + public BubbleChartData(List values) { + setValues(values); + } + + /** + * Copy constructor for deep copy. + */ + public BubbleChartData(BubbleChartData data) { + super(data); + this.formatter = data.formatter; + this.hasLabels = data.hasLabels; + this.hasLabelsOnlyForSelected = data.hasLabelsOnlyForSelected; + this.minBubbleRadius = data.minBubbleRadius; + this.bubbleScale = data.bubbleScale; + + for (BubbleValue bubbleValue : data.getValues()) { + this.values.add(new BubbleValue(bubbleValue)); + } + } + + @Override + public void update(float scale) { + for (BubbleValue value : values) { + value.update(scale); + } + } + + @Override + public void finish() { + for (BubbleValue value : values) { + value.finish(); + } + } + + public List getValues() { + return values; + } + + public BubbleChartData setValues(List values) { + if (null == values) { + this.values = new ArrayList(); + } else { + this.values = values; + } + return this; + } + + public boolean hasLabels() { + return hasLabels; + } + + public BubbleChartData setHasLabels(boolean hasLabels) { + this.hasLabels = hasLabels; + if (hasLabels) { + hasLabelsOnlyForSelected = false; + } + return this; + } + + /** + * @see #setHasLabelsOnlyForSelected(boolean) + */ + public boolean hasLabelsOnlyForSelected() { + return hasLabelsOnlyForSelected; + } + + /** + * Set true if you want to show value labels only for selected value, works best when chart has + * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}. + */ + public BubbleChartData setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) { + this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected; + if (hasLabelsOnlyForSelected) { + this.hasLabels = false; + } + return this; + } + + /** + * Returns minimal bubble radius in dp. + * + * @see #setMinBubbleRadius(int) + */ + public int getMinBubbleRadius() { + return minBubbleRadius; + } + + /** + * Set minimal bubble radius in dp, helpful when you want small bubbles(bubbles with very small z values compared to + * other bubbles) to be visible on chart, default 6dp + */ + public void setMinBubbleRadius(int minBubbleRadius) { + this.minBubbleRadius = minBubbleRadius; + } + + /** + * Returns bubble scale which is used to adjust bubble size. + * + * @see #setBubbleScale(float) + */ + public float getBubbleScale() { + return bubbleScale; + } + + /** + * Set bubble scale which is used to adjust bubble size. If you want smaller bubbles set scale {@code <0, 1>}, + * if you want bigger bubbles set scale greater than 1, default is 1.0f. + */ + public void setBubbleScale(float bubbleScale) { + this.bubbleScale = bubbleScale; + } + + public BubbleChartValueFormatter getFormatter() { + return formatter; + } + + public BubbleChartData setFormatter(BubbleChartValueFormatter formatter) { + if (null != formatter) { + this.formatter = formatter; + } + return this; + } + + public static BubbleChartData generateDummyData() { + final int numValues = 4; + BubbleChartData data = new BubbleChartData(); + List values = new ArrayList(numValues); + values.add(new BubbleValue(0, 20, 15000)); + values.add(new BubbleValue(3, 22, 20000)); + values.add(new BubbleValue(5, 25, 5000)); + values.add(new BubbleValue(7, 30, 30000)); + values.add(new BubbleValue(11, 22, 10)); + data.setValues(values); + return data; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/BubbleValue.java b/hellocharts-library/src/lecho/lib/hellocharts/model/BubbleValue.java index 54ddd01c..fe38ebb9 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/BubbleValue.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/BubbleValue.java @@ -7,150 +7,166 @@ /** * Single value drawn as bubble on BubbleChart. - * */ public class BubbleValue { - /** Current X value. */ - private float x; - /** Current Y value. */ - private float y; - /** Current Z value , third bubble value interpreted as bubble area. */ - private float z; - - /** Origin X value, used during value animation. */ - private float originX; - /** Origin Y value, used during value animation. */ - private float originY; - /** Origin Z value, used during value animation. */ - private float originZ; - - /** Difference between originX value and target X value. */ - private float diffX; - - /** Difference between originX value and target X value. */ - private float diffY; - - /** Difference between originX value and target X value. */ - private float diffZ; - private int color = ChartUtils.DEFAULT_COLOR; - private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR; - private ValueShape shape = ValueShape.CIRCLE; - private char[] label; - - public BubbleValue() { - set(0, 0, 0); - } - - public BubbleValue(float x, float y, float z) { - set(x, y, z); - } - - public BubbleValue(float x, float y, float z, int color) { - set(x, y, z); - setColor(color); - } - - public BubbleValue(BubbleValue bubbleValue) { - set(bubbleValue.x, bubbleValue.y, bubbleValue.z); - setColor(bubbleValue.color); - this.label = bubbleValue.label; - } - - public void update(float scale) { - x = originX + diffX * scale; - y = originY + diffY * scale; - z = originZ + diffZ * scale; - } - - public void finish() { - set(originX + diffX, originY + diffY, originZ + diffZ); - } - - public BubbleValue set(float x, float y, float z) { - this.x = x; - this.y = y; - this.z = z; - this.originX = x; - this.originY = y; - this.originZ = z; - this.diffX = 0; - this.diffY = 0; - this.diffZ = 0; - return this; - } - - /** - * Set target values that should be reached when data animation finish then call {@link Chart#startDataAnimation()} - * - */ - public BubbleValue setTarget(float targetX, float targetY, float targetZ) { - set(x, y, z); - this.diffX = targetX - originX; - this.diffY = targetY - originY; - this.diffZ = targetZ - originZ; - return this; - } - - public float getX() { - return this.x; - } - - public float getY() { - return this.y; - } - - public float getZ() { - return this.z; - } - - public int getColor() { - return color; - } - - public BubbleValue setColor(int color) { - this.color = color; - this.darkenColor = ChartUtils.darkenColor(color); - return this; - } - - public int getDarkenColor() { - return darkenColor; - } - - public ValueShape getShape() { - return shape; - } - - public BubbleValue setShape(ValueShape shape) { - this.shape = shape; - return this; - } - - @Deprecated - public char[] getLabel() { - return label; - } - - public char[] getLabelAsChars() { - return label; - } - - @Deprecated - public BubbleValue setLabel(char[] label) { - this.label = label; - return this; - } - - public BubbleValue setLabel(String label) { - this.label = label.toCharArray(); - return this; - } - - @Override - public String toString() { - return "BubbleValue [x=" + x + ", y=" + y + ", z=" + z + "]"; - } + /** + * Current X value. + */ + private float x; + /** + * Current Y value. + */ + private float y; + /** + * Current Z value , third bubble value interpreted as bubble area. + */ + private float z; + + /** + * Origin X value, used during value animation. + */ + private float originX; + /** + * Origin Y value, used during value animation. + */ + private float originY; + /** + * Origin Z value, used during value animation. + */ + private float originZ; + + /** + * Difference between originX value and target X value. + */ + private float diffX; + + /** + * Difference between originX value and target X value. + */ + private float diffY; + + /** + * Difference between originX value and target X value. + */ + private float diffZ; + private int color = ChartUtils.DEFAULT_COLOR; + private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR; + private ValueShape shape = ValueShape.CIRCLE; + private char[] label; + + public BubbleValue() { + set(0, 0, 0); + } + + public BubbleValue(float x, float y, float z) { + set(x, y, z); + } + + public BubbleValue(float x, float y, float z, int color) { + set(x, y, z); + setColor(color); + } + + public BubbleValue(BubbleValue bubbleValue) { + set(bubbleValue.x, bubbleValue.y, bubbleValue.z); + setColor(bubbleValue.color); + this.label = bubbleValue.label; + } + + public void update(float scale) { + x = originX + diffX * scale; + y = originY + diffY * scale; + z = originZ + diffZ * scale; + } + + public void finish() { + set(originX + diffX, originY + diffY, originZ + diffZ); + } + + public BubbleValue set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + this.originX = x; + this.originY = y; + this.originZ = z; + this.diffX = 0; + this.diffY = 0; + this.diffZ = 0; + return this; + } + + /** + * Set target values that should be reached when data animation finish then call {@link Chart#startDataAnimation()} + */ + public BubbleValue setTarget(float targetX, float targetY, float targetZ) { + set(x, y, z); + this.diffX = targetX - originX; + this.diffY = targetY - originY; + this.diffZ = targetZ - originZ; + return this; + } + + public float getX() { + return this.x; + } + + public float getY() { + return this.y; + } + + public float getZ() { + return this.z; + } + + public int getColor() { + return color; + } + + public BubbleValue setColor(int color) { + this.color = color; + this.darkenColor = ChartUtils.darkenColor(color); + return this; + } + + public int getDarkenColor() { + return darkenColor; + } + + public ValueShape getShape() { + return shape; + } + + public BubbleValue setShape(ValueShape shape) { + this.shape = shape; + return this; + } + + @Deprecated + public char[] getLabel() { + return label; + } + + public char[] getLabelAsChars() { + return label; + } + + @Deprecated + public BubbleValue setLabel(char[] label) { + this.label = label; + return this; + } + + public BubbleValue setLabel(String label) { + this.label = label.toCharArray(); + return this; + } + + @Override + public String toString() { + return "BubbleValue [x=" + x + ", y=" + y + ", z=" + z + "]"; + } @Override public boolean equals(Object o) { diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/ChartData.java b/hellocharts-library/src/lecho/lib/hellocharts/model/ChartData.java index f57c2e37..1a199eb4 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/ChartData.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/ChartData.java @@ -4,133 +4,131 @@ /** * Base interface for all chart data models. - * */ public interface ChartData { - /** - * Updates data by scale during animation. - * - * @param scale - * value from 0 to 1.0 - */ - public void update(float scale); - - /** - * Inform data that animation finished(data should be update with scale 1.0f). - */ - public void finish(); - - /** - * Set horizontal axis at the bottom of the chart. Pass null to remove that axis. - * - * @param axisX - */ - public void setAxisXBottom(Axis axisX); - - /** - * @see #setAxisXBottom(Axis) - */ - public Axis getAxisXBottom(); - - /** - * Set vertical axis on the left of the chart. Pass null to remove that axis. - * - * @param axisY - */ - public void setAxisYLeft(Axis axisY); - - /** - * @see #setAxisYLeft(Axis) - */ - public Axis getAxisYLeft(); - - /** - * Set horizontal axis at the top of the chart. Pass null to remove that axis. - * - * @param axisX - */ - public void setAxisXTop(Axis axisX); - - /** - * @see #setAxisXTop(Axis) - */ - public Axis getAxisXTop(); - - /** - * Set vertical axis on the right of the chart. Pass null to remove that axis. - * - * @param axisY - */ - public void setAxisYRight(Axis axisY); - - /** - * @see #setAxisYRight(Axis) - */ - public Axis getAxisYRight(); - - /** - * Returns color used to draw value label text. - */ - public int getValueLabelTextColor(); - - /** - * Set value label text color, by default Color.WHITE. - */ - public void setValueLabelsTextColor(int labelsTextColor); - - /** - * Returns text size for value label in SP units. - */ - public int getValueLabelTextSize(); - - /** - * Set text size for value label in SP units. - */ - public void setValueLabelTextSize(int labelsTextSize); - - /** - * Returns Typeface for value labels. - * - * @return Typeface or null if Typeface is not set. - */ - public Typeface getValueLabelTypeface(); - - /** - * Set Typeface for all values labels. - * - * @param typeface - */ - public void setValueLabelTypeface(Typeface typeface); - - /** - * @see #setValueLabelBackgroundEnabled(boolean) - */ - public boolean isValueLabelBackgroundEnabled(); - - /** - * Set whether labels should have rectangle background. Default is true. - */ - public void setValueLabelBackgroundEnabled(boolean isValueLabelBackgroundEnabled); - - /** - * @see #setValueLabelBackgroundAuto(boolean) - */ - public boolean isValueLabelBackgroundAuto(); - - /** - * Set false if you want to set custom color for all value labels. Default is true. - */ - public void setValueLabelBackgroundAuto(boolean isValueLabelBackgrountAuto); - - /** - * @see #setValueLabelBackgroundColor(int) - */ - public int getValueLabelBackgroundColor(); - - /** - * Set value labels background. This value is used only if isValueLabelBackgroundAuto returns false. Default is - * green. - */ - public void setValueLabelBackgroundColor(int valueLabelBackgroundColor); + /** + * Updates data by scale during animation. + * + * @param scale value from 0 to 1.0 + */ + public void update(float scale); + + /** + * Inform data that animation finished(data should be update with scale 1.0f). + */ + public void finish(); + + /** + * Set horizontal axis at the bottom of the chart. Pass null to remove that axis. + * + * @param axisX + */ + public void setAxisXBottom(Axis axisX); + + /** + * @see #setAxisXBottom(Axis) + */ + public Axis getAxisXBottom(); + + /** + * Set vertical axis on the left of the chart. Pass null to remove that axis. + * + * @param axisY + */ + public void setAxisYLeft(Axis axisY); + + /** + * @see #setAxisYLeft(Axis) + */ + public Axis getAxisYLeft(); + + /** + * Set horizontal axis at the top of the chart. Pass null to remove that axis. + * + * @param axisX + */ + public void setAxisXTop(Axis axisX); + + /** + * @see #setAxisXTop(Axis) + */ + public Axis getAxisXTop(); + + /** + * Set vertical axis on the right of the chart. Pass null to remove that axis. + * + * @param axisY + */ + public void setAxisYRight(Axis axisY); + + /** + * @see #setAxisYRight(Axis) + */ + public Axis getAxisYRight(); + + /** + * Returns color used to draw value label text. + */ + public int getValueLabelTextColor(); + + /** + * Set value label text color, by default Color.WHITE. + */ + public void setValueLabelsTextColor(int labelsTextColor); + + /** + * Returns text size for value label in SP units. + */ + public int getValueLabelTextSize(); + + /** + * Set text size for value label in SP units. + */ + public void setValueLabelTextSize(int labelsTextSize); + + /** + * Returns Typeface for value labels. + * + * @return Typeface or null if Typeface is not set. + */ + public Typeface getValueLabelTypeface(); + + /** + * Set Typeface for all values labels. + * + * @param typeface + */ + public void setValueLabelTypeface(Typeface typeface); + + /** + * @see #setValueLabelBackgroundEnabled(boolean) + */ + public boolean isValueLabelBackgroundEnabled(); + + /** + * Set whether labels should have rectangle background. Default is true. + */ + public void setValueLabelBackgroundEnabled(boolean isValueLabelBackgroundEnabled); + + /** + * @see #setValueLabelBackgroundAuto(boolean) + */ + public boolean isValueLabelBackgroundAuto(); + + /** + * Set false if you want to set custom color for all value labels. Default is true. + */ + public void setValueLabelBackgroundAuto(boolean isValueLabelBackgrountAuto); + + /** + * @see #setValueLabelBackgroundColor(int) + */ + public int getValueLabelBackgroundColor(); + + /** + * Set value labels background. This value is used only if isValueLabelBackgroundAuto returns false. Default is + * green. + */ + public void setValueLabelBackgroundColor(int valueLabelBackgroundColor); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/Column.java b/hellocharts-library/src/lecho/lib/hellocharts/model/Column.java index 43fb9dee..04ade263 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/Column.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/Column.java @@ -15,95 +15,95 @@ * If you want to display AxisValue for given column you should initialize AxisValue with X value of that column. */ public class Column { - private boolean hasLabels = false; - private boolean hasLabelsOnlyForSelected = false; - private ColumnChartValueFormatter formatter = new SimpleColumnChartValueFormatter(); - // TODO: consider Collections.emptyList() - private List values = new ArrayList(); - - public Column() { - - } - - public Column(List values) { - setValues(values); - } - - public Column(Column column) { - this.hasLabels = column.hasLabels; - this.hasLabelsOnlyForSelected = column.hasLabelsOnlyForSelected; - this.formatter = column.formatter; - - for (SubcolumnValue columnValue : column.values) { - this.values.add(new SubcolumnValue(columnValue)); - } - } - - public void update(float scale) { - for (SubcolumnValue value : values) { - value.update(scale); - } - - } - - public void finish() { - for (SubcolumnValue value : values) { - value.finish(); - } - } - - public List getValues() { - return values; - } - - public Column setValues(List values) { - if (null == values) { - this.values = new ArrayList(); - } else { - this.values = values; - } - return this; - } - - public boolean hasLabels() { - return hasLabels; - } - - public Column setHasLabels(boolean hasLabels) { - this.hasLabels = hasLabels; - if (hasLabels) { - this.hasLabelsOnlyForSelected = false; - } - return this; - } - - /** - * @see #setHasLabelsOnlyForSelected(boolean) - */ - public boolean hasLabelsOnlyForSelected() { - return hasLabelsOnlyForSelected; - } - - /** - * Set true if you want to show value labels only for selected value, works best when chart has - * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}. - */ - public Column setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) { - this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected; - if (hasLabelsOnlyForSelected) { - this.hasLabels = false; - } - return this; - } - - public ColumnChartValueFormatter getFormatter() { - return formatter; - } - - public Column setFormatter(ColumnChartValueFormatter formatter) { - if (null != formatter) { - this.formatter = formatter; - } - return this; - } + private boolean hasLabels = false; + private boolean hasLabelsOnlyForSelected = false; + private ColumnChartValueFormatter formatter = new SimpleColumnChartValueFormatter(); + // TODO: consider Collections.emptyList() + private List values = new ArrayList(); + + public Column() { + + } + + public Column(List values) { + setValues(values); + } + + public Column(Column column) { + this.hasLabels = column.hasLabels; + this.hasLabelsOnlyForSelected = column.hasLabelsOnlyForSelected; + this.formatter = column.formatter; + + for (SubcolumnValue columnValue : column.values) { + this.values.add(new SubcolumnValue(columnValue)); + } + } + + public void update(float scale) { + for (SubcolumnValue value : values) { + value.update(scale); + } + + } + + public void finish() { + for (SubcolumnValue value : values) { + value.finish(); + } + } + + public List getValues() { + return values; + } + + public Column setValues(List values) { + if (null == values) { + this.values = new ArrayList(); + } else { + this.values = values; + } + return this; + } + + public boolean hasLabels() { + return hasLabels; + } + + public Column setHasLabels(boolean hasLabels) { + this.hasLabels = hasLabels; + if (hasLabels) { + this.hasLabelsOnlyForSelected = false; + } + return this; + } + + /** + * @see #setHasLabelsOnlyForSelected(boolean) + */ + public boolean hasLabelsOnlyForSelected() { + return hasLabelsOnlyForSelected; + } + + /** + * Set true if you want to show value labels only for selected value, works best when chart has + * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}. + */ + public Column setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) { + this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected; + if (hasLabelsOnlyForSelected) { + this.hasLabels = false; + } + return this; + } + + public ColumnChartValueFormatter getFormatter() { + return formatter; + } + + public Column setFormatter(ColumnChartValueFormatter formatter) { + if (null != formatter) { + this.formatter = formatter; + } + return this; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/ColumnChartData.java b/hellocharts-library/src/lecho/lib/hellocharts/model/ColumnChartData.java index 60854629..284978a6 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/ColumnChartData.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/ColumnChartData.java @@ -5,135 +5,134 @@ /** * Data model for column chart. - * + *

* Note: you can set X value for columns or sub-columns, columns are by default indexed from 0 to numOfColumns-1 and * column index is used as column X value, so first column has X value 0, second clumn has X value 1 etc. * If you want to display AxisValue for given column you should initialize AxisValue with X value of that column. - * */ public class ColumnChartData extends AbstractChartData { - public static final float DEFAULT_FILL_RATIO = 0.75f; - public static final float DEFAULT_BASE_VALUE = 0.0f; - private List columns = new ArrayList(); - private boolean isStacked = false; - private float fillRatio = DEFAULT_FILL_RATIO; - private float baseValue = DEFAULT_BASE_VALUE; - - public ColumnChartData() { - } - - public ColumnChartData(List columns) { - setColumns(columns); - } - - /** - * Copy constructor for deep copy. - */ - public ColumnChartData(ColumnChartData data) { - super(data); - this.isStacked = data.isStacked; - this.fillRatio = data.fillRatio; - - for (Column column : data.columns) { - this.columns.add(new Column(column)); - } - } - - @Override - public void update(float scale) { - for (Column column : columns) { - column.update(scale); - } - - } - - @Override - public void finish() { - for (Column column : columns) { - column.finish(); - } - } - - public List getColumns() { - return columns; - } - - public ColumnChartData setColumns(List columns) { - if (null == columns) { - this.columns = new ArrayList(); - } else { - this.columns = columns; - } - return this; - } - - public boolean isStacked() { - return isStacked; - } - - /** - * Set true if you want stacked column chart. - * - * @param isStacked - * @return - */ - public ColumnChartData setStacked(boolean isStacked) { - this.isStacked = isStacked; - return this; - } - - public float getFillRatio() { - return fillRatio; - } - - /** - * Set fill ration for columns, value from 0 to 1, 1 means that there will be almost no free space between columns, - * 0 means that columns will have minimum width(2px). - * - * @param fillRatio - * @return - */ - public ColumnChartData setFillRatio(float fillRatio) { - if (fillRatio < 0) { - fillRatio = 0; - } - if (fillRatio > 1) { - fillRatio = 1; - } - this.fillRatio = fillRatio; - return this; - } - - /** - * @see #setBaseValue(float) - */ - public float getBaseValue() { - return baseValue; - } - - /** - * Set value below which values will be drawn as negative, by default 0. - */ - public ColumnChartData setBaseValue(float baseValue) { - this.baseValue = baseValue; - return this; - } - - public static ColumnChartData generateDummyData() { - final int numColumns = 4; - ColumnChartData data = new ColumnChartData(); - List columns = new ArrayList(numColumns); - List values; - Column column; - for (int i = 1; i <= numColumns; ++i) { - values = new ArrayList(numColumns); - values.add(new SubcolumnValue(i)); - column = new Column(values); - columns.add(column); - } - - data.setColumns(columns); - return data; - } + public static final float DEFAULT_FILL_RATIO = 0.75f; + public static final float DEFAULT_BASE_VALUE = 0.0f; + private List columns = new ArrayList(); + private boolean isStacked = false; + private float fillRatio = DEFAULT_FILL_RATIO; + private float baseValue = DEFAULT_BASE_VALUE; + + public ColumnChartData() { + } + + public ColumnChartData(List columns) { + setColumns(columns); + } + + /** + * Copy constructor for deep copy. + */ + public ColumnChartData(ColumnChartData data) { + super(data); + this.isStacked = data.isStacked; + this.fillRatio = data.fillRatio; + + for (Column column : data.columns) { + this.columns.add(new Column(column)); + } + } + + @Override + public void update(float scale) { + for (Column column : columns) { + column.update(scale); + } + + } + + @Override + public void finish() { + for (Column column : columns) { + column.finish(); + } + } + + public List getColumns() { + return columns; + } + + public ColumnChartData setColumns(List columns) { + if (null == columns) { + this.columns = new ArrayList(); + } else { + this.columns = columns; + } + return this; + } + + public boolean isStacked() { + return isStacked; + } + + /** + * Set true if you want stacked column chart. + * + * @param isStacked + * @return + */ + public ColumnChartData setStacked(boolean isStacked) { + this.isStacked = isStacked; + return this; + } + + public float getFillRatio() { + return fillRatio; + } + + /** + * Set fill ration for columns, value from 0 to 1, 1 means that there will be almost no free space between columns, + * 0 means that columns will have minimum width(2px). + * + * @param fillRatio + * @return + */ + public ColumnChartData setFillRatio(float fillRatio) { + if (fillRatio < 0) { + fillRatio = 0; + } + if (fillRatio > 1) { + fillRatio = 1; + } + this.fillRatio = fillRatio; + return this; + } + + /** + * @see #setBaseValue(float) + */ + public float getBaseValue() { + return baseValue; + } + + /** + * Set value below which values will be drawn as negative, by default 0. + */ + public ColumnChartData setBaseValue(float baseValue) { + this.baseValue = baseValue; + return this; + } + + public static ColumnChartData generateDummyData() { + final int numColumns = 4; + ColumnChartData data = new ColumnChartData(); + List columns = new ArrayList(numColumns); + List values; + Column column; + for (int i = 1; i <= numColumns; ++i) { + values = new ArrayList(numColumns); + values.add(new SubcolumnValue(i)); + column = new Column(values); + columns.add(column); + } + + data.setColumns(columns); + return data; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/ComboLineColumnChartData.java b/hellocharts-library/src/lecho/lib/hellocharts/model/ComboLineColumnChartData.java index dbca359a..068401bd 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/ComboLineColumnChartData.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/ComboLineColumnChartData.java @@ -2,71 +2,70 @@ /** * Data model for combo line-column chart. It uses ColumnChartData and LineChartData internally. - * */ public class ComboLineColumnChartData extends AbstractChartData { - private ColumnChartData columnChartData; - private LineChartData lineChartData; + private ColumnChartData columnChartData; + private LineChartData lineChartData; - public ComboLineColumnChartData() { - this.columnChartData = new ColumnChartData(); - this.lineChartData = new LineChartData(); - } + public ComboLineColumnChartData() { + this.columnChartData = new ColumnChartData(); + this.lineChartData = new LineChartData(); + } - public ComboLineColumnChartData(ColumnChartData columnChartData, LineChartData lineChartData) { - setColumnChartData(columnChartData); - setLineChartData(lineChartData); - } + public ComboLineColumnChartData(ColumnChartData columnChartData, LineChartData lineChartData) { + setColumnChartData(columnChartData); + setLineChartData(lineChartData); + } - public ComboLineColumnChartData(ComboLineColumnChartData data) { - super(data); + public ComboLineColumnChartData(ComboLineColumnChartData data) { + super(data); - setColumnChartData(new ColumnChartData(data.getColumnChartData())); - setLineChartData(new LineChartData(data.getLineChartData())); - } + setColumnChartData(new ColumnChartData(data.getColumnChartData())); + setLineChartData(new LineChartData(data.getLineChartData())); + } - @Override - public void update(float scale) { - columnChartData.update(scale); - lineChartData.update(scale); - } + @Override + public void update(float scale) { + columnChartData.update(scale); + lineChartData.update(scale); + } - @Override - public void finish() { - columnChartData.finish(); - lineChartData.finish(); - } + @Override + public void finish() { + columnChartData.finish(); + lineChartData.finish(); + } - public ColumnChartData getColumnChartData() { - return columnChartData; - } + public ColumnChartData getColumnChartData() { + return columnChartData; + } - public void setColumnChartData(ColumnChartData columnChartData) { - if (null == columnChartData) { - this.columnChartData = new ColumnChartData(); - } else { - this.columnChartData = columnChartData; - } - } + public void setColumnChartData(ColumnChartData columnChartData) { + if (null == columnChartData) { + this.columnChartData = new ColumnChartData(); + } else { + this.columnChartData = columnChartData; + } + } - public LineChartData getLineChartData() { - return lineChartData; - } + public LineChartData getLineChartData() { + return lineChartData; + } - public void setLineChartData(LineChartData lineChartData) { - if (null == lineChartData) { - this.lineChartData = new LineChartData(); - } else { - this.lineChartData = lineChartData; - } - } + public void setLineChartData(LineChartData lineChartData) { + if (null == lineChartData) { + this.lineChartData = new LineChartData(); + } else { + this.lineChartData = lineChartData; + } + } - public static ComboLineColumnChartData generateDummyData() { - ComboLineColumnChartData data = new ComboLineColumnChartData(); - data.setColumnChartData(ColumnChartData.generateDummyData()); - data.setLineChartData(LineChartData.generateDummyData()); - return data; - } + public static ComboLineColumnChartData generateDummyData() { + ComboLineColumnChartData data = new ComboLineColumnChartData(); + data.setColumnChartData(ColumnChartData.generateDummyData()); + data.setLineChartData(LineChartData.generateDummyData()); + return data; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/Line.java b/hellocharts-library/src/lecho/lib/hellocharts/model/Line.java index 7e9c491b..f2372b6b 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/Line.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/Line.java @@ -12,245 +12,246 @@ /** * Single line for line chart. - * */ public class Line { - private static final int DEFAULT_LINE_STROKE_WIDTH_DP = 3; - private static final int DEFAULT_POINT_RADIUS_DP = 6; - private static final int DEFAULT_AREA_TRANSPARENCY = 64; - private int color = ChartUtils.DEFAULT_COLOR; - private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR; - /** Transparency of area when line is filled. **/ - private int areaTransparency = DEFAULT_AREA_TRANSPARENCY; - private int strokeWidth = DEFAULT_LINE_STROKE_WIDTH_DP; - private int pointRadius = DEFAULT_POINT_RADIUS_DP; - private boolean hasPoints = true; - private boolean hasLines = true; - private boolean hasLabels = false; - private boolean hasLabelsOnlyForSelected = false; - private boolean isCubic = false; - private boolean isFilled = false; - private ValueShape shape = ValueShape.CIRCLE; - private PathEffect pathEffect; - private LineChartValueFormatter formatter = new SimpleLineChartValueFormatter(); - private List values = new ArrayList(); - - public Line() { - - } - - public Line(List values) { - setValues(values); - } - - public Line(Line line) { - this.color = line.color; - this.darkenColor = line.color; - this.areaTransparency = line.areaTransparency; - this.strokeWidth = line.strokeWidth; - this.pointRadius = line.pointRadius; - this.hasPoints = line.hasPoints; - this.hasLines = line.hasLines; - this.hasLabels = line.hasLabels; - this.hasLabelsOnlyForSelected = line.hasLabelsOnlyForSelected; - this.isCubic = line.isCubic; - this.isFilled = line.isFilled; - this.shape = line.shape; - this.pathEffect = line.pathEffect; - this.formatter = line.formatter; - - for (PointValue pointValue : line.values) { - this.values.add(new PointValue(pointValue)); - } - } - - public void update(float scale) { - for (PointValue value : values) { - value.update(scale); - } - } - - public void finish() { - for (PointValue value : values) { - value.finish(); - } - } - - public void setValues(List values) { - if (null == values) { - this.values = new ArrayList(); - } else { - this.values = values; - } - } - - public List getValues() { - return this.values; - } - - public int getColor() { - return color; - } - - public Line setColor(int color) { - this.color = color; - this.darkenColor = ChartUtils.darkenColor(color); - return this; - } - - public int getDarkenColor() { - return darkenColor; - } - - /** - * @see #setAreaTransparency(int) - */ - public int getAreaTransparency() { - return areaTransparency; - } - - /** - * Set area transparency(255 is full opacity) for filled lines - * - * @param areaTransparency - * @return - */ - public Line setAreaTransparency(int areaTransparency) { - this.areaTransparency = areaTransparency; - return this; - } - - public int getStrokeWidth() { - return strokeWidth; - } - - public Line setStrokeWidth(int strokeWidth) { - this.strokeWidth = strokeWidth; - return this; - } - - public boolean hasPoints() { - return hasPoints; - } - - public Line setHasPoints(boolean hasPoints) { - this.hasPoints = hasPoints; - return this; - } - - public boolean hasLines() { - return hasLines; - } - - public Line setHasLines(boolean hasLines) { - this.hasLines = hasLines; - return this; - } - - public boolean hasLabels() { - return hasLabels; - } - - public Line setHasLabels(boolean hasLabels) { - this.hasLabels = hasLabels; - if (hasLabels) { - this.hasLabelsOnlyForSelected = false; - } - return this; - } - - /** - * @see #setHasLabelsOnlyForSelected(boolean) - */ - public boolean hasLabelsOnlyForSelected() { - return hasLabelsOnlyForSelected; - } - - /** - * Set true if you want to show value labels only for selected value, works best when chart has - * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}. - */ - public Line setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) { - this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected; - if (hasLabelsOnlyForSelected) { - this.hasLabels = false; - } - return this; - } - - public int getPointRadius() { - return pointRadius; - } - - /** - * Set radius for points for this line. - * - * @param pointRadius - * @return - */ - public Line setPointRadius(int pointRadius) { - this.pointRadius = pointRadius; - return this; - } - - public boolean isCubic() { - return isCubic; - } - - public Line setCubic(boolean isCubic) { - this.isCubic = isCubic; - return this; - } - - public boolean isFilled() { - return isFilled; - } - - public Line setFilled(boolean isFilled) { - this.isFilled = isFilled; - return this; - } - - /** - * @see #setShape(ValueShape) - */ - public ValueShape getShape() { - return shape; - } - - /** - * Set shape for points, possible values: SQUARE, CIRCLE - * - * @param shape - * @return - */ - public Line setShape(ValueShape shape) { - this.shape = shape; - return this; - } - - public PathEffect getPathEffect() { - return pathEffect; - } - - /** - * Set path effect for this line, note: it will slow down drawing, try to not use complicated effects, - * DashPathEffect should be safe choice. - * - * @param pathEffect - */ - public void setPathEffect(PathEffect pathEffect) { - this.pathEffect = pathEffect; - } - - public LineChartValueFormatter getFormatter() { - return formatter; - } - - public Line setFormatter(LineChartValueFormatter formatter) { - if (null != formatter) { - this.formatter = formatter; - } - return this; - } + private static final int DEFAULT_LINE_STROKE_WIDTH_DP = 3; + private static final int DEFAULT_POINT_RADIUS_DP = 6; + private static final int DEFAULT_AREA_TRANSPARENCY = 64; + private int color = ChartUtils.DEFAULT_COLOR; + private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR; + /** + * Transparency of area when line is filled. * + */ + private int areaTransparency = DEFAULT_AREA_TRANSPARENCY; + private int strokeWidth = DEFAULT_LINE_STROKE_WIDTH_DP; + private int pointRadius = DEFAULT_POINT_RADIUS_DP; + private boolean hasPoints = true; + private boolean hasLines = true; + private boolean hasLabels = false; + private boolean hasLabelsOnlyForSelected = false; + private boolean isCubic = false; + private boolean isFilled = false; + private ValueShape shape = ValueShape.CIRCLE; + private PathEffect pathEffect; + private LineChartValueFormatter formatter = new SimpleLineChartValueFormatter(); + private List values = new ArrayList(); + + public Line() { + + } + + public Line(List values) { + setValues(values); + } + + public Line(Line line) { + this.color = line.color; + this.darkenColor = line.color; + this.areaTransparency = line.areaTransparency; + this.strokeWidth = line.strokeWidth; + this.pointRadius = line.pointRadius; + this.hasPoints = line.hasPoints; + this.hasLines = line.hasLines; + this.hasLabels = line.hasLabels; + this.hasLabelsOnlyForSelected = line.hasLabelsOnlyForSelected; + this.isCubic = line.isCubic; + this.isFilled = line.isFilled; + this.shape = line.shape; + this.pathEffect = line.pathEffect; + this.formatter = line.formatter; + + for (PointValue pointValue : line.values) { + this.values.add(new PointValue(pointValue)); + } + } + + public void update(float scale) { + for (PointValue value : values) { + value.update(scale); + } + } + + public void finish() { + for (PointValue value : values) { + value.finish(); + } + } + + public void setValues(List values) { + if (null == values) { + this.values = new ArrayList(); + } else { + this.values = values; + } + } + + public List getValues() { + return this.values; + } + + public int getColor() { + return color; + } + + public Line setColor(int color) { + this.color = color; + this.darkenColor = ChartUtils.darkenColor(color); + return this; + } + + public int getDarkenColor() { + return darkenColor; + } + + /** + * @see #setAreaTransparency(int) + */ + public int getAreaTransparency() { + return areaTransparency; + } + + /** + * Set area transparency(255 is full opacity) for filled lines + * + * @param areaTransparency + * @return + */ + public Line setAreaTransparency(int areaTransparency) { + this.areaTransparency = areaTransparency; + return this; + } + + public int getStrokeWidth() { + return strokeWidth; + } + + public Line setStrokeWidth(int strokeWidth) { + this.strokeWidth = strokeWidth; + return this; + } + + public boolean hasPoints() { + return hasPoints; + } + + public Line setHasPoints(boolean hasPoints) { + this.hasPoints = hasPoints; + return this; + } + + public boolean hasLines() { + return hasLines; + } + + public Line setHasLines(boolean hasLines) { + this.hasLines = hasLines; + return this; + } + + public boolean hasLabels() { + return hasLabels; + } + + public Line setHasLabels(boolean hasLabels) { + this.hasLabels = hasLabels; + if (hasLabels) { + this.hasLabelsOnlyForSelected = false; + } + return this; + } + + /** + * @see #setHasLabelsOnlyForSelected(boolean) + */ + public boolean hasLabelsOnlyForSelected() { + return hasLabelsOnlyForSelected; + } + + /** + * Set true if you want to show value labels only for selected value, works best when chart has + * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}. + */ + public Line setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) { + this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected; + if (hasLabelsOnlyForSelected) { + this.hasLabels = false; + } + return this; + } + + public int getPointRadius() { + return pointRadius; + } + + /** + * Set radius for points for this line. + * + * @param pointRadius + * @return + */ + public Line setPointRadius(int pointRadius) { + this.pointRadius = pointRadius; + return this; + } + + public boolean isCubic() { + return isCubic; + } + + public Line setCubic(boolean isCubic) { + this.isCubic = isCubic; + return this; + } + + public boolean isFilled() { + return isFilled; + } + + public Line setFilled(boolean isFilled) { + this.isFilled = isFilled; + return this; + } + + /** + * @see #setShape(ValueShape) + */ + public ValueShape getShape() { + return shape; + } + + /** + * Set shape for points, possible values: SQUARE, CIRCLE + * + * @param shape + * @return + */ + public Line setShape(ValueShape shape) { + this.shape = shape; + return this; + } + + public PathEffect getPathEffect() { + return pathEffect; + } + + /** + * Set path effect for this line, note: it will slow down drawing, try to not use complicated effects, + * DashPathEffect should be safe choice. + * + * @param pathEffect + */ + public void setPathEffect(PathEffect pathEffect) { + this.pathEffect = pathEffect; + } + + public LineChartValueFormatter getFormatter() { + return formatter; + } + + public Line setFormatter(LineChartValueFormatter formatter) { + if (null != formatter) { + this.formatter = formatter; + } + return this; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/LineChartData.java b/hellocharts-library/src/lecho/lib/hellocharts/model/LineChartData.java index d20f82bd..dacb768f 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/LineChartData.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/LineChartData.java @@ -5,89 +5,88 @@ /** * Data model for LineChartView. - * */ public class LineChartData extends AbstractChartData { - public static final float DEFAULT_BASE_VALUE = 0.0f; + public static final float DEFAULT_BASE_VALUE = 0.0f; - private List lines = new ArrayList(); - private float baseValue = DEFAULT_BASE_VALUE; + private List lines = new ArrayList(); + private float baseValue = DEFAULT_BASE_VALUE; - public LineChartData() { + public LineChartData() { - } + } - public LineChartData(List lines) { - setLines(lines); - } + public LineChartData(List lines) { + setLines(lines); + } - /** - * Copy constructor to perform deep copy of chart data. - */ - public LineChartData(LineChartData data) { - super(data); - this.baseValue = data.baseValue; + /** + * Copy constructor to perform deep copy of chart data. + */ + public LineChartData(LineChartData data) { + super(data); + this.baseValue = data.baseValue; - for (Line line : data.lines) { - this.lines.add(new Line(line)); - } - } + for (Line line : data.lines) { + this.lines.add(new Line(line)); + } + } - @Override - public void update(float scale) { - for (Line line : lines) { - line.update(scale); - } - } + @Override + public void update(float scale) { + for (Line line : lines) { + line.update(scale); + } + } - @Override - public void finish() { - for (Line line : lines) { - line.finish(); - } - } + @Override + public void finish() { + for (Line line : lines) { + line.finish(); + } + } - public List getLines() { - return lines; - } + public List getLines() { + return lines; + } - public LineChartData setLines(List lines) { - if (null == lines) { - this.lines = new ArrayList(); - } else { - this.lines = lines; - } - return this; - } + public LineChartData setLines(List lines) { + if (null == lines) { + this.lines = new ArrayList(); + } else { + this.lines = lines; + } + return this; + } - /** - * @see #setBaseValue(float) - */ - public float getBaseValue() { - return baseValue; - } + /** + * @see #setBaseValue(float) + */ + public float getBaseValue() { + return baseValue; + } - /** - * Set value below which values will be drawn as negative, important attribute for drawing filled area charts, by - * default 0. - */ - public LineChartData setBaseValue(float baseValue) { - this.baseValue = baseValue; - return this; - } + /** + * Set value below which values will be drawn as negative, important attribute for drawing filled area charts, by + * default 0. + */ + public LineChartData setBaseValue(float baseValue) { + this.baseValue = baseValue; + return this; + } - public static LineChartData generateDummyData() { - final int numValues = 4; - LineChartData data = new LineChartData(); - List values = new ArrayList(numValues); - values.add(new PointValue(0, 2)); - values.add(new PointValue(1, 4)); - values.add(new PointValue(2, 3)); - values.add(new PointValue(3, 4)); - Line line = new Line(values); - List lines = new ArrayList(1); - lines.add(line); - data.setLines(lines); - return data; - } + public static LineChartData generateDummyData() { + final int numValues = 4; + LineChartData data = new LineChartData(); + List values = new ArrayList(numValues); + values.add(new PointValue(0, 2)); + values.add(new PointValue(1, 4)); + values.add(new PointValue(2, 3)); + values.add(new PointValue(3, 4)); + Line line = new Line(values); + List lines = new ArrayList(1); + lines.add(line); + data.setLines(lines); + return data; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/PieChartData.java b/hellocharts-library/src/lecho/lib/hellocharts/model/PieChartData.java index 6d6a011c..6b396652 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/PieChartData.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/PieChartData.java @@ -15,260 +15,268 @@ * Data for PieChart, by default it doesn't have axes. */ public class PieChartData extends AbstractChartData { - public static final int DEFAULT_CENTER_TEXT1_SIZE_SP = 42; - public static final int DEFAULT_CENTER_TEXT2_SIZE_SP = 16; - public static final float DEFAULT_CENTER_CIRCLE_SCALE = 0.6f; + public static final int DEFAULT_CENTER_TEXT1_SIZE_SP = 42; + private int centerText1FontSize = DEFAULT_CENTER_TEXT1_SIZE_SP; + public static final int DEFAULT_CENTER_TEXT2_SIZE_SP = 16; + private int centerText2FontSize = DEFAULT_CENTER_TEXT2_SIZE_SP; + public static final float DEFAULT_CENTER_CIRCLE_SCALE = 0.6f; + private float centerCircleScale = DEFAULT_CENTER_CIRCLE_SCALE; private static final int DEFAULT_SLICE_SPACING_DP = 2; - - private PieChartValueFormatter formatter = new SimplePieChartValueFormatter(); - private boolean hasLabels = false; - private boolean hasLabelsOnlyForSelected = false; - private boolean hasLabelsOutside = false; private int slicesSpacing = DEFAULT_SLICE_SPACING_DP; + private PieChartValueFormatter formatter = new SimplePieChartValueFormatter(); + private boolean hasLabels = false; + private boolean hasLabelsOnlyForSelected = false; + private boolean hasLabelsOutside = false; + private boolean hasCenterCircle = false; + private int centerCircleColor = Color.TRANSPARENT; + private int centerText1Color = Color.BLACK; + private Typeface centerText1Typeface; + private String centerText1; + private int centerText2Color = Color.BLACK; + private Typeface centerText2Typeface; + private String centerText2; + + private List values = new ArrayList(); + + public PieChartData() { + setAxisXBottom(null); + setAxisYLeft(null); + } + + public PieChartData(List values) { + setValues(values); + // Empty axes. Pie chart don't need axes. + setAxisXBottom(null); + setAxisYLeft(null); + } + + public PieChartData(PieChartData data) { + super(data); + this.formatter = data.formatter; + this.hasLabels = data.hasLabels; + this.hasLabelsOnlyForSelected = data.hasLabelsOnlyForSelected; + this.hasLabelsOutside = data.hasLabelsOutside; + + this.hasCenterCircle = data.hasCenterCircle; + this.centerCircleColor = data.centerCircleColor; + this.centerCircleScale = data.centerCircleScale; + + this.centerText1Color = data.centerText1Color; + this.centerText1FontSize = data.centerText1FontSize; + this.centerText1Typeface = data.centerText1Typeface; + this.centerText1 = data.centerText1; + + this.centerText2Color = data.centerText2Color; + this.centerText2FontSize = data.centerText2FontSize; + this.centerText2Typeface = data.centerText2Typeface; + this.centerText2 = data.centerText2; + + for (SliceValue sliceValue : data.values) { + this.values.add(new SliceValue(sliceValue)); + } + } + + public static PieChartData generateDummyData() { + final int numValues = 4; + PieChartData data = new PieChartData(); + List values = new ArrayList(numValues); + values.add(new SliceValue(40f)); + values.add(new SliceValue(20f)); + values.add(new SliceValue(30f)); + values.add(new SliceValue(50f)); + data.setValues(values); + return data; + } + + @Override + public void update(float scale) { + for (SliceValue value : values) { + value.update(scale); + } + } + + @Override + public void finish() { + for (SliceValue value : values) { + value.finish(); + } + } + + /** + * PieChart does not support axes so method call will be ignored + */ + @Override + public void setAxisXBottom(Axis axisX) { + super.setAxisXBottom(null); + } + + /** + * PieChart does not support axes so method call will be ignored + */ + @Override + public void setAxisYLeft(Axis axisY) { + super.setAxisYLeft(null); + } + + public List getValues() { + return values; + } + + public PieChartData setValues(List values) { + if (null == values) { + this.values = new ArrayList(); + } else { + this.values = values; + } + return this; + } + + public boolean hasLabels() { + return hasLabels; + } + + public PieChartData setHasLabels(boolean hasLabels) { + this.hasLabels = hasLabels; + if (hasLabels) { + hasLabelsOnlyForSelected = false; + } + return this; + } + + /** + * @see #setHasLabelsOnlyForSelected(boolean) + */ + public boolean hasLabelsOnlyForSelected() { + return hasLabelsOnlyForSelected; + } + + /** + * Set true if you want to show value labels only for selected value, works best when chart has + * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}. + */ + public PieChartData setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) { + this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected; + if (hasLabelsOnlyForSelected) { + this.hasLabels = false; + } + return this; + } + + public boolean hasLabelsOutside() { + return hasLabelsOutside; + } - private boolean hasCenterCircle = false; - private int centerCircleColor = Color.TRANSPARENT; - private float centerCircleScale = DEFAULT_CENTER_CIRCLE_SCALE; - - private int centerText1Color = Color.BLACK; - private int centerText1FontSize = DEFAULT_CENTER_TEXT1_SIZE_SP; - private Typeface centerText1Typeface; - private String centerText1; - - private int centerText2Color = Color.BLACK; - private int centerText2FontSize = DEFAULT_CENTER_TEXT2_SIZE_SP; - private Typeface centerText2Typeface; - private String centerText2; - - private List values = new ArrayList(); - - public PieChartData() { - setAxisXBottom(null); - setAxisYLeft(null); - } - - public PieChartData(List values) { - setValues(values); - // Empty axes. Pie chart don't need axes. - setAxisXBottom(null); - setAxisYLeft(null); - } - - public PieChartData(PieChartData data) { - super(data); - this.formatter = data.formatter; - this.hasLabels = data.hasLabels; - this.hasLabelsOnlyForSelected = data.hasLabelsOnlyForSelected; - this.hasLabelsOutside = data.hasLabelsOutside; - - this.hasCenterCircle = data.hasCenterCircle; - this.centerCircleColor = data.centerCircleColor; - this.centerCircleScale = data.centerCircleScale; - - this.centerText1Color = data.centerText1Color; - this.centerText1FontSize = data.centerText1FontSize; - this.centerText1Typeface = data.centerText1Typeface; - this.centerText1 = data.centerText1; - - this.centerText2Color = data.centerText2Color; - this.centerText2FontSize = data.centerText2FontSize; - this.centerText2Typeface = data.centerText2Typeface; - this.centerText2 = data.centerText2; - - for (SliceValue sliceValue : data.values) { - this.values.add(new SliceValue(sliceValue)); - } - } - - @Override - public void update(float scale) { - for (SliceValue value : values) { - value.update(scale); - } - } - - @Override - public void finish() { - for (SliceValue value : values) { - value.finish(); - } - } - - /** - * PieChart does not support axes so method call will be ignored - */ - @Override - public void setAxisXBottom(Axis axisX) { - super.setAxisXBottom(null); - } - - /** - * PieChart does not support axes so method call will be ignored - */ - @Override - public void setAxisYLeft(Axis axisY) { - super.setAxisYLeft(null); - } - - public List getValues() { - return values; - } - - public PieChartData setValues(List values) { - if (null == values) { - this.values = new ArrayList(); - } else { - this.values = values; - } - return this; - } - - public boolean hasLabels() { - return hasLabels; - } - - public PieChartData setHasLabels(boolean hasLabels) { - this.hasLabels = hasLabels; - if (hasLabels) { - hasLabelsOnlyForSelected = false; - } - return this; - } - - /** - * @see #setHasLabelsOnlyForSelected(boolean) - */ - public boolean hasLabelsOnlyForSelected() { - return hasLabelsOnlyForSelected; - } - - /** - * Set true if you want to show value labels only for selected value, works best when chart has - * isValueSelectionEnabled set to true {@link Chart#setValueSelectionEnabled(boolean)}. - */ - public PieChartData setHasLabelsOnlyForSelected(boolean hasLabelsOnlyForSelected) { - this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected; - if (hasLabelsOnlyForSelected) { - this.hasLabels = false; - } - return this; - } - - public boolean hasLabelsOutside() { - return hasLabelsOutside; - } - - /** - * Set if labels should be drawn inside circle(false) or outside(true). By default false. If you set it to true you - * should also change chart fill ration using {@link PieChartView#setCircleFillRatio(float)}. This flag is used only - * if you also set hasLabels or hasLabelsOnlyForSelected flags. - */ - public PieChartData setHasLabelsOutside(boolean hasLabelsOutside) { - this.hasLabelsOutside = hasLabelsOutside; + /** + * Set if labels should be drawn inside circle(false) or outside(true). By default false. If you set it to true you + * should also change chart fill ration using {@link PieChartView#setCircleFillRatio(float)}. This flag is used only + * if you also set hasLabels or hasLabelsOnlyForSelected flags. + */ + public PieChartData setHasLabelsOutside(boolean hasLabelsOutside) { + this.hasLabelsOutside = hasLabelsOutside; return this; - } + } - public boolean hasCenterCircle() { - return hasCenterCircle; - } + public boolean hasCenterCircle() { + return hasCenterCircle; + } - public PieChartData setHasCenterCircle(boolean hasCenterCircle) { - this.hasCenterCircle = hasCenterCircle; + public PieChartData setHasCenterCircle(boolean hasCenterCircle) { + this.hasCenterCircle = hasCenterCircle; return this; - } + } - public int getCenterCircleColor() { - return centerCircleColor; - } + public int getCenterCircleColor() { + return centerCircleColor; + } - public PieChartData setCenterCircleColor(int centerCircleColor) { - this.centerCircleColor = centerCircleColor; + public PieChartData setCenterCircleColor(int centerCircleColor) { + this.centerCircleColor = centerCircleColor; return this; - } + } - public float getCenterCircleScale() { - return centerCircleScale; - } + public float getCenterCircleScale() { + return centerCircleScale; + } - public PieChartData setCenterCircleScale(float centerCircleScale) { - this.centerCircleScale = centerCircleScale; + public PieChartData setCenterCircleScale(float centerCircleScale) { + this.centerCircleScale = centerCircleScale; return this; - } + } - public int getCenterText1Color() { - return centerText1Color; - } + public int getCenterText1Color() { + return centerText1Color; + } - public PieChartData setCenterText1Color(int centerText1Color) { - this.centerText1Color = centerText1Color; + public PieChartData setCenterText1Color(int centerText1Color) { + this.centerText1Color = centerText1Color; return this; - } + } - public int getCenterText1FontSize() { - return centerText1FontSize; - } + public int getCenterText1FontSize() { + return centerText1FontSize; + } - public PieChartData setCenterText1FontSize(int centerText1FontSize) { - this.centerText1FontSize = centerText1FontSize; + public PieChartData setCenterText1FontSize(int centerText1FontSize) { + this.centerText1FontSize = centerText1FontSize; return this; - } + } - public Typeface getCenterText1Typeface() { - return centerText1Typeface; - } + public Typeface getCenterText1Typeface() { + return centerText1Typeface; + } - public PieChartData setCenterText1Typeface(Typeface text1Typeface) { - this.centerText1Typeface = text1Typeface; + public PieChartData setCenterText1Typeface(Typeface text1Typeface) { + this.centerText1Typeface = text1Typeface; return this; - } + } - public String getCenterText1() { - return centerText1; - } + public String getCenterText1() { + return centerText1; + } - public PieChartData setCenterText1(String centerText1) { - this.centerText1 = centerText1; + public PieChartData setCenterText1(String centerText1) { + this.centerText1 = centerText1; return this; - } + } - public String getCenterText2() { - return centerText2; - } + public String getCenterText2() { + return centerText2; + } - /** - * Note that centerText2 will be drawn only if centerText1 is not empty/null. - */ - public PieChartData setCenterText2(String centerText2) { - this.centerText2 = centerText2; + /** + * Note that centerText2 will be drawn only if centerText1 is not empty/null. + */ + public PieChartData setCenterText2(String centerText2) { + this.centerText2 = centerText2; return this; - } + } - public int getCenterText2Color() { - return centerText2Color; - } + public int getCenterText2Color() { + return centerText2Color; + } - public PieChartData setCenterText2Color(int centerText2Color) { - this.centerText2Color = centerText2Color; + public PieChartData setCenterText2Color(int centerText2Color) { + this.centerText2Color = centerText2Color; return this; - } + } - public int getCenterText2FontSize() { - return centerText2FontSize; - } + public int getCenterText2FontSize() { + return centerText2FontSize; + } - public PieChartData setCenterText2FontSize(int centerText2FontSize) { - this.centerText2FontSize = centerText2FontSize; + public PieChartData setCenterText2FontSize(int centerText2FontSize) { + this.centerText2FontSize = centerText2FontSize; return this; - } + } - public Typeface getCenterText2Typeface() { - return centerText2Typeface; - } + public Typeface getCenterText2Typeface() { + return centerText2Typeface; + } - public PieChartData setCenterText2Typeface(Typeface text2Typeface) { - this.centerText2Typeface = text2Typeface; + public PieChartData setCenterText2Typeface(Typeface text2Typeface) { + this.centerText2Typeface = text2Typeface; return this; - } + } public int getSlicesSpacing() { return slicesSpacing; @@ -279,26 +287,14 @@ public PieChartData setSlicesSpacing(int sliceSpacing) { return this; } - public PieChartValueFormatter getFormatter() { - return formatter; - } + public PieChartValueFormatter getFormatter() { + return formatter; + } - public PieChartData setFormatter(PieChartValueFormatter formatter) { - if (null != formatter) { - this.formatter = formatter; - } + public PieChartData setFormatter(PieChartValueFormatter formatter) { + if (null != formatter) { + this.formatter = formatter; + } return this; - } - - public static PieChartData generateDummyData() { - final int numValues = 4; - PieChartData data = new PieChartData(); - List values = new ArrayList(numValues); - values.add(new SliceValue(40f)); - values.add(new SliceValue(20f)); - values.add(new SliceValue(30f)); - values.add(new SliceValue(50f)); - data.setValues(values); - return data; - } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/PointValue.java b/hellocharts-library/src/lecho/lib/hellocharts/model/PointValue.java index 3bb75002..14df9b49 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/PointValue.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/PointValue.java @@ -6,93 +6,91 @@ /** * Single point coordinates, used for LineChartData. - * */ public class PointValue { - private float x; - private float y; - private float originX; - private float originY; - private float diffX; - private float diffY; - private char[] label; - - public PointValue() { - set(0, 0); - } - - public PointValue(float x, float y) { - set(x, y); - } - - public PointValue(PointValue pointValue) { - set(pointValue.x, pointValue.y); - this.label = pointValue.label; - } - - public void update(float scale) { - x = originX + diffX * scale; - y = originY + diffY * scale; - } - - public void finish() { - set(originX + diffX, originY + diffY); - } - - public PointValue set(float x, float y) { - this.x = x; - this.y = y; - this.originX = x; - this.originY = y; - this.diffX = 0; - this.diffY = 0; - return this; - } - - /** - * Set target values that should be reached when data animation finish then call {@link Chart#startDataAnimation()} - * - */ - public PointValue setTarget(float targetX, float targetY) { - set(x, y); - this.diffX = targetX - originX; - this.diffY = targetY - originY; - return this; - } - - public float getX() { - return this.x; - } - - public float getY() { - return this.y; - } - - @Deprecated - public char[] getLabel() { - return label; - } - - public char[] getLabelAsChars() { - return label; - } - - @Deprecated - public PointValue setLabel(char[] label) { - this.label = label; - return this; - } - - public PointValue setLabel(String label) { - this.label = label.toCharArray(); - return this; - } - - @Override - public String toString() { - return "PointValue [x=" + x + ", y=" + y + "]"; - } + private float x; + private float y; + private float originX; + private float originY; + private float diffX; + private float diffY; + private char[] label; + + public PointValue() { + set(0, 0); + } + + public PointValue(float x, float y) { + set(x, y); + } + + public PointValue(PointValue pointValue) { + set(pointValue.x, pointValue.y); + this.label = pointValue.label; + } + + public void update(float scale) { + x = originX + diffX * scale; + y = originY + diffY * scale; + } + + public void finish() { + set(originX + diffX, originY + diffY); + } + + public PointValue set(float x, float y) { + this.x = x; + this.y = y; + this.originX = x; + this.originY = y; + this.diffX = 0; + this.diffY = 0; + return this; + } + + /** + * Set target values that should be reached when data animation finish then call {@link Chart#startDataAnimation()} + */ + public PointValue setTarget(float targetX, float targetY) { + set(x, y); + this.diffX = targetX - originX; + this.diffY = targetY - originY; + return this; + } + + public float getX() { + return this.x; + } + + public float getY() { + return this.y; + } + + @Deprecated + public char[] getLabel() { + return label; + } + + public char[] getLabelAsChars() { + return label; + } + + @Deprecated + public PointValue setLabel(char[] label) { + this.label = label; + return this; + } + + public PointValue setLabel(String label) { + this.label = label.toCharArray(); + return this; + } + + @Override + public String toString() { + return "PointValue [x=" + x + ", y=" + y + "]"; + } @Override public boolean equals(Object o) { diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/SelectedValue.java b/hellocharts-library/src/lecho/lib/hellocharts/model/SelectedValue.java index 74de6258..0f8f0bd9 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/SelectedValue.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/SelectedValue.java @@ -2,132 +2,131 @@ /** * Holds selected values indexes, i.e. for LineChartModel it will be firstIndex=lineIndex; secondIndex=valueIndex. - * */ public class SelectedValue { - /** - * First index i.e for LineChart that will be line index. - */ - private int firstIndex; - - /** - * Second index i.e for LineChart that will be PointValue index. - */ - private int secondIndex; - - /** - * Used only for combo charts, in other cases should have value NONE. - */ - private SelectedValueType type = SelectedValueType.NONE; - - public SelectedValue() { - clear(); - } - - public SelectedValue(int firstIndex, int secondIndex, SelectedValueType type) { - set(firstIndex, secondIndex, type); - } - - public void set(int firstIndex, int secondIndex, SelectedValueType type) { - this.firstIndex = firstIndex; - this.secondIndex = secondIndex; - if (null != type) { - this.type = type; - } else { - this.type = SelectedValueType.NONE; - } - } - - public void set(SelectedValue selectedValue) { - this.firstIndex = selectedValue.firstIndex; - this.secondIndex = selectedValue.secondIndex; - this.type = selectedValue.type; - } - - public void clear() { - set(Integer.MIN_VALUE, Integer.MIN_VALUE, SelectedValueType.NONE); - } - - /** - * Return true if selected value have meaningful value. - */ - public boolean isSet() { - if (firstIndex >= 0 && secondIndex >= 0) { - return true; - } else { - return false; - } - } - - /** - * First index i.e for LineChart that will be line index. - */ - public int getFirstIndex() { - return firstIndex; - } - - public void setFirstIndex(int firstIndex) { - this.firstIndex = firstIndex; - } - - /** - * Second index i.e for LineChart that will be PointValue index. - */ - public int getSecondIndex() { - return secondIndex; - } - - public void setSecondIndex(int secondIndex) { - this.secondIndex = secondIndex; - } - - public SelectedValueType getType() { - return type; - } - - public void setType(SelectedValueType type) { - this.type = type; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + firstIndex; - result = prime * result + secondIndex; - result = prime * result + ((type == null) ? 0 : type.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - SelectedValue other = (SelectedValue) obj; - if (firstIndex != other.firstIndex) - return false; - if (secondIndex != other.secondIndex) - return false; - if (type != other.type) - return false; - return true; - } - - @Override - public String toString() { - return "SelectedValue [firstIndex=" + firstIndex + ", secondIndex=" + secondIndex + ", type=" + type + "]"; - } - - /** - * Used in combo chart to determine if selected value is used for line or column selection. - */ - public enum SelectedValueType { - NONE, LINE, COLUMN - } + /** + * First index i.e for LineChart that will be line index. + */ + private int firstIndex; + + /** + * Second index i.e for LineChart that will be PointValue index. + */ + private int secondIndex; + + /** + * Used only for combo charts, in other cases should have value NONE. + */ + private SelectedValueType type = SelectedValueType.NONE; + + public SelectedValue() { + clear(); + } + + public SelectedValue(int firstIndex, int secondIndex, SelectedValueType type) { + set(firstIndex, secondIndex, type); + } + + public void set(int firstIndex, int secondIndex, SelectedValueType type) { + this.firstIndex = firstIndex; + this.secondIndex = secondIndex; + if (null != type) { + this.type = type; + } else { + this.type = SelectedValueType.NONE; + } + } + + public void set(SelectedValue selectedValue) { + this.firstIndex = selectedValue.firstIndex; + this.secondIndex = selectedValue.secondIndex; + this.type = selectedValue.type; + } + + public void clear() { + set(Integer.MIN_VALUE, Integer.MIN_VALUE, SelectedValueType.NONE); + } + + /** + * Return true if selected value have meaningful value. + */ + public boolean isSet() { + if (firstIndex >= 0 && secondIndex >= 0) { + return true; + } else { + return false; + } + } + + /** + * First index i.e for LineChart that will be line index. + */ + public int getFirstIndex() { + return firstIndex; + } + + public void setFirstIndex(int firstIndex) { + this.firstIndex = firstIndex; + } + + /** + * Second index i.e for LineChart that will be PointValue index. + */ + public int getSecondIndex() { + return secondIndex; + } + + public void setSecondIndex(int secondIndex) { + this.secondIndex = secondIndex; + } + + public SelectedValueType getType() { + return type; + } + + public void setType(SelectedValueType type) { + this.type = type; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + firstIndex; + result = prime * result + secondIndex; + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + SelectedValue other = (SelectedValue) obj; + if (firstIndex != other.firstIndex) + return false; + if (secondIndex != other.secondIndex) + return false; + if (type != other.type) + return false; + return true; + } + + @Override + public String toString() { + return "SelectedValue [firstIndex=" + firstIndex + ", secondIndex=" + secondIndex + ", type=" + type + "]"; + } + + /** + * Used in combo chart to determine if selected value is used for line or column selection. + */ + public enum SelectedValueType { + NONE, LINE, COLUMN + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/SliceValue.java b/hellocharts-library/src/lecho/lib/hellocharts/model/SliceValue.java index 1a180e04..d7b7a6d5 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/SliceValue.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/SliceValue.java @@ -7,139 +7,143 @@ /** * Model representing single slice on PieChart. - * */ public class SliceValue { - private static final int DEFAULT_SLICE_SPACING_DP = 2; + private static final int DEFAULT_SLICE_SPACING_DP = 2; + @Deprecated + /** Spacing between this slice and its neighbors. */ + private int sliceSpacing = DEFAULT_SLICE_SPACING_DP; + /** + * Current value of this slice. + */ + private float value; + /** + * Origin value of this slice, used during value animation. + */ + private float originValue; + /** + * Difference between originValue and targetValue. + */ + private float diff; + /** + * Color of this slice. + */ + private int color = ChartUtils.DEFAULT_COLOR; + /** + * Darken color used to draw label background and give touch feedback. + */ + private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR; + /** + * Custom label for this slice, if not set number formatting will be used. + */ + private char[] label; + + public SliceValue() { + setValue(0); + } + + public SliceValue(float value) { + setValue(value); + } + + public SliceValue(float value, int color) { + setValue(value); + setColor(color); + } + + public SliceValue(float value, int color, int sliceSpacing) { + setValue(value); + setColor(color); + this.sliceSpacing = sliceSpacing; + } + + public SliceValue(SliceValue sliceValue) { + setValue(sliceValue.value); + setColor(sliceValue.color); + this.sliceSpacing = sliceValue.sliceSpacing; + this.label = sliceValue.label; + } + + public void update(float scale) { + value = originValue + diff * scale; + } + + public void finish() { + setValue(originValue + diff); + } + + public float getValue() { + return value; + } + + public SliceValue setValue(float value) { + this.value = value; + this.originValue = value; + this.diff = 0; + return this; + } - /** Current value of this slice. */ - private float value; + /** + * Set target value that should be reached when data animation finish then call {@link Chart#startDataAnimation()} + * + * @param target + * @return + */ + public SliceValue setTarget(float target) { + setValue(value); + this.diff = target - originValue; + return this; + } - /** Origin value of this slice, used during value animation. */ - private float originValue; + public int getColor() { + return color; + } - /** Difference between originValue and targetValue. */ - private float diff; + public SliceValue setColor(int color) { + this.color = color; + this.darkenColor = ChartUtils.darkenColor(color); + return this; + } - /** Color of this slice. */ - private int color = ChartUtils.DEFAULT_COLOR; + public int getDarkenColor() { + return darkenColor; + } - /** Darken color used to draw label background and give touch feedback. */ - private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR; + @Deprecated + public int getSliceSpacing() { + return sliceSpacing; + } @Deprecated - /** Spacing between this slice and its neighbors. */ - private int sliceSpacing = DEFAULT_SLICE_SPACING_DP; - - /** Custom label for this slice, if not set number formatting will be used. */ - private char[] label; - - public SliceValue() { - setValue(0); - } - - public SliceValue(float value) { - setValue(value); - } - - public SliceValue(float value, int color) { - setValue(value); - setColor(color); - } - - public SliceValue(float value, int color, int sliceSpacing) { - setValue(value); - setColor(color); - this.sliceSpacing = sliceSpacing; - } - - public SliceValue(SliceValue sliceValue) { - setValue(sliceValue.value); - setColor(sliceValue.color); - this.sliceSpacing = sliceValue.sliceSpacing; - this.label = sliceValue.label; - } - - public void update(float scale) { - value = originValue + diff * scale; - } - - public void finish() { - setValue(originValue + diff); - } - - public float getValue() { - return value; - } - - public SliceValue setValue(float value) { - this.value = value; - this.originValue = value; - this.diff = 0; - return this; - } - - /** - * Set target value that should be reached when data animation finish then call {@link Chart#startDataAnimation()} - * - * @param target - * @return - */ - public SliceValue setTarget(float target) { - setValue(value); - this.diff = target - originValue; - return this; - } - - public int getColor() { - return color; - } - - public SliceValue setColor(int color) { - this.color = color; - this.darkenColor = ChartUtils.darkenColor(color); - return this; - } - - public int getDarkenColor() { - return darkenColor; - } + public SliceValue setSliceSpacing(int sliceSpacing) { + this.sliceSpacing = sliceSpacing; + return this; + } @Deprecated - public int getSliceSpacing() { - return sliceSpacing; - } + public char[] getLabel() { + return label; + } + + public SliceValue setLabel(String label) { + this.label = label.toCharArray(); + return this; + } + + public char[] getLabelAsChars() { + return label; + } @Deprecated - public SliceValue setSliceSpacing(int sliceSpacing) { - this.sliceSpacing = sliceSpacing; - return this; - } - - @Deprecated - public char[] getLabel() { - return label; - } - - public char[] getLabelAsChars() { - return label; - } - - @Deprecated - public SliceValue setLabel(char[] label) { - this.label = label; - return this; - } - - public SliceValue setLabel(String label) { - this.label = label.toCharArray(); - return this; - } - - @Override - public String toString() { - return "SliceValue [value=" + value + "]"; - } + public SliceValue setLabel(char[] label) { + this.label = label; + return this; + } + + @Override + public String toString() { + return "SliceValue [value=" + value + "]"; + } @Override public boolean equals(Object o) { diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/SubcolumnValue.java b/hellocharts-library/src/lecho/lib/hellocharts/model/SubcolumnValue.java index aa7f765c..06331bcd 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/SubcolumnValue.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/SubcolumnValue.java @@ -10,103 +10,103 @@ */ public class SubcolumnValue { - private float value; - private float originValue; - private float diff; - private int color = ChartUtils.DEFAULT_COLOR; - private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR; - private char[] label; - - public SubcolumnValue() { - setValue(0); - } - - public SubcolumnValue(float value) { - // point and targetPoint have to be different objects - setValue(value); - } - - public SubcolumnValue(float value, int color) { - // point and targetPoint have to be different objects - setValue(value); - setColor(color); - } - - public SubcolumnValue(SubcolumnValue columnValue) { - setValue(columnValue.value); - setColor(columnValue.color); - this.label = columnValue.label; - } - - public void update(float scale) { - value = originValue + diff * scale; - } - - public void finish() { - setValue(originValue + diff); - } - - public float getValue() { - return value; - } - - public SubcolumnValue setValue(float value) { - this.value = value; - this.originValue = value; - this.diff = 0; - return this; - } - - /** - * Set target value that should be reached when data animation finish then call {@link Chart#startDataAnimation()} - * - * @param target - * @return - */ - public SubcolumnValue setTarget(float target) { - setValue(value); - this.diff = target - originValue; - return this; - } - - public int getColor() { - return color; - } - - public SubcolumnValue setColor(int color) { - this.color = color; - this.darkenColor = ChartUtils.darkenColor(color); - return this; - } - - public int getDarkenColor() { - return darkenColor; - } - - @Deprecated - public char[] getLabel() { - return label; - } - - public char[] getLabelAsChars() { - return label; - } - - @Deprecated - public SubcolumnValue setLabel(char[] label) { - this.label = label; - return this; - } - - public SubcolumnValue setLabel(String label) { - this.label = label.toCharArray(); - return this; - } - - @Override - public String toString() { - return "ColumnValue [value=" + value + "]"; - } + private float value; + private float originValue; + private float diff; + private int color = ChartUtils.DEFAULT_COLOR; + private int darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR; + private char[] label; + + public SubcolumnValue() { + setValue(0); + } + + public SubcolumnValue(float value) { + // point and targetPoint have to be different objects + setValue(value); + } + + public SubcolumnValue(float value, int color) { + // point and targetPoint have to be different objects + setValue(value); + setColor(color); + } + + public SubcolumnValue(SubcolumnValue columnValue) { + setValue(columnValue.value); + setColor(columnValue.color); + this.label = columnValue.label; + } + + public void update(float scale) { + value = originValue + diff * scale; + } + + public void finish() { + setValue(originValue + diff); + } + + public float getValue() { + return value; + } + + public SubcolumnValue setValue(float value) { + this.value = value; + this.originValue = value; + this.diff = 0; + return this; + } + + /** + * Set target value that should be reached when data animation finish then call {@link Chart#startDataAnimation()} + * + * @param target + * @return + */ + public SubcolumnValue setTarget(float target) { + setValue(value); + this.diff = target - originValue; + return this; + } + + public int getColor() { + return color; + } + + public SubcolumnValue setColor(int color) { + this.color = color; + this.darkenColor = ChartUtils.darkenColor(color); + return this; + } + + public int getDarkenColor() { + return darkenColor; + } + + @Deprecated + public char[] getLabel() { + return label; + } + + public char[] getLabelAsChars() { + return label; + } + + @Deprecated + public SubcolumnValue setLabel(char[] label) { + this.label = label; + return this; + } + + public SubcolumnValue setLabel(String label) { + this.label = label.toCharArray(); + return this; + } + + @Override + public String toString() { + return "ColumnValue [value=" + value + "]"; + } @Override public boolean equals(Object o) { diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/ValueShape.java b/hellocharts-library/src/lecho/lib/hellocharts/model/ValueShape.java index 1afb0914..0acfd4ed 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/ValueShape.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/ValueShape.java @@ -1,5 +1,5 @@ package lecho.lib.hellocharts.model; public enum ValueShape { - CIRCLE, SQUARE + CIRCLE, SQUARE } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/model/Viewport.java b/hellocharts-library/src/lecho/lib/hellocharts/model/Viewport.java index bfebfe29..a98191de 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/model/Viewport.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/model/Viewport.java @@ -9,420 +9,384 @@ * bottom). These fields can be accessed directly. Use width() and height() to retrieve the viewport's width and height. * Note: most methods do not check to see that the coordinates are sorted correctly (i.e. left is less than right and * bottom is less than top). - * + *

* Viewport implements Parcerable. - * */ public class Viewport implements Parcelable { - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Viewport other = (Viewport) obj; - if (Float.floatToIntBits(bottom) != Float.floatToIntBits(other.bottom)) - return false; - if (Float.floatToIntBits(left) != Float.floatToIntBits(other.left)) - return false; - if (Float.floatToIntBits(right) != Float.floatToIntBits(other.right)) - return false; - if (Float.floatToIntBits(top) != Float.floatToIntBits(other.top)) - return false; - return true; - } - - public float left; - public float top; - public float right; - public float bottom; - - /** - * Create a new empty Viewport. All coordinates are initialized to 0. - */ - public Viewport() { - } - - /** - * Create a new viewport with the specified coordinates. Note: no range checking is performed, so the caller must - * ensure that left is less than right and bottom is less than top. - * - * @param left - * The X coordinate of the left side of the viewport - * @param top - * The Y coordinate of the top of the viewport - * @param right - * The X coordinate of the right side of the viewport - * @param bottom - * The Y coordinate of the bottom of the viewport - */ - public Viewport(float left, float top, float right, float bottom) { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - - /** - * Create a new viewport, initialized with the values in the specified viewport (which is left unmodified). - * - * @param v - * The viewport whose coordinates are copied into the new viewport. - */ - public Viewport(Viewport v) { - if (v == null) { - left = top = right = bottom = 0.0f; - } else { - left = v.left; - top = v.top; - right = v.right; - bottom = v.bottom; - } - } - - /** - * Returns true if the viewport is empty {@code left >= right or bottom >= top} - */ - public final boolean isEmpty() { - return left >= right || bottom >= top; - } - - /** - * Set the viewport to (0,0,0,0) - */ - public void setEmpty() { - left = right = top = bottom = 0; - } - - /** - * @return the viewport's width. This does not check for a valid viewport (i.e. {@code left <= right}) so the - * result may be negative. - */ - public final float width() { - return right - left; - } - - /** - * @return the viewport's height. This does not check for a valid viewport (i.e. {@code top <= bottom}) so the - * result may be negative. - */ - public final float height() { - return top - bottom; - } - - /** - * @return the horizontal center of the viewport. This does not check for a valid viewport (i.e. {@code left <= - * right}) - */ - public final float centerX() { - return (left + right) * 0.5f; - } - - /** - * @return the vertical center of the viewport. This does not check for a valid viewport (i.e. {@code bottom <= - * top}) - */ - public final float centerY() { - return (top + bottom) * 0.5f; - } - - /** - * Set the viewport's coordinates to the specified values. Note: no range checking is performed, so it is up to the - * caller to ensure that {@code left <= right and bottom <= top}. - * - * @param left - * The X coordinate of the left side of the viewport - * @param top - * The Y coordinate of the top of the viewport - * @param right - * The X coordinate of the right side of the viewport - * @param bottom - * The Y coordinate of the bottom of the viewport - */ - public void set(float left, float top, float right, float bottom) { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - - /** - * Copy the coordinates from src into this viewport. - * - * @param src - * The viewport whose coordinates are copied into this viewport. - */ - public void set(Viewport src) { - this.left = src.left; - this.top = src.top; - this.right = src.right; - this.bottom = src.bottom; - } - - /** - * Offset the viewport by adding dx to its left and right coordinates, and adding dy to its top and bottom - * coordinates. - * - * @param dx - * The amount to add to the viewport's left and right coordinates - * @param dy - * The amount to add to the viewport's top and bottom coordinates - */ - public void offset(float dx, float dy) { - left += dx; - top += dy; - right += dx; - bottom += dy; - } - - /** - * Offset the viewport to a specific (left, top) position, keeping its width and height the same. - * - * @param newLeft - * The new "left" coordinate for the viewport - * @param newTop - * The new "top" coordinate for the viewport - */ - public void offsetTo(float newLeft, float newTop) { - right += newLeft - left; - bottom += newTop - top; - left = newLeft; - top = newTop; - } - - /** - * Inset the viewport by (dx,dy). If dx is positive, then the sides are moved inwards, making the viewport narrower. - * If dx is negative, then the sides are moved outwards, making the viewport wider. The same holds true for dy and - * the top and bottom. - * - * @param dx - * The amount to add(subtract) from the viewport's left(right) - * @param dy - * The amount to add(subtract) from the viewport's top(bottom) - */ - public void inset(float dx, float dy) { - left += dx; - top -= dy; - right -= dx; - bottom += dy; - } - - /** - * Returns true if (x,y) is inside the viewport. The left and top are considered to be inside, while the right and - * bottom are not. This means that for a x,y to be contained: {@code left <= x < right and bottom <= y < top}. An - * empty viewport never contains any point. - * - * @param x - * The X coordinate of the point being tested for containment - * @param y - * The Y coordinate of the point being tested for containment - * @return true iff (x,y) are contained by the viewport, where containment means {@code left <= x < right and top <= - * y < bottom} - */ - public boolean contains(float x, float y) { - return left < right && bottom < top // check for empty first - && x >= left && x < right && y >= bottom && y < top; - } - - /** - * Returns true iff the 4 specified sides of a viewport are inside or equal to this viewport. i.e. is this viewport - * a superset of the specified viewport. An empty viewport never contains another viewport. - * - * @param left - * The left side of the viewport being tested for containment - * @param top - * The top of the viewport being tested for containment - * @param right - * The right side of the viewport being tested for containment - * @param bottom - * The bottom of the viewport being tested for containment - * @return true iff the the 4 specified sides of a viewport are inside or equal to this viewport - */ - public boolean contains(float left, float top, float right, float bottom) { - // check for empty first - return this.left < this.right && this.bottom < this.top - // now check for containment - && this.left <= left && this.top >= top && this.right >= right && this.bottom <= bottom; - } - - /** - * Returns true iff the specified viewport r is inside or equal to this viewport. An empty viewport never contains - * another viewport. - * - * @param v - * The viewport being tested for containment. - * @return true iff the specified viewport r is inside or equal to this viewport - */ - public boolean contains(Viewport v) { - // check for empty first - return this.left < this.right && this.bottom < this.top - // now check for containment - && left <= v.left && top >= v.top && right >= v.right && bottom <= v.bottom; - } - - /** - * Update this Viewport to enclose itself and the specified viewport. If the specified viewport is empty, nothing is - * done. If this viewport is empty it is set to the specified viewport. - * - * @param left - * The left edge being unioned with this viewport - * @param top - * The top edge being unioned with this viewport - * @param right - * The right edge being unioned with this viewport - * @param bottom - * The bottom edge being unioned with this viewport - */ - public void union(float left, float top, float right, float bottom) { - if ((left < right) && (bottom < top)) { - if ((this.left < this.right) && (this.bottom < this.top)) { - if (this.left > left) - this.left = left; - if (this.top < top) - this.top = top; - if (this.right < right) - this.right = right; - if (this.bottom > bottom) - this.bottom = bottom; - } else { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - } - } - - /** - * Update this Viewport to enclose itself and the specified viewport. If the specified viewport is empty, nothing is - * done. If this viewport is empty it is set to the specified viewport. - * - * @param v - * The viewport being unioned with this viewport - */ - public void union(Viewport v) { - union(v.left, v.top, v.right, v.bottom); - } - - /** - * If the viewport specified by left,top,right,bottom intersects this viewport, return true and set this viewport to - * that intersection, otherwise return false and do not change this viewport. No check is performed to see if either - * viewport is empty. Note: To just test for intersection, use intersects() - * - * @param left - * The left side of the viewport being intersected with this viewport - * @param top - * The top of the viewport being intersected with this viewport - * @param right - * The right side of the viewport being intersected with this viewport. - * @param bottom - * The bottom of the viewport being intersected with this viewport. - * @return true if the specified viewport and this viewport intersect (and this viewport is then set to that - * intersection) else return false and do not change this viewport. - */ - public boolean intersect(float left, float top, float right, float bottom) { - if (this.left < right && left < this.right && this.bottom < top && bottom < this.top) { - if (this.left < left) { - this.left = left; - } - if (this.top > top) { - this.top = top; - } - if (this.right > right) { - this.right = right; - } - if (this.bottom < bottom) { - this.bottom = bottom; - } - return true; - } - return false; - } - - /** - * If the specified viewport intersects this viewport, return true and set this viewport to that intersection, - * otherwise return false and do not change this viewport. No check is performed to see if either viewport is empty. - * To just test for intersection, use intersects() - * - * @param v - * The viewport being intersected with this viewport. - * @return true if the specified viewport and this viewport intersect (and this viewport is then set to that - * intersection) else return false and do not change this viewport. - */ - public boolean intersect(Viewport v) { - return intersect(v.left, v.top, v.right, v.bottom); - } - - @Override - public String toString() { - return "Viewport [left=" + left + ", top=" + top + ", right=" + right + ", bottom=" + bottom + "]"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + Float.floatToIntBits(bottom); - result = prime * result + Float.floatToIntBits(left); - result = prime * result + Float.floatToIntBits(right); - result = prime * result + Float.floatToIntBits(top); - return result; - } - - // ** PARCERABLE ** - - /** - * Parcelable interface methods - */ - public int describeContents() { - return 0; - } - - /** - * Write this viewport to the specified parcel. To restore a viewport from a parcel, use readFromParcel() - * - * @param out - * The parcel to write the viewport's coordinates into - */ - public void writeToParcel(Parcel out, int flags) { - out.writeFloat(left); - out.writeFloat(top); - out.writeFloat(right); - out.writeFloat(bottom); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - /** - * Return a new viewport from the data in the specified parcel. - */ - public Viewport createFromParcel(Parcel in) { - Viewport v = new Viewport(); - v.readFromParcel(in); - return v; - } - - /** - * Return an array of viewports of the specified size. - */ - public Viewport[] newArray(int size) { - return new Viewport[size]; - } - }; - - /** - * Set the viewport's coordinates from the data stored in the specified parcel. To write a viewport to a parcel, - * call writeToParcel(). - * - * @param in - * The parcel to read the viewport's coordinates from - */ - public void readFromParcel(Parcel in) { - left = in.readFloat(); - top = in.readFloat(); - right = in.readFloat(); - bottom = in.readFloat(); - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Viewport other = (Viewport) obj; + if (Float.floatToIntBits(bottom) != Float.floatToIntBits(other.bottom)) + return false; + if (Float.floatToIntBits(left) != Float.floatToIntBits(other.left)) + return false; + if (Float.floatToIntBits(right) != Float.floatToIntBits(other.right)) + return false; + if (Float.floatToIntBits(top) != Float.floatToIntBits(other.top)) + return false; + return true; + } + + public float left; + public float top; + public float right; + public float bottom; + + /** + * Create a new empty Viewport. All coordinates are initialized to 0. + */ + public Viewport() { + } + + /** + * Create a new viewport with the specified coordinates. Note: no range checking is performed, so the caller must + * ensure that left is less than right and bottom is less than top. + * + * @param left The X coordinate of the left side of the viewport + * @param top The Y coordinate of the top of the viewport + * @param right The X coordinate of the right side of the viewport + * @param bottom The Y coordinate of the bottom of the viewport + */ + public Viewport(float left, float top, float right, float bottom) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + /** + * Create a new viewport, initialized with the values in the specified viewport (which is left unmodified). + * + * @param v The viewport whose coordinates are copied into the new viewport. + */ + public Viewport(Viewport v) { + if (v == null) { + left = top = right = bottom = 0.0f; + } else { + left = v.left; + top = v.top; + right = v.right; + bottom = v.bottom; + } + } + + /** + * Returns true if the viewport is empty {@code left >= right or bottom >= top} + */ + public final boolean isEmpty() { + return left >= right || bottom >= top; + } + + /** + * Set the viewport to (0,0,0,0) + */ + public void setEmpty() { + left = right = top = bottom = 0; + } + + /** + * @return the viewport's width. This does not check for a valid viewport (i.e. {@code left <= right}) so the + * result may be negative. + */ + public final float width() { + return right - left; + } + + /** + * @return the viewport's height. This does not check for a valid viewport (i.e. {@code top <= bottom}) so the + * result may be negative. + */ + public final float height() { + return top - bottom; + } + + /** + * @return the horizontal center of the viewport. This does not check for a valid viewport (i.e. {@code left <= + * right}) + */ + public final float centerX() { + return (left + right) * 0.5f; + } + + /** + * @return the vertical center of the viewport. This does not check for a valid viewport (i.e. {@code bottom <= + * top}) + */ + public final float centerY() { + return (top + bottom) * 0.5f; + } + + /** + * Set the viewport's coordinates to the specified values. Note: no range checking is performed, so it is up to the + * caller to ensure that {@code left <= right and bottom <= top}. + * + * @param left The X coordinate of the left side of the viewport + * @param top The Y coordinate of the top of the viewport + * @param right The X coordinate of the right side of the viewport + * @param bottom The Y coordinate of the bottom of the viewport + */ + public void set(float left, float top, float right, float bottom) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + /** + * Copy the coordinates from src into this viewport. + * + * @param src The viewport whose coordinates are copied into this viewport. + */ + public void set(Viewport src) { + this.left = src.left; + this.top = src.top; + this.right = src.right; + this.bottom = src.bottom; + } + + /** + * Offset the viewport by adding dx to its left and right coordinates, and adding dy to its top and bottom + * coordinates. + * + * @param dx The amount to add to the viewport's left and right coordinates + * @param dy The amount to add to the viewport's top and bottom coordinates + */ + public void offset(float dx, float dy) { + left += dx; + top += dy; + right += dx; + bottom += dy; + } + + /** + * Offset the viewport to a specific (left, top) position, keeping its width and height the same. + * + * @param newLeft The new "left" coordinate for the viewport + * @param newTop The new "top" coordinate for the viewport + */ + public void offsetTo(float newLeft, float newTop) { + right += newLeft - left; + bottom += newTop - top; + left = newLeft; + top = newTop; + } + + /** + * Inset the viewport by (dx,dy). If dx is positive, then the sides are moved inwards, making the viewport narrower. + * If dx is negative, then the sides are moved outwards, making the viewport wider. The same holds true for dy and + * the top and bottom. + * + * @param dx The amount to add(subtract) from the viewport's left(right) + * @param dy The amount to add(subtract) from the viewport's top(bottom) + */ + public void inset(float dx, float dy) { + left += dx; + top -= dy; + right -= dx; + bottom += dy; + } + + /** + * Returns true if (x,y) is inside the viewport. The left and top are considered to be inside, while the right and + * bottom are not. This means that for a x,y to be contained: {@code left <= x < right and bottom <= y < top}. An + * empty viewport never contains any point. + * + * @param x The X coordinate of the point being tested for containment + * @param y The Y coordinate of the point being tested for containment + * @return true iff (x,y) are contained by the viewport, where containment means {@code left <= x < right and top <= + * y < bottom} + */ + public boolean contains(float x, float y) { + return left < right && bottom < top // check for empty first + && x >= left && x < right && y >= bottom && y < top; + } + + /** + * Returns true iff the 4 specified sides of a viewport are inside or equal to this viewport. i.e. is this viewport + * a superset of the specified viewport. An empty viewport never contains another viewport. + * + * @param left The left side of the viewport being tested for containment + * @param top The top of the viewport being tested for containment + * @param right The right side of the viewport being tested for containment + * @param bottom The bottom of the viewport being tested for containment + * @return true iff the the 4 specified sides of a viewport are inside or equal to this viewport + */ + public boolean contains(float left, float top, float right, float bottom) { + // check for empty first + return this.left < this.right && this.bottom < this.top + // now check for containment + && this.left <= left && this.top >= top && this.right >= right && this.bottom <= bottom; + } + + /** + * Returns true iff the specified viewport r is inside or equal to this viewport. An empty viewport never contains + * another viewport. + * + * @param v The viewport being tested for containment. + * @return true iff the specified viewport r is inside or equal to this viewport + */ + public boolean contains(Viewport v) { + // check for empty first + return this.left < this.right && this.bottom < this.top + // now check for containment + && left <= v.left && top >= v.top && right >= v.right && bottom <= v.bottom; + } + + /** + * Update this Viewport to enclose itself and the specified viewport. If the specified viewport is empty, nothing is + * done. If this viewport is empty it is set to the specified viewport. + * + * @param left The left edge being unioned with this viewport + * @param top The top edge being unioned with this viewport + * @param right The right edge being unioned with this viewport + * @param bottom The bottom edge being unioned with this viewport + */ + public void union(float left, float top, float right, float bottom) { + if ((left < right) && (bottom < top)) { + if ((this.left < this.right) && (this.bottom < this.top)) { + if (this.left > left) + this.left = left; + if (this.top < top) + this.top = top; + if (this.right < right) + this.right = right; + if (this.bottom > bottom) + this.bottom = bottom; + } else { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + } + } + + /** + * Update this Viewport to enclose itself and the specified viewport. If the specified viewport is empty, nothing is + * done. If this viewport is empty it is set to the specified viewport. + * + * @param v The viewport being unioned with this viewport + */ + public void union(Viewport v) { + union(v.left, v.top, v.right, v.bottom); + } + + /** + * If the viewport specified by left,top,right,bottom intersects this viewport, return true and set this viewport to + * that intersection, otherwise return false and do not change this viewport. No check is performed to see if either + * viewport is empty. Note: To just test for intersection, use intersects() + * + * @param left The left side of the viewport being intersected with this viewport + * @param top The top of the viewport being intersected with this viewport + * @param right The right side of the viewport being intersected with this viewport. + * @param bottom The bottom of the viewport being intersected with this viewport. + * @return true if the specified viewport and this viewport intersect (and this viewport is then set to that + * intersection) else return false and do not change this viewport. + */ + public boolean intersect(float left, float top, float right, float bottom) { + if (this.left < right && left < this.right && this.bottom < top && bottom < this.top) { + if (this.left < left) { + this.left = left; + } + if (this.top > top) { + this.top = top; + } + if (this.right > right) { + this.right = right; + } + if (this.bottom < bottom) { + this.bottom = bottom; + } + return true; + } + return false; + } + + /** + * If the specified viewport intersects this viewport, return true and set this viewport to that intersection, + * otherwise return false and do not change this viewport. No check is performed to see if either viewport is empty. + * To just test for intersection, use intersects() + * + * @param v The viewport being intersected with this viewport. + * @return true if the specified viewport and this viewport intersect (and this viewport is then set to that + * intersection) else return false and do not change this viewport. + */ + public boolean intersect(Viewport v) { + return intersect(v.left, v.top, v.right, v.bottom); + } + + @Override + public String toString() { + return "Viewport [left=" + left + ", top=" + top + ", right=" + right + ", bottom=" + bottom + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Float.floatToIntBits(bottom); + result = prime * result + Float.floatToIntBits(left); + result = prime * result + Float.floatToIntBits(right); + result = prime * result + Float.floatToIntBits(top); + return result; + } + + // ** PARCERABLE ** + + /** + * Parcelable interface methods + */ + public int describeContents() { + return 0; + } + + /** + * Write this viewport to the specified parcel. To restore a viewport from a parcel, use readFromParcel() + * + * @param out The parcel to write the viewport's coordinates into + */ + public void writeToParcel(Parcel out, int flags) { + out.writeFloat(left); + out.writeFloat(top); + out.writeFloat(right); + out.writeFloat(bottom); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + /** + * Return a new viewport from the data in the specified parcel. + */ + public Viewport createFromParcel(Parcel in) { + Viewport v = new Viewport(); + v.readFromParcel(in); + return v; + } + + /** + * Return an array of viewports of the specified size. + */ + public Viewport[] newArray(int size) { + return new Viewport[size]; + } + }; + + /** + * Set the viewport's coordinates from the data stored in the specified parcel. To write a viewport to a parcel, + * call writeToParcel(). + * + * @param in The parcel to read the viewport's coordinates from + */ + public void readFromParcel(Parcel in) { + left = in.readFloat(); + top = in.readFloat(); + right = in.readFloat(); + bottom = in.readFloat(); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/provider/BubbleChartDataProvider.java b/hellocharts-library/src/lecho/lib/hellocharts/provider/BubbleChartDataProvider.java index aeed66b5..9a5b1885 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/provider/BubbleChartDataProvider.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/provider/BubbleChartDataProvider.java @@ -4,8 +4,8 @@ public interface BubbleChartDataProvider { - public BubbleChartData getBubbleChartData(); + public BubbleChartData getBubbleChartData(); - public void setBubbleChartData(BubbleChartData data); + public void setBubbleChartData(BubbleChartData data); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/provider/ColumnChartDataProvider.java b/hellocharts-library/src/lecho/lib/hellocharts/provider/ColumnChartDataProvider.java index 634c0c24..36f54cbb 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/provider/ColumnChartDataProvider.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/provider/ColumnChartDataProvider.java @@ -4,8 +4,8 @@ public interface ColumnChartDataProvider { - public ColumnChartData getColumnChartData(); + public ColumnChartData getColumnChartData(); - public void setColumnChartData(ColumnChartData data); + public void setColumnChartData(ColumnChartData data); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/provider/ComboLineColumnChartDataProvider.java b/hellocharts-library/src/lecho/lib/hellocharts/provider/ComboLineColumnChartDataProvider.java index bb170913..7af85b1d 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/provider/ComboLineColumnChartDataProvider.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/provider/ComboLineColumnChartDataProvider.java @@ -4,8 +4,8 @@ public interface ComboLineColumnChartDataProvider { - public ComboLineColumnChartData getComboLineColumnChartData(); + public ComboLineColumnChartData getComboLineColumnChartData(); - public void setComboLineColumnChartData(ComboLineColumnChartData data); + public void setComboLineColumnChartData(ComboLineColumnChartData data); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/provider/LineChartDataProvider.java b/hellocharts-library/src/lecho/lib/hellocharts/provider/LineChartDataProvider.java index 3a372aa0..6d3ab77a 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/provider/LineChartDataProvider.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/provider/LineChartDataProvider.java @@ -4,8 +4,8 @@ public interface LineChartDataProvider { - public LineChartData getLineChartData(); + public LineChartData getLineChartData(); - public void setLineChartData(LineChartData data); + public void setLineChartData(LineChartData data); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/provider/PieChartDataProvider.java b/hellocharts-library/src/lecho/lib/hellocharts/provider/PieChartDataProvider.java index 93a16b5d..1acda061 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/provider/PieChartDataProvider.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/provider/PieChartDataProvider.java @@ -4,8 +4,8 @@ public interface PieChartDataProvider { - public PieChartData getPieChartData(); + public PieChartData getPieChartData(); - public void setPieChartData(PieChartData data); + public void setPieChartData(PieChartData data); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/renderer/AbstractChartRenderer.java b/hellocharts-library/src/lecho/lib/hellocharts/renderer/AbstractChartRenderer.java index 285f2a78..aab0ff64 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/renderer/AbstractChartRenderer.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/renderer/AbstractChartRenderer.java @@ -20,161 +20,161 @@ * Abstract renderer implementation, every chart renderer extends this class(although it is not required it helps). */ public abstract class AbstractChartRenderer implements ChartRenderer { - public int DEFAULT_LABEL_MARGIN_DP = 4; - protected Chart chart; - protected ChartComputator computator; - /** - * Paint for value labels. - */ - protected Paint labelPaint = new Paint(); - /** - * Paint for labels background. - */ - protected Paint labelBackgroundPaint = new Paint(); - /** - * Holds coordinates for label background rect. - */ - protected RectF labelBackgroundRect = new RectF(); - /** - * Font metrics for label paint, used to determine text height. - */ - protected FontMetricsInt fontMetrics = new FontMetricsInt(); - /** - * If true maximum and current viewport will be calculated when chart data change or during data animations. - */ - protected boolean isViewportCalculationEnabled = true; - protected float density; - protected float scaledDensity; - protected SelectedValue selectedValue = new SelectedValue(); - protected char[] labelBuffer = new char[32]; - protected int labelOffset; - protected int labelMargin; - protected boolean isValueLabelBackgroundEnabled; - protected boolean isValueLabelBackgroundAuto; - - public AbstractChartRenderer(Context context, Chart chart) { - this.density = context.getResources().getDisplayMetrics().density; - this.scaledDensity = context.getResources().getDisplayMetrics().scaledDensity; - this.chart = chart; - this.computator = chart.getChartComputator(); - - labelMargin = ChartUtils.dp2px(density, DEFAULT_LABEL_MARGIN_DP); - labelOffset = labelMargin; - - labelPaint.setAntiAlias(true); - labelPaint.setStyle(Paint.Style.FILL); - labelPaint.setTextAlign(Align.LEFT); - labelPaint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); - labelPaint.setColor(Color.WHITE); - - labelBackgroundPaint.setAntiAlias(true); - labelBackgroundPaint.setStyle(Paint.Style.FILL); - } - - @Override - public void resetRenderer(){ - this.computator = chart.getChartComputator(); - } - - @Override - public void onChartDataChanged() { - final ChartData data = chart.getChartData(); - - Typeface typeface = chart.getChartData().getValueLabelTypeface(); - if (null != typeface) { - labelPaint.setTypeface(typeface); - } + public int DEFAULT_LABEL_MARGIN_DP = 4; + protected Chart chart; + protected ChartComputator computator; + /** + * Paint for value labels. + */ + protected Paint labelPaint = new Paint(); + /** + * Paint for labels background. + */ + protected Paint labelBackgroundPaint = new Paint(); + /** + * Holds coordinates for label background rect. + */ + protected RectF labelBackgroundRect = new RectF(); + /** + * Font metrics for label paint, used to determine text height. + */ + protected FontMetricsInt fontMetrics = new FontMetricsInt(); + /** + * If true maximum and current viewport will be calculated when chart data change or during data animations. + */ + protected boolean isViewportCalculationEnabled = true; + protected float density; + protected float scaledDensity; + protected SelectedValue selectedValue = new SelectedValue(); + protected char[] labelBuffer = new char[32]; + protected int labelOffset; + protected int labelMargin; + protected boolean isValueLabelBackgroundEnabled; + protected boolean isValueLabelBackgroundAuto; + + public AbstractChartRenderer(Context context, Chart chart) { + this.density = context.getResources().getDisplayMetrics().density; + this.scaledDensity = context.getResources().getDisplayMetrics().scaledDensity; + this.chart = chart; + this.computator = chart.getChartComputator(); + + labelMargin = ChartUtils.dp2px(density, DEFAULT_LABEL_MARGIN_DP); + labelOffset = labelMargin; + + labelPaint.setAntiAlias(true); + labelPaint.setStyle(Paint.Style.FILL); + labelPaint.setTextAlign(Align.LEFT); + labelPaint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); + labelPaint.setColor(Color.WHITE); + + labelBackgroundPaint.setAntiAlias(true); + labelBackgroundPaint.setStyle(Paint.Style.FILL); + } + + @Override + public void resetRenderer() { + this.computator = chart.getChartComputator(); + } + + @Override + public void onChartDataChanged() { + final ChartData data = chart.getChartData(); + + Typeface typeface = chart.getChartData().getValueLabelTypeface(); + if (null != typeface) { + labelPaint.setTypeface(typeface); + } labelPaint.setColor(data.getValueLabelTextColor()); - labelPaint.setTextSize(ChartUtils.sp2px(scaledDensity, data.getValueLabelTextSize())); - labelPaint.getFontMetricsInt(fontMetrics); + labelPaint.setTextSize(ChartUtils.sp2px(scaledDensity, data.getValueLabelTextSize())); + labelPaint.getFontMetricsInt(fontMetrics); this.isValueLabelBackgroundEnabled = data.isValueLabelBackgroundEnabled(); this.isValueLabelBackgroundAuto = data.isValueLabelBackgroundAuto(); this.labelBackgroundPaint.setColor(data.getValueLabelBackgroundColor()); - // Important - clear selection when data changed. - selectedValue.clear(); - - } - - /** - * Draws label text and label background if isValueLabelBackgroundEnabled is true. - */ - protected void drawLabelTextAndBackground(Canvas canvas, char[] labelBuffer, int startIndex, int numChars, - int autoBackgroundColor) { - final float textX; - final float textY; - - if (isValueLabelBackgroundEnabled) { - - if (isValueLabelBackgroundAuto) { - labelBackgroundPaint.setColor(autoBackgroundColor); - } - - canvas.drawRect(labelBackgroundRect, labelBackgroundPaint); - - textX = labelBackgroundRect.left + labelMargin; - textY = labelBackgroundRect.bottom - labelMargin; - } else { - textX = labelBackgroundRect.left; - textY = labelBackgroundRect.bottom; - } - - canvas.drawText(labelBuffer, startIndex, numChars, textX, textY, labelPaint); - } - - @Override - public boolean isTouched() { - return selectedValue.isSet(); - } - - @Override - public void clearTouch() { - selectedValue.clear(); - } - - @Override - public Viewport getMaximumViewport() { - return computator.getMaximumViewport(); - } - - @Override - public void setMaximumViewport(Viewport maxViewport) { - if (null != maxViewport) { - computator.setMaxViewport(maxViewport); - } - } - - @Override - public Viewport getCurrentViewport() { - return computator.getCurrentViewport(); - } - - @Override - public void setCurrentViewport(Viewport viewport) { - if (null != viewport) { - computator.setCurrentViewport(viewport); - } - } - - @Override - public boolean isViewportCalculationEnabled() { - return isViewportCalculationEnabled; - } - - @Override - public void setViewportCalculationEnabled(boolean isEnabled) { - this.isViewportCalculationEnabled = isEnabled; - } - - @Override - public void selectValue(SelectedValue selectedValue) { - this.selectedValue.set(selectedValue); - } - - @Override - public SelectedValue getSelectedValue() { - return selectedValue; - } + // Important - clear selection when data changed. + selectedValue.clear(); + + } + + /** + * Draws label text and label background if isValueLabelBackgroundEnabled is true. + */ + protected void drawLabelTextAndBackground(Canvas canvas, char[] labelBuffer, int startIndex, int numChars, + int autoBackgroundColor) { + final float textX; + final float textY; + + if (isValueLabelBackgroundEnabled) { + + if (isValueLabelBackgroundAuto) { + labelBackgroundPaint.setColor(autoBackgroundColor); + } + + canvas.drawRect(labelBackgroundRect, labelBackgroundPaint); + + textX = labelBackgroundRect.left + labelMargin; + textY = labelBackgroundRect.bottom - labelMargin; + } else { + textX = labelBackgroundRect.left; + textY = labelBackgroundRect.bottom; + } + + canvas.drawText(labelBuffer, startIndex, numChars, textX, textY, labelPaint); + } + + @Override + public boolean isTouched() { + return selectedValue.isSet(); + } + + @Override + public void clearTouch() { + selectedValue.clear(); + } + + @Override + public Viewport getMaximumViewport() { + return computator.getMaximumViewport(); + } + + @Override + public void setMaximumViewport(Viewport maxViewport) { + if (null != maxViewport) { + computator.setMaxViewport(maxViewport); + } + } + + @Override + public Viewport getCurrentViewport() { + return computator.getCurrentViewport(); + } + + @Override + public void setCurrentViewport(Viewport viewport) { + if (null != viewport) { + computator.setCurrentViewport(viewport); + } + } + + @Override + public boolean isViewportCalculationEnabled() { + return isViewportCalculationEnabled; + } + + @Override + public void setViewportCalculationEnabled(boolean isEnabled) { + this.isViewportCalculationEnabled = isEnabled; + } + + @Override + public void selectValue(SelectedValue selectedValue) { + this.selectedValue.set(selectedValue); + } + + @Override + public SelectedValue getSelectedValue() { + return selectedValue; + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/renderer/BubbleChartRenderer.java b/hellocharts-library/src/lecho/lib/hellocharts/renderer/BubbleChartRenderer.java index c60b6943..4946178d 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/renderer/BubbleChartRenderer.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/renderer/BubbleChartRenderer.java @@ -19,333 +19,333 @@ import lecho.lib.hellocharts.view.Chart; public class BubbleChartRenderer extends AbstractChartRenderer { - private static final int DEFAULT_TOUCH_ADDITIONAL_DP = 4; - private static final int MODE_DRAW = 0; - private static final int MODE_HIGHLIGHT = 1; - - private BubbleChartDataProvider dataProvider; - - /** - * Additional value added to bubble radius when drawing highlighted bubble, used to give tauch feedback. - */ - private int touchAdditional; - - /** - * Scales for bubble radius value, only one is used depending on screen orientation; - */ - private float bubbleScaleX; - private float bubbleScaleY; - - /** - * True if bubbleScale = bubbleScaleX so the renderer should used {@link ChartComputator#computeRawDistanceX(float)} - * , if false bubbleScale = bubbleScaleY and renderer should use - * {@link ChartComputator#computeRawDistanceY(float)}. - */ - private boolean isBubbleScaledByX = true; - - /** - * Maximum bubble radius. - */ - private float maxRadius; - - /** - * Minimal bubble radius in pixels. - */ - private float minRawRadius; - private PointF bubbleCenter = new PointF(); - private Paint bubblePaint = new Paint(); - - /** - * Rect used for drawing bubbles with SHAPE_SQUARE. - */ - private RectF bubbleRect = new RectF(); - - private boolean hasLabels; - private boolean hasLabelsOnlyForSelected; - private BubbleChartValueFormatter valueFormatter; - private Viewport tempMaximumViewport = new Viewport(); - - public BubbleChartRenderer(Context context, Chart chart, BubbleChartDataProvider dataProvider) { - super(context, chart); - this.dataProvider = dataProvider; - - touchAdditional = ChartUtils.dp2px(density, DEFAULT_TOUCH_ADDITIONAL_DP); - - bubblePaint.setAntiAlias(true); - bubblePaint.setStyle(Paint.Style.FILL); - - } - - @Override - public void onChartSizeChanged(){ - final ChartComputator computator = chart.getChartComputator(); - Rect contentRect = computator.getContentRectMinusAllMargins(); - if (contentRect.width() < contentRect.height()) { - isBubbleScaledByX = true; - } else { - isBubbleScaledByX = false; - } - } - - @Override - public void onChartDataChanged(){ - super.onChartDataChanged(); - BubbleChartData data = dataProvider.getBubbleChartData(); - this.hasLabels = data.hasLabels(); - this.hasLabelsOnlyForSelected = data.hasLabelsOnlyForSelected(); - this.valueFormatter = data.getFormatter(); - - onChartViewportChanged(); - } - - @Override - public void onChartViewportChanged(){ - if (isViewportCalculationEnabled) { - calculateMaxViewport(); - computator.setMaxViewport(tempMaximumViewport); - computator.setCurrentViewport(computator.getMaximumViewport()); - } - } - - @Override - public void draw(Canvas canvas) { - drawBubbles(canvas); - if (isTouched()) { - highlightBubbles(canvas); - } - } - - @Override - public void drawUnclipped(Canvas canvas) { - } - - @Override - public boolean checkTouch(float touchX, float touchY) { - selectedValue.clear(); - final BubbleChartData data = dataProvider.getBubbleChartData(); - int valueIndex = 0; - for (BubbleValue bubbleValue : data.getValues()) { - float rawRadius = processBubble(bubbleValue, bubbleCenter); - - if (ValueShape.SQUARE.equals(bubbleValue.getShape())) { - if (bubbleRect.contains(touchX, touchY)) { - selectedValue.set(valueIndex, valueIndex, SelectedValueType.NONE); - } - } else if (ValueShape.CIRCLE.equals(bubbleValue.getShape())) { - final float diffX = touchX - bubbleCenter.x; - final float diffY = touchY - bubbleCenter.y; - final float touchDistance = (float) Math.sqrt((diffX * diffX) + (diffY * diffY)); - - if (touchDistance <= rawRadius) { - selectedValue.set(valueIndex, valueIndex, SelectedValueType.NONE); - } - } else { - throw new IllegalArgumentException("Invalid bubble shape: " + bubbleValue.getShape()); - } - - ++valueIndex; - } - - return isTouched(); - } - - /** - * Removes empty spaces on sides of chart(left-right for landscape, top-bottom for portrait). *This method should be - * called after layout had been drawn*. Because most often chart is drawn as rectangle with proportions other than - * 1:1 and bubbles have to be drawn as circles not ellipses I am unable to calculate correct margins based on chart - * data only. I need to know chart dimension to remove extra empty spaces, that bad because viewport depends a - * little on contentRectMinusAllMargins. - */ - public void removeMargins() { - final Rect contentRect = computator.getContentRectMinusAllMargins(); - if (contentRect.height() == 0 || contentRect.width() == 0) { - // View probably not yet measured, skip removing margins. - return; - } - final float pxX = computator.computeRawDistanceX(maxRadius * bubbleScaleX); - final float pxY = computator.computeRawDistanceY(maxRadius * bubbleScaleY); - final float scaleX = computator.getMaximumViewport().width() / contentRect.width(); - final float scaleY = computator.getMaximumViewport().height() / contentRect.height(); - float dx = 0; - float dy = 0; - if (isBubbleScaledByX) { - dy = (pxY - pxX) * scaleY * 0.75f; - } else { - dx = (pxX - pxY) * scaleX * 0.75f; - } - - Viewport maxViewport = computator.getMaximumViewport(); - maxViewport.inset(dx, dy); - Viewport currentViewport = computator.getCurrentViewport(); - currentViewport.inset(dx, dy); - computator.setMaxViewport(maxViewport); - computator.setCurrentViewport(currentViewport); - } - - private void drawBubbles(Canvas canvas) { - final BubbleChartData data = dataProvider.getBubbleChartData(); - for (BubbleValue bubbleValue : data.getValues()) { - drawBubble(canvas, bubbleValue); - } - } - - private void drawBubble(Canvas canvas, BubbleValue bubbleValue) { - float rawRadius = processBubble(bubbleValue, bubbleCenter); - // Not touched bubbles are a little smaller than touched to give user touch feedback. - rawRadius -= touchAdditional; - bubbleRect.inset(touchAdditional, touchAdditional); - bubblePaint.setColor(bubbleValue.getColor()); - drawBubbleShapeAndLabel(canvas, bubbleValue, rawRadius, MODE_DRAW); - - } - - private void drawBubbleShapeAndLabel(Canvas canvas, BubbleValue bubbleValue, float rawRadius, int mode) { - if (ValueShape.SQUARE.equals(bubbleValue.getShape())) { - canvas.drawRect(bubbleRect, bubblePaint); - } else if (ValueShape.CIRCLE.equals(bubbleValue.getShape())) { - canvas.drawCircle(bubbleCenter.x, bubbleCenter.y, rawRadius, bubblePaint); - } else { - throw new IllegalArgumentException("Invalid bubble shape: " + bubbleValue.getShape()); - } - - if (MODE_HIGHLIGHT == mode) { - if (hasLabels || hasLabelsOnlyForSelected) { - drawLabel(canvas, bubbleValue, bubbleCenter.x, bubbleCenter.y); - } - } else if (MODE_DRAW == mode) { - if (hasLabels) { - drawLabel(canvas, bubbleValue, bubbleCenter.x, bubbleCenter.y); - } - } else { - throw new IllegalStateException("Cannot process bubble in mode: " + mode); - } - } - - private void highlightBubbles(Canvas canvas) { - final BubbleChartData data = dataProvider.getBubbleChartData(); - BubbleValue bubbleValue = data.getValues().get(selectedValue.getFirstIndex()); - highlightBubble(canvas, bubbleValue); - } - - private void highlightBubble(Canvas canvas, BubbleValue bubbleValue) { - float rawRadius = processBubble(bubbleValue, bubbleCenter); - bubblePaint.setColor(bubbleValue.getDarkenColor()); - drawBubbleShapeAndLabel(canvas, bubbleValue, rawRadius, MODE_HIGHLIGHT); - } - - /** - * Calculate bubble radius and center x and y coordinates. Center x and x will be stored in point parameter, radius - * will be returned as float value. - */ - private float processBubble(BubbleValue bubbleValue, PointF point) { - final float rawX = computator.computeRawX(bubbleValue.getX()); - final float rawY = computator.computeRawY(bubbleValue.getY()); - float radius = (float) Math.sqrt(Math.abs(bubbleValue.getZ()) / Math.PI); - float rawRadius; - if (isBubbleScaledByX) { - radius *= bubbleScaleX; - rawRadius = computator.computeRawDistanceX(radius); - } else { - radius *= bubbleScaleY; - rawRadius = computator.computeRawDistanceY(radius); - } - - if (rawRadius < minRawRadius + touchAdditional) { - rawRadius = minRawRadius + touchAdditional; - } - - bubbleCenter.set(rawX, rawY); - if (ValueShape.SQUARE.equals(bubbleValue.getShape())) { - bubbleRect.set(rawX - rawRadius, rawY - rawRadius, rawX + rawRadius, rawY + rawRadius); - } - return rawRadius; - } - - private void drawLabel(Canvas canvas, BubbleValue bubbleValue, float rawX, float rawY) { - final Rect contentRect = computator.getContentRectMinusAllMargins(); - final int numChars = valueFormatter.formatChartValue(labelBuffer, bubbleValue); - - if (numChars == 0) { - // No need to draw empty label - return; - } - - final float labelWidth = labelPaint.measureText(labelBuffer, labelBuffer.length - numChars, numChars); - final int labelHeight = Math.abs(fontMetrics.ascent); - float left = rawX - labelWidth / 2 - labelMargin; - float right = rawX + labelWidth / 2 + labelMargin; - float top = rawY - labelHeight / 2 - labelMargin; - float bottom = rawY + labelHeight / 2 + labelMargin; - - if (top < contentRect.top) { - top = rawY; - bottom = rawY + labelHeight + labelMargin * 2; - } - if (bottom > contentRect.bottom) { - top = rawY - labelHeight - labelMargin * 2; - bottom = rawY; - } - if (left < contentRect.left) { - left = rawX; - right = rawX + labelWidth + labelMargin * 2; - } - if (right > contentRect.right) { - left = rawX - labelWidth - labelMargin * 2; - right = rawX; - } - - labelBackgroundRect.set(left, top, right, bottom); - drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length - numChars, numChars, - bubbleValue.getDarkenColor()); - - } - - private void calculateMaxViewport() { - float maxZ = Float.MIN_VALUE; - tempMaximumViewport.set(Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MAX_VALUE); - BubbleChartData data = dataProvider.getBubbleChartData(); - // TODO: Optimize. - for (BubbleValue bubbleValue : data.getValues()) { - if (Math.abs(bubbleValue.getZ()) > maxZ) { - maxZ = Math.abs(bubbleValue.getZ()); - } - if (bubbleValue.getX() < tempMaximumViewport.left) { - tempMaximumViewport.left = bubbleValue.getX(); - } - if (bubbleValue.getX() > tempMaximumViewport.right) { - tempMaximumViewport.right = bubbleValue.getX(); - } - if (bubbleValue.getY() < tempMaximumViewport.bottom) { - tempMaximumViewport.bottom = bubbleValue.getY(); - } - if (bubbleValue.getY() > tempMaximumViewport.top) { - tempMaximumViewport.top = bubbleValue.getY(); - } - } - - maxRadius = (float) Math.sqrt(maxZ / Math.PI); - - // Number 4 is determined by trials and errors method, no magic behind it:). - bubbleScaleX = tempMaximumViewport.width() / (maxRadius * 4); - if (bubbleScaleX == 0) { - // case for 0 viewport width. - bubbleScaleX = 1; - } - - bubbleScaleY = tempMaximumViewport.height() / (maxRadius * 4); - if (bubbleScaleY == 0) { - // case for 0 viewport height. - bubbleScaleY = 1; - } - - // For cases when user sets different than 1 bubble scale in BubbleChartData. - bubbleScaleX *= data.getBubbleScale(); - bubbleScaleY *= data.getBubbleScale(); - - // Prevent cutting of bubbles on the edges of chart area. - tempMaximumViewport.inset(-maxRadius * bubbleScaleX, -maxRadius * bubbleScaleY); - - minRawRadius = ChartUtils.dp2px(density, dataProvider.getBubbleChartData().getMinBubbleRadius()); - } + private static final int DEFAULT_TOUCH_ADDITIONAL_DP = 4; + private static final int MODE_DRAW = 0; + private static final int MODE_HIGHLIGHT = 1; + + private BubbleChartDataProvider dataProvider; + + /** + * Additional value added to bubble radius when drawing highlighted bubble, used to give tauch feedback. + */ + private int touchAdditional; + + /** + * Scales for bubble radius value, only one is used depending on screen orientation; + */ + private float bubbleScaleX; + private float bubbleScaleY; + + /** + * True if bubbleScale = bubbleScaleX so the renderer should used {@link ChartComputator#computeRawDistanceX(float)} + * , if false bubbleScale = bubbleScaleY and renderer should use + * {@link ChartComputator#computeRawDistanceY(float)}. + */ + private boolean isBubbleScaledByX = true; + + /** + * Maximum bubble radius. + */ + private float maxRadius; + + /** + * Minimal bubble radius in pixels. + */ + private float minRawRadius; + private PointF bubbleCenter = new PointF(); + private Paint bubblePaint = new Paint(); + + /** + * Rect used for drawing bubbles with SHAPE_SQUARE. + */ + private RectF bubbleRect = new RectF(); + + private boolean hasLabels; + private boolean hasLabelsOnlyForSelected; + private BubbleChartValueFormatter valueFormatter; + private Viewport tempMaximumViewport = new Viewport(); + + public BubbleChartRenderer(Context context, Chart chart, BubbleChartDataProvider dataProvider) { + super(context, chart); + this.dataProvider = dataProvider; + + touchAdditional = ChartUtils.dp2px(density, DEFAULT_TOUCH_ADDITIONAL_DP); + + bubblePaint.setAntiAlias(true); + bubblePaint.setStyle(Paint.Style.FILL); + + } + + @Override + public void onChartSizeChanged() { + final ChartComputator computator = chart.getChartComputator(); + Rect contentRect = computator.getContentRectMinusAllMargins(); + if (contentRect.width() < contentRect.height()) { + isBubbleScaledByX = true; + } else { + isBubbleScaledByX = false; + } + } + + @Override + public void onChartDataChanged() { + super.onChartDataChanged(); + BubbleChartData data = dataProvider.getBubbleChartData(); + this.hasLabels = data.hasLabels(); + this.hasLabelsOnlyForSelected = data.hasLabelsOnlyForSelected(); + this.valueFormatter = data.getFormatter(); + + onChartViewportChanged(); + } + + @Override + public void onChartViewportChanged() { + if (isViewportCalculationEnabled) { + calculateMaxViewport(); + computator.setMaxViewport(tempMaximumViewport); + computator.setCurrentViewport(computator.getMaximumViewport()); + } + } + + @Override + public void draw(Canvas canvas) { + drawBubbles(canvas); + if (isTouched()) { + highlightBubbles(canvas); + } + } + + @Override + public void drawUnclipped(Canvas canvas) { + } + + @Override + public boolean checkTouch(float touchX, float touchY) { + selectedValue.clear(); + final BubbleChartData data = dataProvider.getBubbleChartData(); + int valueIndex = 0; + for (BubbleValue bubbleValue : data.getValues()) { + float rawRadius = processBubble(bubbleValue, bubbleCenter); + + if (ValueShape.SQUARE.equals(bubbleValue.getShape())) { + if (bubbleRect.contains(touchX, touchY)) { + selectedValue.set(valueIndex, valueIndex, SelectedValueType.NONE); + } + } else if (ValueShape.CIRCLE.equals(bubbleValue.getShape())) { + final float diffX = touchX - bubbleCenter.x; + final float diffY = touchY - bubbleCenter.y; + final float touchDistance = (float) Math.sqrt((diffX * diffX) + (diffY * diffY)); + + if (touchDistance <= rawRadius) { + selectedValue.set(valueIndex, valueIndex, SelectedValueType.NONE); + } + } else { + throw new IllegalArgumentException("Invalid bubble shape: " + bubbleValue.getShape()); + } + + ++valueIndex; + } + + return isTouched(); + } + + /** + * Removes empty spaces on sides of chart(left-right for landscape, top-bottom for portrait). *This method should be + * called after layout had been drawn*. Because most often chart is drawn as rectangle with proportions other than + * 1:1 and bubbles have to be drawn as circles not ellipses I am unable to calculate correct margins based on chart + * data only. I need to know chart dimension to remove extra empty spaces, that bad because viewport depends a + * little on contentRectMinusAllMargins. + */ + public void removeMargins() { + final Rect contentRect = computator.getContentRectMinusAllMargins(); + if (contentRect.height() == 0 || contentRect.width() == 0) { + // View probably not yet measured, skip removing margins. + return; + } + final float pxX = computator.computeRawDistanceX(maxRadius * bubbleScaleX); + final float pxY = computator.computeRawDistanceY(maxRadius * bubbleScaleY); + final float scaleX = computator.getMaximumViewport().width() / contentRect.width(); + final float scaleY = computator.getMaximumViewport().height() / contentRect.height(); + float dx = 0; + float dy = 0; + if (isBubbleScaledByX) { + dy = (pxY - pxX) * scaleY * 0.75f; + } else { + dx = (pxX - pxY) * scaleX * 0.75f; + } + + Viewport maxViewport = computator.getMaximumViewport(); + maxViewport.inset(dx, dy); + Viewport currentViewport = computator.getCurrentViewport(); + currentViewport.inset(dx, dy); + computator.setMaxViewport(maxViewport); + computator.setCurrentViewport(currentViewport); + } + + private void drawBubbles(Canvas canvas) { + final BubbleChartData data = dataProvider.getBubbleChartData(); + for (BubbleValue bubbleValue : data.getValues()) { + drawBubble(canvas, bubbleValue); + } + } + + private void drawBubble(Canvas canvas, BubbleValue bubbleValue) { + float rawRadius = processBubble(bubbleValue, bubbleCenter); + // Not touched bubbles are a little smaller than touched to give user touch feedback. + rawRadius -= touchAdditional; + bubbleRect.inset(touchAdditional, touchAdditional); + bubblePaint.setColor(bubbleValue.getColor()); + drawBubbleShapeAndLabel(canvas, bubbleValue, rawRadius, MODE_DRAW); + + } + + private void drawBubbleShapeAndLabel(Canvas canvas, BubbleValue bubbleValue, float rawRadius, int mode) { + if (ValueShape.SQUARE.equals(bubbleValue.getShape())) { + canvas.drawRect(bubbleRect, bubblePaint); + } else if (ValueShape.CIRCLE.equals(bubbleValue.getShape())) { + canvas.drawCircle(bubbleCenter.x, bubbleCenter.y, rawRadius, bubblePaint); + } else { + throw new IllegalArgumentException("Invalid bubble shape: " + bubbleValue.getShape()); + } + + if (MODE_HIGHLIGHT == mode) { + if (hasLabels || hasLabelsOnlyForSelected) { + drawLabel(canvas, bubbleValue, bubbleCenter.x, bubbleCenter.y); + } + } else if (MODE_DRAW == mode) { + if (hasLabels) { + drawLabel(canvas, bubbleValue, bubbleCenter.x, bubbleCenter.y); + } + } else { + throw new IllegalStateException("Cannot process bubble in mode: " + mode); + } + } + + private void highlightBubbles(Canvas canvas) { + final BubbleChartData data = dataProvider.getBubbleChartData(); + BubbleValue bubbleValue = data.getValues().get(selectedValue.getFirstIndex()); + highlightBubble(canvas, bubbleValue); + } + + private void highlightBubble(Canvas canvas, BubbleValue bubbleValue) { + float rawRadius = processBubble(bubbleValue, bubbleCenter); + bubblePaint.setColor(bubbleValue.getDarkenColor()); + drawBubbleShapeAndLabel(canvas, bubbleValue, rawRadius, MODE_HIGHLIGHT); + } + + /** + * Calculate bubble radius and center x and y coordinates. Center x and x will be stored in point parameter, radius + * will be returned as float value. + */ + private float processBubble(BubbleValue bubbleValue, PointF point) { + final float rawX = computator.computeRawX(bubbleValue.getX()); + final float rawY = computator.computeRawY(bubbleValue.getY()); + float radius = (float) Math.sqrt(Math.abs(bubbleValue.getZ()) / Math.PI); + float rawRadius; + if (isBubbleScaledByX) { + radius *= bubbleScaleX; + rawRadius = computator.computeRawDistanceX(radius); + } else { + radius *= bubbleScaleY; + rawRadius = computator.computeRawDistanceY(radius); + } + + if (rawRadius < minRawRadius + touchAdditional) { + rawRadius = minRawRadius + touchAdditional; + } + + bubbleCenter.set(rawX, rawY); + if (ValueShape.SQUARE.equals(bubbleValue.getShape())) { + bubbleRect.set(rawX - rawRadius, rawY - rawRadius, rawX + rawRadius, rawY + rawRadius); + } + return rawRadius; + } + + private void drawLabel(Canvas canvas, BubbleValue bubbleValue, float rawX, float rawY) { + final Rect contentRect = computator.getContentRectMinusAllMargins(); + final int numChars = valueFormatter.formatChartValue(labelBuffer, bubbleValue); + + if (numChars == 0) { + // No need to draw empty label + return; + } + + final float labelWidth = labelPaint.measureText(labelBuffer, labelBuffer.length - numChars, numChars); + final int labelHeight = Math.abs(fontMetrics.ascent); + float left = rawX - labelWidth / 2 - labelMargin; + float right = rawX + labelWidth / 2 + labelMargin; + float top = rawY - labelHeight / 2 - labelMargin; + float bottom = rawY + labelHeight / 2 + labelMargin; + + if (top < contentRect.top) { + top = rawY; + bottom = rawY + labelHeight + labelMargin * 2; + } + if (bottom > contentRect.bottom) { + top = rawY - labelHeight - labelMargin * 2; + bottom = rawY; + } + if (left < contentRect.left) { + left = rawX; + right = rawX + labelWidth + labelMargin * 2; + } + if (right > contentRect.right) { + left = rawX - labelWidth - labelMargin * 2; + right = rawX; + } + + labelBackgroundRect.set(left, top, right, bottom); + drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length - numChars, numChars, + bubbleValue.getDarkenColor()); + + } + + private void calculateMaxViewport() { + float maxZ = Float.MIN_VALUE; + tempMaximumViewport.set(Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MAX_VALUE); + BubbleChartData data = dataProvider.getBubbleChartData(); + // TODO: Optimize. + for (BubbleValue bubbleValue : data.getValues()) { + if (Math.abs(bubbleValue.getZ()) > maxZ) { + maxZ = Math.abs(bubbleValue.getZ()); + } + if (bubbleValue.getX() < tempMaximumViewport.left) { + tempMaximumViewport.left = bubbleValue.getX(); + } + if (bubbleValue.getX() > tempMaximumViewport.right) { + tempMaximumViewport.right = bubbleValue.getX(); + } + if (bubbleValue.getY() < tempMaximumViewport.bottom) { + tempMaximumViewport.bottom = bubbleValue.getY(); + } + if (bubbleValue.getY() > tempMaximumViewport.top) { + tempMaximumViewport.top = bubbleValue.getY(); + } + } + + maxRadius = (float) Math.sqrt(maxZ / Math.PI); + + // Number 4 is determined by trials and errors method, no magic behind it:). + bubbleScaleX = tempMaximumViewport.width() / (maxRadius * 4); + if (bubbleScaleX == 0) { + // case for 0 viewport width. + bubbleScaleX = 1; + } + + bubbleScaleY = tempMaximumViewport.height() / (maxRadius * 4); + if (bubbleScaleY == 0) { + // case for 0 viewport height. + bubbleScaleY = 1; + } + + // For cases when user sets different than 1 bubble scale in BubbleChartData. + bubbleScaleX *= data.getBubbleScale(); + bubbleScaleY *= data.getBubbleScale(); + + // Prevent cutting of bubbles on the edges of chart area. + tempMaximumViewport.inset(-maxRadius * bubbleScaleX, -maxRadius * bubbleScaleY); + + minRawRadius = ChartUtils.dp2px(density, dataProvider.getBubbleChartData().getMinBubbleRadius()); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/renderer/ChartRenderer.java b/hellocharts-library/src/lecho/lib/hellocharts/renderer/ChartRenderer.java index 46db3c87..7340f5a9 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/renderer/ChartRenderer.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/renderer/ChartRenderer.java @@ -1,62 +1,63 @@ package lecho.lib.hellocharts.renderer; +import android.graphics.Canvas; + import lecho.lib.hellocharts.model.SelectedValue; import lecho.lib.hellocharts.model.Viewport; -import lecho.lib.hellocharts.view.Chart; - -import android.content.Context; -import android.graphics.Canvas; /** * Interface for all chart renderer. */ public interface ChartRenderer { - public void onChartSizeChanged(); - public void onChartDataChanged(); - public void onChartViewportChanged(); - public void resetRenderer(); + public void onChartSizeChanged(); + + public void onChartDataChanged(); + + public void onChartViewportChanged(); + + public void resetRenderer(); - /** - * Draw chart data. - */ - public void draw(Canvas canvas); + /** + * Draw chart data. + */ + public void draw(Canvas canvas); - /** - * Draw chart data that should not be clipped to contentRect area. - */ - public void drawUnclipped(Canvas canvas); + /** + * Draw chart data that should not be clipped to contentRect area. + */ + public void drawUnclipped(Canvas canvas); - /** - * Checks if given pixel coordinates corresponds to any chart value. If yes return true and set selectedValue, if - * not selectedValue should be *cleared* and method should return false. - */ - public boolean checkTouch(float touchX, float touchY); + /** + * Checks if given pixel coordinates corresponds to any chart value. If yes return true and set selectedValue, if + * not selectedValue should be *cleared* and method should return false. + */ + public boolean checkTouch(float touchX, float touchY); - /** - * Returns true if there is value selected. - */ - public boolean isTouched(); + /** + * Returns true if there is value selected. + */ + public boolean isTouched(); - /** - * Clear value selection. - */ - public void clearTouch(); + /** + * Clear value selection. + */ + public void clearTouch(); - public void setMaximumViewport(Viewport maxViewport); + public void setMaximumViewport(Viewport maxViewport); - public Viewport getMaximumViewport(); + public Viewport getMaximumViewport(); - public void setCurrentViewport(Viewport viewport); + public void setCurrentViewport(Viewport viewport); - public Viewport getCurrentViewport(); + public Viewport getCurrentViewport(); - public boolean isViewportCalculationEnabled(); + public boolean isViewportCalculationEnabled(); - public void setViewportCalculationEnabled(boolean isEnabled); + public void setViewportCalculationEnabled(boolean isEnabled); - public void selectValue(SelectedValue selectedValue); + public void selectValue(SelectedValue selectedValue); - public SelectedValue getSelectedValue(); + public SelectedValue getSelectedValue(); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/renderer/ColumnChartRenderer.java b/hellocharts-library/src/lecho/lib/hellocharts/renderer/ColumnChartRenderer.java index 41435fec..da677138 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/renderer/ColumnChartRenderer.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/renderer/ColumnChartRenderer.java @@ -7,7 +7,6 @@ import android.graphics.PointF; import android.graphics.RectF; -import lecho.lib.hellocharts.computator.ChartComputator; import lecho.lib.hellocharts.model.Column; import lecho.lib.hellocharts.model.ColumnChartData; import lecho.lib.hellocharts.model.SelectedValue.SelectedValueType; @@ -21,403 +20,403 @@ * Magic renderer for ColumnChart. */ public class ColumnChartRenderer extends AbstractChartRenderer { - public static final int DEFAULT_SUBCOLUMN_SPACING_DP = 1; - public static final int DEFAULT_COLUMN_TOUCH_ADDITIONAL_WIDTH_DP = 4; - - private static final int MODE_DRAW = 0; - private static final int MODE_CHECK_TOUCH = 1; - private static final int MODE_HIGHLIGHT = 2; - - private ColumnChartDataProvider dataProvider; - - /** - * Additional width for hightlighted column, used to give tauch feedback. - */ - private int touchAdditionalWidth; - - /** - * Spacing between sub-columns. - */ - private int subcolumnSpacing; - - /** - * Paint used to draw every column. - */ - private Paint columnPaint = new Paint(); - - /** - * Holds coordinates for currently processed column/sub-column. - */ - private RectF drawRect = new RectF(); - - /** - * Coordinated of user tauch. - */ - private PointF touchedPoint = new PointF(); - - private float fillRatio; - - private float baseValue; - - private Viewport tempMaximumViewport = new Viewport(); - - public ColumnChartRenderer(Context context, Chart chart, ColumnChartDataProvider dataProvider) { - super(context, chart); - this.dataProvider = dataProvider; - subcolumnSpacing = ChartUtils.dp2px(density, DEFAULT_SUBCOLUMN_SPACING_DP); - touchAdditionalWidth = ChartUtils.dp2px(density, DEFAULT_COLUMN_TOUCH_ADDITIONAL_WIDTH_DP); - - columnPaint.setAntiAlias(true); - columnPaint.setStyle(Paint.Style.FILL); - columnPaint.setStrokeCap(Cap.SQUARE); - } - - @Override - public void onChartSizeChanged() { - } - - @Override - public void onChartDataChanged() { - super.onChartDataChanged(); - ColumnChartData data = dataProvider.getColumnChartData(); - fillRatio = data.getFillRatio(); - baseValue = data.getBaseValue(); - - onChartViewportChanged(); - } - - @Override - public void onChartViewportChanged() { - if (isViewportCalculationEnabled) { - calculateMaxViewport(); - computator.setMaxViewport(tempMaximumViewport); - computator.setCurrentViewport(computator.getMaximumViewport()); - } - } - - public void draw(Canvas canvas) { - final ColumnChartData data = dataProvider.getColumnChartData(); - if (data.isStacked()) { - drawColumnForStacked(canvas); - if (isTouched()) { - highlightColumnForStacked(canvas); - } - } else { - drawColumnsForSubcolumns(canvas); - if (isTouched()) { - highlightColumnsForSubcolumns(canvas); - } - } - } - - @Override - public void drawUnclipped(Canvas canvas) { - // Do nothing, for this kind of chart there is nothing to draw beyond clipped area - } - - public boolean checkTouch(float touchX, float touchY) { - selectedValue.clear(); - final ColumnChartData data = dataProvider.getColumnChartData(); - if (data.isStacked()) { - checkTouchForStacked(touchX, touchY); - } else { - checkTouchForSubcolumns(touchX, touchY); - } - return isTouched(); - } - - private void calculateMaxViewport() { - final ColumnChartData data = dataProvider.getColumnChartData(); - // Column chart always has X values from 0 to numColumns-1, to add some margin on the left and right I added - // extra 0.5 to the each side, that margins will be negative scaled according to number of columns, so for more - // columns there will be less margin. - tempMaximumViewport.set(-0.5f, baseValue, data.getColumns().size() - 0.5f, baseValue); - if (data.isStacked()) { - calculateMaxViewportForStacked(data); - } else { - calculateMaxViewportForSubcolumns(data); - } - } - - private void calculateMaxViewportForSubcolumns(ColumnChartData data) { - for (Column column : data.getColumns()) { - for (SubcolumnValue columnValue : column.getValues()) { - if (columnValue.getValue() >= baseValue && columnValue.getValue() > tempMaximumViewport.top) { - tempMaximumViewport.top = columnValue.getValue(); - } - if (columnValue.getValue() < baseValue && columnValue.getValue() < tempMaximumViewport.bottom) { - tempMaximumViewport.bottom = columnValue.getValue(); - } - } - } - } - - private void calculateMaxViewportForStacked(ColumnChartData data) { - for (Column column : data.getColumns()) { - float sumPositive = baseValue; - float sumNegative = baseValue; - for (SubcolumnValue columnValue : column.getValues()) { - if (columnValue.getValue() >= baseValue) { - sumPositive += columnValue.getValue(); - } else { - sumNegative += columnValue.getValue(); - } - } - if (sumPositive > tempMaximumViewport.top) { - tempMaximumViewport.top = sumPositive; - } - if (sumNegative < tempMaximumViewport.bottom) { - tempMaximumViewport.bottom = sumNegative; - } - } - } - - private void drawColumnsForSubcolumns(Canvas canvas) { - final ColumnChartData data = dataProvider.getColumnChartData(); - final float columnWidth = calculateColumnWidth(); - int columnIndex = 0; - for (Column column : data.getColumns()) { - processColumnForSubcolumns(canvas, column, columnWidth, columnIndex, MODE_DRAW); - ++columnIndex; - } - } - - private void highlightColumnsForSubcolumns(Canvas canvas) { - final ColumnChartData data = dataProvider.getColumnChartData(); - final float columnWidth = calculateColumnWidth(); - Column column = data.getColumns().get(selectedValue.getFirstIndex()); - processColumnForSubcolumns(canvas, column, columnWidth, selectedValue.getFirstIndex(), MODE_HIGHLIGHT); - } - - private void checkTouchForSubcolumns(float touchX, float touchY) { - // Using member variable to hold touch point to avoid too much parameters in methods. - touchedPoint.x = touchX; - touchedPoint.y = touchY; - final ColumnChartData data = dataProvider.getColumnChartData(); - final float columnWidth = calculateColumnWidth(); - int columnIndex = 0; - for (Column column : data.getColumns()) { - // canvas is not needed for checking touch - processColumnForSubcolumns(null, column, columnWidth, columnIndex, MODE_CHECK_TOUCH); - ++columnIndex; - } - } - - private void processColumnForSubcolumns(Canvas canvas, Column column, float columnWidth, int columnIndex, - int mode) { - // For n subcolumns there will be n-1 spacing and there will be one - // subcolumn for every columnValue - float subcolumnWidth = (columnWidth - (subcolumnSpacing * (column.getValues().size() - 1))) - / column.getValues().size(); - if (subcolumnWidth < 1) { - subcolumnWidth = 1; - } - // Columns are indexes from 0 to n, column index is also column X value - final float rawX = computator.computeRawX(columnIndex); - final float halfColumnWidth = columnWidth / 2; - final float baseRawY = computator.computeRawY(baseValue); - // First subcolumn will starts at the left edge of current column, - // rawValueX is horizontal center of that column - float subcolumnRawX = rawX - halfColumnWidth; - int valueIndex = 0; - for (SubcolumnValue columnValue : column.getValues()) { - columnPaint.setColor(columnValue.getColor()); - if (subcolumnRawX > rawX + halfColumnWidth) { - break; - } - final float rawY = computator.computeRawY(columnValue.getValue()); - calculateRectToDraw(columnValue, subcolumnRawX, subcolumnRawX + subcolumnWidth, baseRawY, rawY); - switch (mode) { - case MODE_DRAW: - drawSubcolumn(canvas, column, columnValue, false); - break; - case MODE_HIGHLIGHT: - highlightSubcolumn(canvas, column, columnValue, valueIndex, false); - break; - case MODE_CHECK_TOUCH: - checkRectToDraw(columnIndex, valueIndex); - break; - default: - // There no else, every case should be handled or exception will - // be thrown - throw new IllegalStateException("Cannot process column in mode: " + mode); - } - subcolumnRawX += subcolumnWidth + subcolumnSpacing; - ++valueIndex; - } - } - - private void drawColumnForStacked(Canvas canvas) { - final ColumnChartData data = dataProvider.getColumnChartData(); - final float columnWidth = calculateColumnWidth(); - // Columns are indexes from 0 to n, column index is also column X value - int columnIndex = 0; - for (Column column : data.getColumns()) { - processColumnForStacked(canvas, column, columnWidth, columnIndex, MODE_DRAW); - ++columnIndex; - } - } - - private void highlightColumnForStacked(Canvas canvas) { - final ColumnChartData data = dataProvider.getColumnChartData(); - final float columnWidth = calculateColumnWidth(); - // Columns are indexes from 0 to n, column index is also column X value - Column column = data.getColumns().get(selectedValue.getFirstIndex()); - processColumnForStacked(canvas, column, columnWidth, selectedValue.getFirstIndex(), MODE_HIGHLIGHT); - } - - private void checkTouchForStacked(float touchX, float touchY) { - touchedPoint.x = touchX; - touchedPoint.y = touchY; - final ColumnChartData data = dataProvider.getColumnChartData(); - final float columnWidth = calculateColumnWidth(); - int columnIndex = 0; - for (Column column : data.getColumns()) { - // canvas is not needed for checking touch - processColumnForStacked(null, column, columnWidth, columnIndex, MODE_CHECK_TOUCH); - ++columnIndex; - } - } - - private void processColumnForStacked(Canvas canvas, Column column, float columnWidth, int columnIndex, int mode) { - final float rawX = computator.computeRawX(columnIndex); - final float halfColumnWidth = columnWidth / 2; - float mostPositiveValue = baseValue; - float mostNegativeValue = baseValue; - float subcolumnBaseValue = baseValue; - int valueIndex = 0; - for (SubcolumnValue columnValue : column.getValues()) { - columnPaint.setColor(columnValue.getColor()); - if (columnValue.getValue() >= baseValue) { - // Using values instead of raw pixels make code easier to - // understand(for me) - subcolumnBaseValue = mostPositiveValue; - mostPositiveValue += columnValue.getValue(); - } else { - subcolumnBaseValue = mostNegativeValue; - mostNegativeValue += columnValue.getValue(); - } - final float rawBaseY = computator.computeRawY(subcolumnBaseValue); - final float rawY = computator.computeRawY(subcolumnBaseValue + columnValue.getValue()); - calculateRectToDraw(columnValue, rawX - halfColumnWidth, rawX + halfColumnWidth, rawBaseY, rawY); - switch (mode) { - case MODE_DRAW: - drawSubcolumn(canvas, column, columnValue, true); - break; - case MODE_HIGHLIGHT: - highlightSubcolumn(canvas, column, columnValue, valueIndex, true); - break; - case MODE_CHECK_TOUCH: - checkRectToDraw(columnIndex, valueIndex); - break; - default: - // There no else, every case should be handled or exception will - // be thrown - throw new IllegalStateException("Cannot process column in mode: " + mode); - } - ++valueIndex; - } - } - - private void drawSubcolumn(Canvas canvas, Column column, SubcolumnValue columnValue, boolean isStacked) { - canvas.drawRect(drawRect, columnPaint); - if (column.hasLabels()) { - drawLabel(canvas, column, columnValue, isStacked, labelOffset); - } - } - - private void highlightSubcolumn(Canvas canvas, Column column, SubcolumnValue columnValue, int valueIndex, - boolean isStacked) { - if (selectedValue.getSecondIndex() == valueIndex) { - columnPaint.setColor(columnValue.getDarkenColor()); - canvas.drawRect(drawRect.left - touchAdditionalWidth, drawRect.top, drawRect.right + touchAdditionalWidth, - drawRect.bottom, columnPaint); - if (column.hasLabels() || column.hasLabelsOnlyForSelected()) { - drawLabel(canvas, column, columnValue, isStacked, labelOffset); - } - } - } - - private void checkRectToDraw(int columnIndex, int valueIndex) { - if (drawRect.contains(touchedPoint.x, touchedPoint.y)) { - selectedValue.set(columnIndex, valueIndex, SelectedValueType.COLUMN); - } - } - - private float calculateColumnWidth() { - // columnWidht should be at least 2 px - float columnWidth = fillRatio * computator.getContentRectMinusAllMargins().width() / computator - .getVisibleViewport().width(); - if (columnWidth < 2) { - columnWidth = 2; - } - return columnWidth; - } - - private void calculateRectToDraw(SubcolumnValue columnValue, float left, float right, float rawBaseY, float rawY) { - // Calculate rect that will be drawn as column, subcolumn or label background. - drawRect.left = left; - drawRect.right = right; - if (columnValue.getValue() >= baseValue) { - drawRect.top = rawY; - drawRect.bottom = rawBaseY - subcolumnSpacing; - } else { - drawRect.bottom = rawY; - drawRect.top = rawBaseY + subcolumnSpacing; - } - } - - private void drawLabel(Canvas canvas, Column column, SubcolumnValue columnValue, boolean isStacked, float offset) { - final int numChars = column.getFormatter().formatChartValue(labelBuffer, columnValue); - - if (numChars == 0) { - // No need to draw empty label - return; - } - - final float labelWidth = labelPaint.measureText(labelBuffer, labelBuffer.length - numChars, numChars); - final int labelHeight = Math.abs(fontMetrics.ascent); - float left = drawRect.centerX() - labelWidth / 2 - labelMargin; - float right = drawRect.centerX() + labelWidth / 2 + labelMargin; - float top; - float bottom; - if (isStacked && labelHeight < drawRect.height() - (2 * labelMargin)) { - // For stacked columns draw label only if label height is less than subcolumn height - (2 * labelMargin). - if (columnValue.getValue() >= baseValue) { - top = drawRect.top; - bottom = drawRect.top + labelHeight + labelMargin * 2; - } else { - top = drawRect.bottom - labelHeight - labelMargin * 2; - bottom = drawRect.bottom; - } - } else if (!isStacked) { - // For not stacked draw label at the top for positive and at the bottom for negative values - if (columnValue.getValue() >= baseValue) { - top = drawRect.top - offset - labelHeight - labelMargin * 2; - if (top < computator.getContentRectMinusAllMargins().top) { - top = drawRect.top + offset; - bottom = drawRect.top + offset + labelHeight + labelMargin * 2; - } else { - bottom = drawRect.top - offset; - } - } else { - bottom = drawRect.bottom + offset + labelHeight + labelMargin * 2; - if (bottom > computator.getContentRectMinusAllMargins().bottom) { - top = drawRect.bottom - offset - labelHeight - labelMargin * 2; - bottom = drawRect.bottom - offset; - } else { - top = drawRect.bottom + offset; - } - } - } else { - // Draw nothing. - return; - } - - labelBackgroundRect.set(left, top, right, bottom); - drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length - numChars, numChars, - columnValue.getDarkenColor()); - - } + public static final int DEFAULT_SUBCOLUMN_SPACING_DP = 1; + public static final int DEFAULT_COLUMN_TOUCH_ADDITIONAL_WIDTH_DP = 4; + + private static final int MODE_DRAW = 0; + private static final int MODE_CHECK_TOUCH = 1; + private static final int MODE_HIGHLIGHT = 2; + + private ColumnChartDataProvider dataProvider; + + /** + * Additional width for hightlighted column, used to give tauch feedback. + */ + private int touchAdditionalWidth; + + /** + * Spacing between sub-columns. + */ + private int subcolumnSpacing; + + /** + * Paint used to draw every column. + */ + private Paint columnPaint = new Paint(); + + /** + * Holds coordinates for currently processed column/sub-column. + */ + private RectF drawRect = new RectF(); + + /** + * Coordinated of user tauch. + */ + private PointF touchedPoint = new PointF(); + + private float fillRatio; + + private float baseValue; + + private Viewport tempMaximumViewport = new Viewport(); + + public ColumnChartRenderer(Context context, Chart chart, ColumnChartDataProvider dataProvider) { + super(context, chart); + this.dataProvider = dataProvider; + subcolumnSpacing = ChartUtils.dp2px(density, DEFAULT_SUBCOLUMN_SPACING_DP); + touchAdditionalWidth = ChartUtils.dp2px(density, DEFAULT_COLUMN_TOUCH_ADDITIONAL_WIDTH_DP); + + columnPaint.setAntiAlias(true); + columnPaint.setStyle(Paint.Style.FILL); + columnPaint.setStrokeCap(Cap.SQUARE); + } + + @Override + public void onChartSizeChanged() { + } + + @Override + public void onChartDataChanged() { + super.onChartDataChanged(); + ColumnChartData data = dataProvider.getColumnChartData(); + fillRatio = data.getFillRatio(); + baseValue = data.getBaseValue(); + + onChartViewportChanged(); + } + + @Override + public void onChartViewportChanged() { + if (isViewportCalculationEnabled) { + calculateMaxViewport(); + computator.setMaxViewport(tempMaximumViewport); + computator.setCurrentViewport(computator.getMaximumViewport()); + } + } + + public void draw(Canvas canvas) { + final ColumnChartData data = dataProvider.getColumnChartData(); + if (data.isStacked()) { + drawColumnForStacked(canvas); + if (isTouched()) { + highlightColumnForStacked(canvas); + } + } else { + drawColumnsForSubcolumns(canvas); + if (isTouched()) { + highlightColumnsForSubcolumns(canvas); + } + } + } + + @Override + public void drawUnclipped(Canvas canvas) { + // Do nothing, for this kind of chart there is nothing to draw beyond clipped area + } + + public boolean checkTouch(float touchX, float touchY) { + selectedValue.clear(); + final ColumnChartData data = dataProvider.getColumnChartData(); + if (data.isStacked()) { + checkTouchForStacked(touchX, touchY); + } else { + checkTouchForSubcolumns(touchX, touchY); + } + return isTouched(); + } + + private void calculateMaxViewport() { + final ColumnChartData data = dataProvider.getColumnChartData(); + // Column chart always has X values from 0 to numColumns-1, to add some margin on the left and right I added + // extra 0.5 to the each side, that margins will be negative scaled according to number of columns, so for more + // columns there will be less margin. + tempMaximumViewport.set(-0.5f, baseValue, data.getColumns().size() - 0.5f, baseValue); + if (data.isStacked()) { + calculateMaxViewportForStacked(data); + } else { + calculateMaxViewportForSubcolumns(data); + } + } + + private void calculateMaxViewportForSubcolumns(ColumnChartData data) { + for (Column column : data.getColumns()) { + for (SubcolumnValue columnValue : column.getValues()) { + if (columnValue.getValue() >= baseValue && columnValue.getValue() > tempMaximumViewport.top) { + tempMaximumViewport.top = columnValue.getValue(); + } + if (columnValue.getValue() < baseValue && columnValue.getValue() < tempMaximumViewport.bottom) { + tempMaximumViewport.bottom = columnValue.getValue(); + } + } + } + } + + private void calculateMaxViewportForStacked(ColumnChartData data) { + for (Column column : data.getColumns()) { + float sumPositive = baseValue; + float sumNegative = baseValue; + for (SubcolumnValue columnValue : column.getValues()) { + if (columnValue.getValue() >= baseValue) { + sumPositive += columnValue.getValue(); + } else { + sumNegative += columnValue.getValue(); + } + } + if (sumPositive > tempMaximumViewport.top) { + tempMaximumViewport.top = sumPositive; + } + if (sumNegative < tempMaximumViewport.bottom) { + tempMaximumViewport.bottom = sumNegative; + } + } + } + + private void drawColumnsForSubcolumns(Canvas canvas) { + final ColumnChartData data = dataProvider.getColumnChartData(); + final float columnWidth = calculateColumnWidth(); + int columnIndex = 0; + for (Column column : data.getColumns()) { + processColumnForSubcolumns(canvas, column, columnWidth, columnIndex, MODE_DRAW); + ++columnIndex; + } + } + + private void highlightColumnsForSubcolumns(Canvas canvas) { + final ColumnChartData data = dataProvider.getColumnChartData(); + final float columnWidth = calculateColumnWidth(); + Column column = data.getColumns().get(selectedValue.getFirstIndex()); + processColumnForSubcolumns(canvas, column, columnWidth, selectedValue.getFirstIndex(), MODE_HIGHLIGHT); + } + + private void checkTouchForSubcolumns(float touchX, float touchY) { + // Using member variable to hold touch point to avoid too much parameters in methods. + touchedPoint.x = touchX; + touchedPoint.y = touchY; + final ColumnChartData data = dataProvider.getColumnChartData(); + final float columnWidth = calculateColumnWidth(); + int columnIndex = 0; + for (Column column : data.getColumns()) { + // canvas is not needed for checking touch + processColumnForSubcolumns(null, column, columnWidth, columnIndex, MODE_CHECK_TOUCH); + ++columnIndex; + } + } + + private void processColumnForSubcolumns(Canvas canvas, Column column, float columnWidth, int columnIndex, + int mode) { + // For n subcolumns there will be n-1 spacing and there will be one + // subcolumn for every columnValue + float subcolumnWidth = (columnWidth - (subcolumnSpacing * (column.getValues().size() - 1))) + / column.getValues().size(); + if (subcolumnWidth < 1) { + subcolumnWidth = 1; + } + // Columns are indexes from 0 to n, column index is also column X value + final float rawX = computator.computeRawX(columnIndex); + final float halfColumnWidth = columnWidth / 2; + final float baseRawY = computator.computeRawY(baseValue); + // First subcolumn will starts at the left edge of current column, + // rawValueX is horizontal center of that column + float subcolumnRawX = rawX - halfColumnWidth; + int valueIndex = 0; + for (SubcolumnValue columnValue : column.getValues()) { + columnPaint.setColor(columnValue.getColor()); + if (subcolumnRawX > rawX + halfColumnWidth) { + break; + } + final float rawY = computator.computeRawY(columnValue.getValue()); + calculateRectToDraw(columnValue, subcolumnRawX, subcolumnRawX + subcolumnWidth, baseRawY, rawY); + switch (mode) { + case MODE_DRAW: + drawSubcolumn(canvas, column, columnValue, false); + break; + case MODE_HIGHLIGHT: + highlightSubcolumn(canvas, column, columnValue, valueIndex, false); + break; + case MODE_CHECK_TOUCH: + checkRectToDraw(columnIndex, valueIndex); + break; + default: + // There no else, every case should be handled or exception will + // be thrown + throw new IllegalStateException("Cannot process column in mode: " + mode); + } + subcolumnRawX += subcolumnWidth + subcolumnSpacing; + ++valueIndex; + } + } + + private void drawColumnForStacked(Canvas canvas) { + final ColumnChartData data = dataProvider.getColumnChartData(); + final float columnWidth = calculateColumnWidth(); + // Columns are indexes from 0 to n, column index is also column X value + int columnIndex = 0; + for (Column column : data.getColumns()) { + processColumnForStacked(canvas, column, columnWidth, columnIndex, MODE_DRAW); + ++columnIndex; + } + } + + private void highlightColumnForStacked(Canvas canvas) { + final ColumnChartData data = dataProvider.getColumnChartData(); + final float columnWidth = calculateColumnWidth(); + // Columns are indexes from 0 to n, column index is also column X value + Column column = data.getColumns().get(selectedValue.getFirstIndex()); + processColumnForStacked(canvas, column, columnWidth, selectedValue.getFirstIndex(), MODE_HIGHLIGHT); + } + + private void checkTouchForStacked(float touchX, float touchY) { + touchedPoint.x = touchX; + touchedPoint.y = touchY; + final ColumnChartData data = dataProvider.getColumnChartData(); + final float columnWidth = calculateColumnWidth(); + int columnIndex = 0; + for (Column column : data.getColumns()) { + // canvas is not needed for checking touch + processColumnForStacked(null, column, columnWidth, columnIndex, MODE_CHECK_TOUCH); + ++columnIndex; + } + } + + private void processColumnForStacked(Canvas canvas, Column column, float columnWidth, int columnIndex, int mode) { + final float rawX = computator.computeRawX(columnIndex); + final float halfColumnWidth = columnWidth / 2; + float mostPositiveValue = baseValue; + float mostNegativeValue = baseValue; + float subcolumnBaseValue = baseValue; + int valueIndex = 0; + for (SubcolumnValue columnValue : column.getValues()) { + columnPaint.setColor(columnValue.getColor()); + if (columnValue.getValue() >= baseValue) { + // Using values instead of raw pixels make code easier to + // understand(for me) + subcolumnBaseValue = mostPositiveValue; + mostPositiveValue += columnValue.getValue(); + } else { + subcolumnBaseValue = mostNegativeValue; + mostNegativeValue += columnValue.getValue(); + } + final float rawBaseY = computator.computeRawY(subcolumnBaseValue); + final float rawY = computator.computeRawY(subcolumnBaseValue + columnValue.getValue()); + calculateRectToDraw(columnValue, rawX - halfColumnWidth, rawX + halfColumnWidth, rawBaseY, rawY); + switch (mode) { + case MODE_DRAW: + drawSubcolumn(canvas, column, columnValue, true); + break; + case MODE_HIGHLIGHT: + highlightSubcolumn(canvas, column, columnValue, valueIndex, true); + break; + case MODE_CHECK_TOUCH: + checkRectToDraw(columnIndex, valueIndex); + break; + default: + // There no else, every case should be handled or exception will + // be thrown + throw new IllegalStateException("Cannot process column in mode: " + mode); + } + ++valueIndex; + } + } + + private void drawSubcolumn(Canvas canvas, Column column, SubcolumnValue columnValue, boolean isStacked) { + canvas.drawRect(drawRect, columnPaint); + if (column.hasLabels()) { + drawLabel(canvas, column, columnValue, isStacked, labelOffset); + } + } + + private void highlightSubcolumn(Canvas canvas, Column column, SubcolumnValue columnValue, int valueIndex, + boolean isStacked) { + if (selectedValue.getSecondIndex() == valueIndex) { + columnPaint.setColor(columnValue.getDarkenColor()); + canvas.drawRect(drawRect.left - touchAdditionalWidth, drawRect.top, drawRect.right + touchAdditionalWidth, + drawRect.bottom, columnPaint); + if (column.hasLabels() || column.hasLabelsOnlyForSelected()) { + drawLabel(canvas, column, columnValue, isStacked, labelOffset); + } + } + } + + private void checkRectToDraw(int columnIndex, int valueIndex) { + if (drawRect.contains(touchedPoint.x, touchedPoint.y)) { + selectedValue.set(columnIndex, valueIndex, SelectedValueType.COLUMN); + } + } + + private float calculateColumnWidth() { + // columnWidht should be at least 2 px + float columnWidth = fillRatio * computator.getContentRectMinusAllMargins().width() / computator + .getVisibleViewport().width(); + if (columnWidth < 2) { + columnWidth = 2; + } + return columnWidth; + } + + private void calculateRectToDraw(SubcolumnValue columnValue, float left, float right, float rawBaseY, float rawY) { + // Calculate rect that will be drawn as column, subcolumn or label background. + drawRect.left = left; + drawRect.right = right; + if (columnValue.getValue() >= baseValue) { + drawRect.top = rawY; + drawRect.bottom = rawBaseY - subcolumnSpacing; + } else { + drawRect.bottom = rawY; + drawRect.top = rawBaseY + subcolumnSpacing; + } + } + + private void drawLabel(Canvas canvas, Column column, SubcolumnValue columnValue, boolean isStacked, float offset) { + final int numChars = column.getFormatter().formatChartValue(labelBuffer, columnValue); + + if (numChars == 0) { + // No need to draw empty label + return; + } + + final float labelWidth = labelPaint.measureText(labelBuffer, labelBuffer.length - numChars, numChars); + final int labelHeight = Math.abs(fontMetrics.ascent); + float left = drawRect.centerX() - labelWidth / 2 - labelMargin; + float right = drawRect.centerX() + labelWidth / 2 + labelMargin; + float top; + float bottom; + if (isStacked && labelHeight < drawRect.height() - (2 * labelMargin)) { + // For stacked columns draw label only if label height is less than subcolumn height - (2 * labelMargin). + if (columnValue.getValue() >= baseValue) { + top = drawRect.top; + bottom = drawRect.top + labelHeight + labelMargin * 2; + } else { + top = drawRect.bottom - labelHeight - labelMargin * 2; + bottom = drawRect.bottom; + } + } else if (!isStacked) { + // For not stacked draw label at the top for positive and at the bottom for negative values + if (columnValue.getValue() >= baseValue) { + top = drawRect.top - offset - labelHeight - labelMargin * 2; + if (top < computator.getContentRectMinusAllMargins().top) { + top = drawRect.top + offset; + bottom = drawRect.top + offset + labelHeight + labelMargin * 2; + } else { + bottom = drawRect.top - offset; + } + } else { + bottom = drawRect.bottom + offset + labelHeight + labelMargin * 2; + if (bottom > computator.getContentRectMinusAllMargins().bottom) { + top = drawRect.bottom - offset - labelHeight - labelMargin * 2; + bottom = drawRect.bottom - offset; + } else { + top = drawRect.bottom + offset; + } + } + } else { + // Draw nothing. + return; + } + + labelBackgroundRect.set(left, top, right, bottom); + drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length - numChars, numChars, + columnValue.getDarkenColor()); + + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/renderer/ComboChartRenderer.java b/hellocharts-library/src/lecho/lib/hellocharts/renderer/ComboChartRenderer.java index d826ceab..5cb4153f 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/renderer/ComboChartRenderer.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/renderer/ComboChartRenderer.java @@ -2,7 +2,6 @@ import android.content.Context; import android.graphics.Canvas; -import android.graphics.Rect; import java.util.ArrayList; import java.util.List; @@ -12,89 +11,89 @@ public class ComboChartRenderer extends AbstractChartRenderer { - protected List renderers; - protected Viewport unionViewport = new Viewport(); - - public ComboChartRenderer(Context context, Chart chart) { - super(context, chart); - this.renderers = new ArrayList<>(); - } - - @Override - public void onChartSizeChanged() { - for (ChartRenderer renderer : renderers) { - renderer.onChartSizeChanged(); - } - } - - @Override - public void onChartDataChanged() { - super.onChartDataChanged(); - for (ChartRenderer renderer : renderers) { - renderer.onChartDataChanged(); - } - onChartViewportChanged(); - } - - @Override - public void onChartViewportChanged() { - if (isViewportCalculationEnabled) { - int rendererIndex = 0; - for (ChartRenderer renderer : renderers) { - renderer.onChartViewportChanged(); - if (rendererIndex == 0) { - unionViewport.set(renderer.getMaximumViewport()); - } else { - unionViewport.union(renderer.getMaximumViewport()); - } - ++rendererIndex; - } - computator.setMaxViewport(unionViewport); - computator.setCurrentViewport(unionViewport); - } - - - } - - public void draw(Canvas canvas) { - for (ChartRenderer renderer : renderers) { - renderer.draw(canvas); - } - } - - @Override - public void drawUnclipped(Canvas canvas) { - for (ChartRenderer renderer : renderers) { - renderer.drawUnclipped(canvas); - } - } - - public boolean checkTouch(float touchX, float touchY) { - selectedValue.clear(); - int rendererIndex = renderers.size() - 1; - for (; rendererIndex >= 0; rendererIndex--) { - ChartRenderer renderer = renderers.get(rendererIndex); - if (renderer.checkTouch(touchX, touchY)) { - selectedValue.set(renderer.getSelectedValue()); - break; - } - } - - //clear the rest of renderers if value was selected, if value was not selected this loop - // will not be executed. - for (rendererIndex--; rendererIndex >= 0; rendererIndex--) { - ChartRenderer renderer = renderers.get(rendererIndex); - renderer.clearTouch(); - } - - return isTouched(); - } - - @Override - public void clearTouch() { - for (ChartRenderer renderer : renderers) { - renderer.clearTouch(); - } - selectedValue.clear(); - } + protected List renderers; + protected Viewport unionViewport = new Viewport(); + + public ComboChartRenderer(Context context, Chart chart) { + super(context, chart); + this.renderers = new ArrayList<>(); + } + + @Override + public void onChartSizeChanged() { + for (ChartRenderer renderer : renderers) { + renderer.onChartSizeChanged(); + } + } + + @Override + public void onChartDataChanged() { + super.onChartDataChanged(); + for (ChartRenderer renderer : renderers) { + renderer.onChartDataChanged(); + } + onChartViewportChanged(); + } + + @Override + public void onChartViewportChanged() { + if (isViewportCalculationEnabled) { + int rendererIndex = 0; + for (ChartRenderer renderer : renderers) { + renderer.onChartViewportChanged(); + if (rendererIndex == 0) { + unionViewport.set(renderer.getMaximumViewport()); + } else { + unionViewport.union(renderer.getMaximumViewport()); + } + ++rendererIndex; + } + computator.setMaxViewport(unionViewport); + computator.setCurrentViewport(unionViewport); + } + + + } + + public void draw(Canvas canvas) { + for (ChartRenderer renderer : renderers) { + renderer.draw(canvas); + } + } + + @Override + public void drawUnclipped(Canvas canvas) { + for (ChartRenderer renderer : renderers) { + renderer.drawUnclipped(canvas); + } + } + + public boolean checkTouch(float touchX, float touchY) { + selectedValue.clear(); + int rendererIndex = renderers.size() - 1; + for (; rendererIndex >= 0; rendererIndex--) { + ChartRenderer renderer = renderers.get(rendererIndex); + if (renderer.checkTouch(touchX, touchY)) { + selectedValue.set(renderer.getSelectedValue()); + break; + } + } + + //clear the rest of renderers if value was selected, if value was not selected this loop + // will not be executed. + for (rendererIndex--; rendererIndex >= 0; rendererIndex--) { + ChartRenderer renderer = renderers.get(rendererIndex); + renderer.clearTouch(); + } + + return isTouched(); + } + + @Override + public void clearTouch() { + for (ChartRenderer renderer : renderers) { + renderer.clearTouch(); + } + selectedValue.clear(); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/renderer/ComboLineColumnChartRenderer.java b/hellocharts-library/src/lecho/lib/hellocharts/renderer/ComboLineColumnChartRenderer.java index 47a3fe72..c44b886c 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/renderer/ComboLineColumnChartRenderer.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/renderer/ComboLineColumnChartRenderer.java @@ -1,23 +1,24 @@ package lecho.lib.hellocharts.renderer; +import android.content.Context; + import lecho.lib.hellocharts.provider.ColumnChartDataProvider; import lecho.lib.hellocharts.provider.LineChartDataProvider; import lecho.lib.hellocharts.view.Chart; -import android.content.Context; public class ComboLineColumnChartRenderer extends ComboChartRenderer { - private ColumnChartRenderer columnChartRenderer; - private LineChartRenderer lineChartRenderer; + private ColumnChartRenderer columnChartRenderer; + private LineChartRenderer lineChartRenderer; - public ComboLineColumnChartRenderer(Context context, Chart chart, ColumnChartDataProvider columnChartDataProvider, - LineChartDataProvider lineChartDataProvider) { - super(context, chart); + public ComboLineColumnChartRenderer(Context context, Chart chart, ColumnChartDataProvider columnChartDataProvider, + LineChartDataProvider lineChartDataProvider) { + super(context, chart); - columnChartRenderer = new ColumnChartRenderer(context, chart, columnChartDataProvider); - lineChartRenderer = new LineChartRenderer(context, chart, lineChartDataProvider); + columnChartRenderer = new ColumnChartRenderer(context, chart, columnChartDataProvider); + lineChartRenderer = new LineChartRenderer(context, chart, lineChartDataProvider); - renderers.add(columnChartRenderer); - renderers.add(lineChartRenderer); - } + renderers.add(columnChartRenderer); + renderers.add(lineChartRenderer); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/renderer/LineChartRenderer.java b/hellocharts-library/src/lecho/lib/hellocharts/renderer/LineChartRenderer.java index 98c251d3..5e159323 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/renderer/LineChartRenderer.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/renderer/LineChartRenderer.java @@ -24,444 +24,444 @@ * Renderer for line chart. Can draw lines, cubic lines, filled area chart and scattered chart. */ public class LineChartRenderer extends AbstractChartRenderer { - private static final float LINE_SMOOTHNESS = 0.16f; - private static final int DEFAULT_LINE_STROKE_WIDTH_DP = 3; - private static final int DEFAULT_TOUCH_TOLERANCE_MARGIN_DP = 4; - - private static final int MODE_DRAW = 0; - private static final int MODE_HIGHLIGHT = 1; - - private LineChartDataProvider dataProvider; - - private int checkPrecision; - - private float baseValue; - - private int touchToleranceMargin; - private Path path = new Path(); - private Paint linePaint = new Paint(); - private Paint pointPaint = new Paint(); - - private Bitmap softwareBitmap; - private Canvas softwareCanvas = new Canvas(); - private Viewport tempMaximumViewport = new Viewport(); - - public LineChartRenderer(Context context, Chart chart, LineChartDataProvider dataProvider) { - super(context, chart); - this.dataProvider = dataProvider; - - touchToleranceMargin = ChartUtils.dp2px(density, DEFAULT_TOUCH_TOLERANCE_MARGIN_DP); - - linePaint.setAntiAlias(true); - linePaint.setStyle(Paint.Style.STROKE); - linePaint.setStrokeCap(Cap.ROUND); - linePaint.setStrokeWidth(ChartUtils.dp2px(density, DEFAULT_LINE_STROKE_WIDTH_DP)); - - pointPaint.setAntiAlias(true); - pointPaint.setStyle(Paint.Style.FILL); - - checkPrecision = ChartUtils.dp2px(density, 2); - - } - - public void onChartSizeChanged() { - final int internalMargin = calculateContentRectInternalMargin(); - computator.insetContentRectByInternalMargins(internalMargin, internalMargin, - internalMargin, internalMargin); - if (computator.getChartWidth() > 0 && computator.getChartHeight() > 0) { - softwareBitmap = Bitmap.createBitmap(computator.getChartWidth(), computator.getChartHeight(), - Bitmap.Config.ARGB_8888); - softwareCanvas.setBitmap(softwareBitmap); - } - } - - @Override - public void onChartDataChanged() { - super.onChartDataChanged(); - final int internalMargin = calculateContentRectInternalMargin(); - computator.insetContentRectByInternalMargins(internalMargin, internalMargin, - internalMargin, internalMargin); - baseValue = dataProvider.getLineChartData().getBaseValue(); - - onChartViewportChanged(); - } - - @Override - public void onChartViewportChanged() { - if (isViewportCalculationEnabled) { - calculateMaxViewport(); - computator.setMaxViewport(tempMaximumViewport); - computator.setCurrentViewport(computator.getMaximumViewport()); - } - } - - @Override - public void draw(Canvas canvas) { - final LineChartData data = dataProvider.getLineChartData(); - - final Canvas drawCanvas; - - // softwareBitmap can be null if chart is rendered in layout editor. In that case use default canvas and not - // softwareCanvas. - if (null != softwareBitmap) { - drawCanvas = softwareCanvas; - drawCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); - } else { - drawCanvas = canvas; - } - - for (Line line : data.getLines()) { - if (line.hasLines()) { - if (line.isCubic()) { - drawSmoothPath(drawCanvas, line); - } else { - drawPath(drawCanvas, line); - } - } - } - - if (null != softwareBitmap) { - canvas.drawBitmap(softwareBitmap, 0, 0, null); - } - } - - @Override - public void drawUnclipped(Canvas canvas) { - final LineChartData data = dataProvider.getLineChartData(); - int lineIndex = 0; - for (Line line : data.getLines()) { - if (checkIfShouldDrawPoints(line)) { - drawPoints(canvas, line, lineIndex, MODE_DRAW); - } - ++lineIndex; - } - if (isTouched()) { - // Redraw touched point to bring it to the front - highlightPoints(canvas); - } - } - - private boolean checkIfShouldDrawPoints(Line line) { - return line.hasPoints() || line.getValues().size() == 1; - } - - @Override - public boolean checkTouch(float touchX, float touchY) { - selectedValue.clear(); - final LineChartData data = dataProvider.getLineChartData(); - int lineIndex = 0; - for (Line line : data.getLines()) { - if(checkIfShouldDrawPoints(line)) { - int pointRadius = ChartUtils.dp2px(density, line.getPointRadius()); - int valueIndex = 0; - for (PointValue pointValue : line.getValues()) { - final float rawValueX = computator.computeRawX(pointValue.getX()); - final float rawValueY = computator.computeRawY(pointValue.getY()); - if (isInArea(rawValueX, rawValueY, touchX, touchY, pointRadius + touchToleranceMargin)) { - selectedValue.set(lineIndex, valueIndex, SelectedValueType.LINE); - } - ++valueIndex; - } - } - ++lineIndex; - } - return isTouched(); - } - - private void calculateMaxViewport() { - tempMaximumViewport.set(Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MAX_VALUE); - LineChartData data = dataProvider.getLineChartData(); - - for (Line line : data.getLines()) { - // Calculate max and min for viewport. - for (PointValue pointValue : line.getValues()) { - if (pointValue.getX() < tempMaximumViewport.left) { - tempMaximumViewport.left = pointValue.getX(); - } - if (pointValue.getX() > tempMaximumViewport.right) { - tempMaximumViewport.right = pointValue.getX(); - } - if (pointValue.getY() < tempMaximumViewport.bottom) { - tempMaximumViewport.bottom = pointValue.getY(); - } - if (pointValue.getY() > tempMaximumViewport.top) { - tempMaximumViewport.top = pointValue.getY(); - } - - } - } - } - - private int calculateContentRectInternalMargin() { - int contentAreaMargin = 0; - final LineChartData data = dataProvider.getLineChartData(); - for (Line line : data.getLines()) { - if (checkIfShouldDrawPoints(line)) { - int margin = line.getPointRadius() + DEFAULT_TOUCH_TOLERANCE_MARGIN_DP; - if (margin > contentAreaMargin) { - contentAreaMargin = margin; - } - } - } - return ChartUtils.dp2px(density, contentAreaMargin); - } - - /** - * Draws lines, uses path for drawing filled area on software canvas. Line is drawn with canvas.drawLines() method. - */ - private void drawPath(Canvas canvas, final Line line) { - prepareLinePaint(line); - - int valueIndex = 0; - for (PointValue pointValue : line.getValues()) { - - final float rawX = computator.computeRawX(pointValue.getX()); - final float rawY = computator.computeRawY(pointValue.getY()); - - if (valueIndex == 0) { - path.moveTo(rawX, rawY); - } else { - path.lineTo(rawX, rawY); - } - - ++valueIndex; - - } - - canvas.drawPath(path, linePaint); - - if (line.isFilled()) { - drawArea(canvas, line); - } - - path.reset(); - } - - private void drawSmoothPath(Canvas canvas, final Line line) { - prepareLinePaint(line); - - final int lineSize = line.getValues().size(); - float prePreviousPointX = Float.NaN; - float prePreviousPointY = Float.NaN; - float previousPointX = Float.NaN; - float previousPointY = Float.NaN; - float currentPointX = Float.NaN; - float currentPointY = Float.NaN; - float nextPointX = Float.NaN; - float nextPointY = Float.NaN; - - for (int valueIndex = 0; valueIndex < lineSize; ++valueIndex) { - if (Float.isNaN(currentPointX)) { - PointValue linePoint = line.getValues().get(valueIndex); - currentPointX = computator.computeRawX(linePoint.getX()); - currentPointY = computator.computeRawY(linePoint.getY()); - } - if (Float.isNaN(previousPointX)) { - if (valueIndex > 0) { - PointValue linePoint = line.getValues().get(valueIndex - 1); - previousPointX = computator.computeRawX(linePoint.getX()); - previousPointY = computator.computeRawY(linePoint.getY()); - } else { - previousPointX = currentPointX; - previousPointY = currentPointY; - } - } - - if (Float.isNaN(prePreviousPointX)) { - if (valueIndex > 1) { - PointValue linePoint = line.getValues().get(valueIndex - 2); - prePreviousPointX = computator.computeRawX(linePoint.getX()); - prePreviousPointY = computator.computeRawY(linePoint.getY()); - } else { - prePreviousPointX = previousPointX; - prePreviousPointY = previousPointY; - } - } - - // nextPoint is always new one or it is equal currentPoint. - if (valueIndex < lineSize - 1) { - PointValue linePoint = line.getValues().get(valueIndex + 1); - nextPointX = computator.computeRawX(linePoint.getX()); - nextPointY = computator.computeRawY(linePoint.getY()); - } else { - nextPointX = currentPointX; - nextPointY = currentPointY; - } - - if (valueIndex == 0) { - // Move to start point. - path.moveTo(currentPointX, currentPointY); - } else { - // Calculate control points. - final float firstDiffX = (currentPointX - prePreviousPointX); - final float firstDiffY = (currentPointY - prePreviousPointY); - final float secondDiffX = (nextPointX - previousPointX); - final float secondDiffY = (nextPointY - previousPointY); - final float firstControlPointX = previousPointX + (LINE_SMOOTHNESS * firstDiffX); - final float firstControlPointY = previousPointY + (LINE_SMOOTHNESS * firstDiffY); - final float secondControlPointX = currentPointX - (LINE_SMOOTHNESS * secondDiffX); - final float secondControlPointY = currentPointY - (LINE_SMOOTHNESS * secondDiffY); - path.cubicTo(firstControlPointX, firstControlPointY, secondControlPointX, secondControlPointY, - currentPointX, currentPointY); - } - - // Shift values by one back to prevent recalculation of values that have - // been already calculated. - prePreviousPointX = previousPointX; - prePreviousPointY = previousPointY; - previousPointX = currentPointX; - previousPointY = currentPointY; - currentPointX = nextPointX; - currentPointY = nextPointY; - } - - canvas.drawPath(path, linePaint); - if (line.isFilled()) { - drawArea(canvas, line); - } - path.reset(); - } - - private void prepareLinePaint(final Line line) { - linePaint.setStrokeWidth(ChartUtils.dp2px(density, line.getStrokeWidth())); - linePaint.setColor(line.getColor()); - linePaint.setPathEffect(line.getPathEffect()); - } - - // TODO Drawing points can be done in the same loop as drawing lines but it - // may cause problems in the future with - // implementing point styles. - private void drawPoints(Canvas canvas, Line line, int lineIndex, int mode) { - pointPaint.setColor(line.getColor()); - int valueIndex = 0; - for (PointValue pointValue : line.getValues()) { - int pointRadius = ChartUtils.dp2px(density, line.getPointRadius()); - final float rawX = computator.computeRawX(pointValue.getX()); - final float rawY = computator.computeRawY(pointValue.getY()); - if (computator.isWithinContentRect(rawX, rawY, checkPrecision)) { - // Draw points only if they are within contentRectMinusAllMargins, using contentRectMinusAllMargins - // instead of viewport to avoid some - // float rounding problems. - if (MODE_DRAW == mode) { - drawPoint(canvas, line, pointValue, rawX, rawY, pointRadius); - if (line.hasLabels()) { - drawLabel(canvas, line, pointValue, rawX, rawY, pointRadius + labelOffset); - } - } else if (MODE_HIGHLIGHT == mode) { - highlightPoint(canvas, line, pointValue, rawX, rawY, lineIndex, valueIndex); - } else { - throw new IllegalStateException("Cannot process points in mode: " + mode); - } - } - ++valueIndex; - } - } - - private void drawPoint(Canvas canvas, Line line, PointValue pointValue, float rawX, float rawY, - float pointRadius) { - if (ValueShape.SQUARE.equals(line.getShape())) { - canvas.drawRect(rawX - pointRadius, rawY - pointRadius, rawX + pointRadius, rawY + pointRadius, - pointPaint); - } else if (ValueShape.CIRCLE.equals(line.getShape())) { - canvas.drawCircle(rawX, rawY, pointRadius, pointPaint); - } else { - throw new IllegalArgumentException("Invalid point shape: " + line.getShape()); - } - } - - private void highlightPoints(Canvas canvas) { - int lineIndex = selectedValue.getFirstIndex(); - Line line = dataProvider.getLineChartData().getLines().get(lineIndex); - drawPoints(canvas, line, lineIndex, MODE_HIGHLIGHT); - } - - private void highlightPoint(Canvas canvas, Line line, PointValue pointValue, float rawX, float rawY, int lineIndex, - int valueIndex) { - if (selectedValue.getFirstIndex() == lineIndex && selectedValue.getSecondIndex() == valueIndex) { - int pointRadius = ChartUtils.dp2px(density, line.getPointRadius()); - pointPaint.setColor(line.getDarkenColor()); - drawPoint(canvas, line, pointValue, rawX, rawY, pointRadius + touchToleranceMargin); - if (line.hasLabels() || line.hasLabelsOnlyForSelected()) { - drawLabel(canvas, line, pointValue, rawX, rawY, pointRadius + labelOffset); - } - } - } - - private void drawLabel(Canvas canvas, Line line, PointValue pointValue, float rawX, float rawY, float offset) { - final Rect contentRect = computator.getContentRectMinusAllMargins(); - final int numChars = line.getFormatter().formatChartValue(labelBuffer, pointValue); - if (numChars == 0) { - // No need to draw empty label - return; - } - - final float labelWidth = labelPaint.measureText(labelBuffer, labelBuffer.length - numChars, numChars); - final int labelHeight = Math.abs(fontMetrics.ascent); - float left = rawX - labelWidth / 2 - labelMargin; - float right = rawX + labelWidth / 2 + labelMargin; - - float top; - float bottom; - - if (pointValue.getY() >= baseValue) { - top = rawY - offset - labelHeight - labelMargin * 2; - bottom = rawY - offset; - } else { - top = rawY + offset; - bottom = rawY + offset + labelHeight + labelMargin * 2; - } - - if (top < contentRect.top) { - top = rawY + offset; - bottom = rawY + offset + labelHeight + labelMargin * 2; - } - if (bottom > contentRect.bottom) { - top = rawY - offset - labelHeight - labelMargin * 2; - bottom = rawY - offset; - } - if (left < contentRect.left) { - left = rawX; - right = rawX + labelWidth + labelMargin * 2; - } - if (right > contentRect.right) { - left = rawX - labelWidth - labelMargin * 2; - right = rawX; - } - - labelBackgroundRect.set(left, top, right, bottom); - drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length - numChars, numChars, - line.getDarkenColor()); - } - - private void drawArea(Canvas canvas, Line line) { - final int lineSize = line.getValues().size(); - if (lineSize < 2) { - //No point to draw area for one point or empty line. - return; - } - - final Rect contentRect = computator.getContentRectMinusAllMargins(); - final float baseRawValue = Math.min(contentRect.bottom, Math.max(computator.computeRawY(baseValue), - contentRect.top)); - //That checks works only if the last point is the right most one. - final float left = Math.max(computator.computeRawX(line.getValues().get(0).getX()), contentRect.left); - final float right = Math.min(computator.computeRawX(line.getValues().get(lineSize - 1).getX()), - contentRect.right); - - path.lineTo(right, baseRawValue); - path.lineTo(left, baseRawValue); - path.close(); - - linePaint.setStyle(Paint.Style.FILL); - linePaint.setAlpha(line.getAreaTransparency()); - canvas.drawPath(path, linePaint); - linePaint.setStyle(Paint.Style.STROKE); - } - - private boolean isInArea(float x, float y, float touchX, float touchY, float radius) { - float diffX = touchX - x; - float diffY = touchY - y; - return Math.pow(diffX, 2) + Math.pow(diffY, 2) <= 2 * Math.pow(radius, 2); - } + private static final float LINE_SMOOTHNESS = 0.16f; + private static final int DEFAULT_LINE_STROKE_WIDTH_DP = 3; + private static final int DEFAULT_TOUCH_TOLERANCE_MARGIN_DP = 4; + + private static final int MODE_DRAW = 0; + private static final int MODE_HIGHLIGHT = 1; + + private LineChartDataProvider dataProvider; + + private int checkPrecision; + + private float baseValue; + + private int touchToleranceMargin; + private Path path = new Path(); + private Paint linePaint = new Paint(); + private Paint pointPaint = new Paint(); + + private Bitmap softwareBitmap; + private Canvas softwareCanvas = new Canvas(); + private Viewport tempMaximumViewport = new Viewport(); + + public LineChartRenderer(Context context, Chart chart, LineChartDataProvider dataProvider) { + super(context, chart); + this.dataProvider = dataProvider; + + touchToleranceMargin = ChartUtils.dp2px(density, DEFAULT_TOUCH_TOLERANCE_MARGIN_DP); + + linePaint.setAntiAlias(true); + linePaint.setStyle(Paint.Style.STROKE); + linePaint.setStrokeCap(Cap.ROUND); + linePaint.setStrokeWidth(ChartUtils.dp2px(density, DEFAULT_LINE_STROKE_WIDTH_DP)); + + pointPaint.setAntiAlias(true); + pointPaint.setStyle(Paint.Style.FILL); + + checkPrecision = ChartUtils.dp2px(density, 2); + + } + + public void onChartSizeChanged() { + final int internalMargin = calculateContentRectInternalMargin(); + computator.insetContentRectByInternalMargins(internalMargin, internalMargin, + internalMargin, internalMargin); + if (computator.getChartWidth() > 0 && computator.getChartHeight() > 0) { + softwareBitmap = Bitmap.createBitmap(computator.getChartWidth(), computator.getChartHeight(), + Bitmap.Config.ARGB_8888); + softwareCanvas.setBitmap(softwareBitmap); + } + } + + @Override + public void onChartDataChanged() { + super.onChartDataChanged(); + final int internalMargin = calculateContentRectInternalMargin(); + computator.insetContentRectByInternalMargins(internalMargin, internalMargin, + internalMargin, internalMargin); + baseValue = dataProvider.getLineChartData().getBaseValue(); + + onChartViewportChanged(); + } + + @Override + public void onChartViewportChanged() { + if (isViewportCalculationEnabled) { + calculateMaxViewport(); + computator.setMaxViewport(tempMaximumViewport); + computator.setCurrentViewport(computator.getMaximumViewport()); + } + } + + @Override + public void draw(Canvas canvas) { + final LineChartData data = dataProvider.getLineChartData(); + + final Canvas drawCanvas; + + // softwareBitmap can be null if chart is rendered in layout editor. In that case use default canvas and not + // softwareCanvas. + if (null != softwareBitmap) { + drawCanvas = softwareCanvas; + drawCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); + } else { + drawCanvas = canvas; + } + + for (Line line : data.getLines()) { + if (line.hasLines()) { + if (line.isCubic()) { + drawSmoothPath(drawCanvas, line); + } else { + drawPath(drawCanvas, line); + } + } + } + + if (null != softwareBitmap) { + canvas.drawBitmap(softwareBitmap, 0, 0, null); + } + } + + @Override + public void drawUnclipped(Canvas canvas) { + final LineChartData data = dataProvider.getLineChartData(); + int lineIndex = 0; + for (Line line : data.getLines()) { + if (checkIfShouldDrawPoints(line)) { + drawPoints(canvas, line, lineIndex, MODE_DRAW); + } + ++lineIndex; + } + if (isTouched()) { + // Redraw touched point to bring it to the front + highlightPoints(canvas); + } + } + + private boolean checkIfShouldDrawPoints(Line line) { + return line.hasPoints() || line.getValues().size() == 1; + } + + @Override + public boolean checkTouch(float touchX, float touchY) { + selectedValue.clear(); + final LineChartData data = dataProvider.getLineChartData(); + int lineIndex = 0; + for (Line line : data.getLines()) { + if (checkIfShouldDrawPoints(line)) { + int pointRadius = ChartUtils.dp2px(density, line.getPointRadius()); + int valueIndex = 0; + for (PointValue pointValue : line.getValues()) { + final float rawValueX = computator.computeRawX(pointValue.getX()); + final float rawValueY = computator.computeRawY(pointValue.getY()); + if (isInArea(rawValueX, rawValueY, touchX, touchY, pointRadius + touchToleranceMargin)) { + selectedValue.set(lineIndex, valueIndex, SelectedValueType.LINE); + } + ++valueIndex; + } + } + ++lineIndex; + } + return isTouched(); + } + + private void calculateMaxViewport() { + tempMaximumViewport.set(Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MAX_VALUE); + LineChartData data = dataProvider.getLineChartData(); + + for (Line line : data.getLines()) { + // Calculate max and min for viewport. + for (PointValue pointValue : line.getValues()) { + if (pointValue.getX() < tempMaximumViewport.left) { + tempMaximumViewport.left = pointValue.getX(); + } + if (pointValue.getX() > tempMaximumViewport.right) { + tempMaximumViewport.right = pointValue.getX(); + } + if (pointValue.getY() < tempMaximumViewport.bottom) { + tempMaximumViewport.bottom = pointValue.getY(); + } + if (pointValue.getY() > tempMaximumViewport.top) { + tempMaximumViewport.top = pointValue.getY(); + } + + } + } + } + + private int calculateContentRectInternalMargin() { + int contentAreaMargin = 0; + final LineChartData data = dataProvider.getLineChartData(); + for (Line line : data.getLines()) { + if (checkIfShouldDrawPoints(line)) { + int margin = line.getPointRadius() + DEFAULT_TOUCH_TOLERANCE_MARGIN_DP; + if (margin > contentAreaMargin) { + contentAreaMargin = margin; + } + } + } + return ChartUtils.dp2px(density, contentAreaMargin); + } + + /** + * Draws lines, uses path for drawing filled area on software canvas. Line is drawn with canvas.drawLines() method. + */ + private void drawPath(Canvas canvas, final Line line) { + prepareLinePaint(line); + + int valueIndex = 0; + for (PointValue pointValue : line.getValues()) { + + final float rawX = computator.computeRawX(pointValue.getX()); + final float rawY = computator.computeRawY(pointValue.getY()); + + if (valueIndex == 0) { + path.moveTo(rawX, rawY); + } else { + path.lineTo(rawX, rawY); + } + + ++valueIndex; + + } + + canvas.drawPath(path, linePaint); + + if (line.isFilled()) { + drawArea(canvas, line); + } + + path.reset(); + } + + private void drawSmoothPath(Canvas canvas, final Line line) { + prepareLinePaint(line); + + final int lineSize = line.getValues().size(); + float prePreviousPointX = Float.NaN; + float prePreviousPointY = Float.NaN; + float previousPointX = Float.NaN; + float previousPointY = Float.NaN; + float currentPointX = Float.NaN; + float currentPointY = Float.NaN; + float nextPointX = Float.NaN; + float nextPointY = Float.NaN; + + for (int valueIndex = 0; valueIndex < lineSize; ++valueIndex) { + if (Float.isNaN(currentPointX)) { + PointValue linePoint = line.getValues().get(valueIndex); + currentPointX = computator.computeRawX(linePoint.getX()); + currentPointY = computator.computeRawY(linePoint.getY()); + } + if (Float.isNaN(previousPointX)) { + if (valueIndex > 0) { + PointValue linePoint = line.getValues().get(valueIndex - 1); + previousPointX = computator.computeRawX(linePoint.getX()); + previousPointY = computator.computeRawY(linePoint.getY()); + } else { + previousPointX = currentPointX; + previousPointY = currentPointY; + } + } + + if (Float.isNaN(prePreviousPointX)) { + if (valueIndex > 1) { + PointValue linePoint = line.getValues().get(valueIndex - 2); + prePreviousPointX = computator.computeRawX(linePoint.getX()); + prePreviousPointY = computator.computeRawY(linePoint.getY()); + } else { + prePreviousPointX = previousPointX; + prePreviousPointY = previousPointY; + } + } + + // nextPoint is always new one or it is equal currentPoint. + if (valueIndex < lineSize - 1) { + PointValue linePoint = line.getValues().get(valueIndex + 1); + nextPointX = computator.computeRawX(linePoint.getX()); + nextPointY = computator.computeRawY(linePoint.getY()); + } else { + nextPointX = currentPointX; + nextPointY = currentPointY; + } + + if (valueIndex == 0) { + // Move to start point. + path.moveTo(currentPointX, currentPointY); + } else { + // Calculate control points. + final float firstDiffX = (currentPointX - prePreviousPointX); + final float firstDiffY = (currentPointY - prePreviousPointY); + final float secondDiffX = (nextPointX - previousPointX); + final float secondDiffY = (nextPointY - previousPointY); + final float firstControlPointX = previousPointX + (LINE_SMOOTHNESS * firstDiffX); + final float firstControlPointY = previousPointY + (LINE_SMOOTHNESS * firstDiffY); + final float secondControlPointX = currentPointX - (LINE_SMOOTHNESS * secondDiffX); + final float secondControlPointY = currentPointY - (LINE_SMOOTHNESS * secondDiffY); + path.cubicTo(firstControlPointX, firstControlPointY, secondControlPointX, secondControlPointY, + currentPointX, currentPointY); + } + + // Shift values by one back to prevent recalculation of values that have + // been already calculated. + prePreviousPointX = previousPointX; + prePreviousPointY = previousPointY; + previousPointX = currentPointX; + previousPointY = currentPointY; + currentPointX = nextPointX; + currentPointY = nextPointY; + } + + canvas.drawPath(path, linePaint); + if (line.isFilled()) { + drawArea(canvas, line); + } + path.reset(); + } + + private void prepareLinePaint(final Line line) { + linePaint.setStrokeWidth(ChartUtils.dp2px(density, line.getStrokeWidth())); + linePaint.setColor(line.getColor()); + linePaint.setPathEffect(line.getPathEffect()); + } + + // TODO Drawing points can be done in the same loop as drawing lines but it + // may cause problems in the future with + // implementing point styles. + private void drawPoints(Canvas canvas, Line line, int lineIndex, int mode) { + pointPaint.setColor(line.getColor()); + int valueIndex = 0; + for (PointValue pointValue : line.getValues()) { + int pointRadius = ChartUtils.dp2px(density, line.getPointRadius()); + final float rawX = computator.computeRawX(pointValue.getX()); + final float rawY = computator.computeRawY(pointValue.getY()); + if (computator.isWithinContentRect(rawX, rawY, checkPrecision)) { + // Draw points only if they are within contentRectMinusAllMargins, using contentRectMinusAllMargins + // instead of viewport to avoid some + // float rounding problems. + if (MODE_DRAW == mode) { + drawPoint(canvas, line, pointValue, rawX, rawY, pointRadius); + if (line.hasLabels()) { + drawLabel(canvas, line, pointValue, rawX, rawY, pointRadius + labelOffset); + } + } else if (MODE_HIGHLIGHT == mode) { + highlightPoint(canvas, line, pointValue, rawX, rawY, lineIndex, valueIndex); + } else { + throw new IllegalStateException("Cannot process points in mode: " + mode); + } + } + ++valueIndex; + } + } + + private void drawPoint(Canvas canvas, Line line, PointValue pointValue, float rawX, float rawY, + float pointRadius) { + if (ValueShape.SQUARE.equals(line.getShape())) { + canvas.drawRect(rawX - pointRadius, rawY - pointRadius, rawX + pointRadius, rawY + pointRadius, + pointPaint); + } else if (ValueShape.CIRCLE.equals(line.getShape())) { + canvas.drawCircle(rawX, rawY, pointRadius, pointPaint); + } else { + throw new IllegalArgumentException("Invalid point shape: " + line.getShape()); + } + } + + private void highlightPoints(Canvas canvas) { + int lineIndex = selectedValue.getFirstIndex(); + Line line = dataProvider.getLineChartData().getLines().get(lineIndex); + drawPoints(canvas, line, lineIndex, MODE_HIGHLIGHT); + } + + private void highlightPoint(Canvas canvas, Line line, PointValue pointValue, float rawX, float rawY, int lineIndex, + int valueIndex) { + if (selectedValue.getFirstIndex() == lineIndex && selectedValue.getSecondIndex() == valueIndex) { + int pointRadius = ChartUtils.dp2px(density, line.getPointRadius()); + pointPaint.setColor(line.getDarkenColor()); + drawPoint(canvas, line, pointValue, rawX, rawY, pointRadius + touchToleranceMargin); + if (line.hasLabels() || line.hasLabelsOnlyForSelected()) { + drawLabel(canvas, line, pointValue, rawX, rawY, pointRadius + labelOffset); + } + } + } + + private void drawLabel(Canvas canvas, Line line, PointValue pointValue, float rawX, float rawY, float offset) { + final Rect contentRect = computator.getContentRectMinusAllMargins(); + final int numChars = line.getFormatter().formatChartValue(labelBuffer, pointValue); + if (numChars == 0) { + // No need to draw empty label + return; + } + + final float labelWidth = labelPaint.measureText(labelBuffer, labelBuffer.length - numChars, numChars); + final int labelHeight = Math.abs(fontMetrics.ascent); + float left = rawX - labelWidth / 2 - labelMargin; + float right = rawX + labelWidth / 2 + labelMargin; + + float top; + float bottom; + + if (pointValue.getY() >= baseValue) { + top = rawY - offset - labelHeight - labelMargin * 2; + bottom = rawY - offset; + } else { + top = rawY + offset; + bottom = rawY + offset + labelHeight + labelMargin * 2; + } + + if (top < contentRect.top) { + top = rawY + offset; + bottom = rawY + offset + labelHeight + labelMargin * 2; + } + if (bottom > contentRect.bottom) { + top = rawY - offset - labelHeight - labelMargin * 2; + bottom = rawY - offset; + } + if (left < contentRect.left) { + left = rawX; + right = rawX + labelWidth + labelMargin * 2; + } + if (right > contentRect.right) { + left = rawX - labelWidth - labelMargin * 2; + right = rawX; + } + + labelBackgroundRect.set(left, top, right, bottom); + drawLabelTextAndBackground(canvas, labelBuffer, labelBuffer.length - numChars, numChars, + line.getDarkenColor()); + } + + private void drawArea(Canvas canvas, Line line) { + final int lineSize = line.getValues().size(); + if (lineSize < 2) { + //No point to draw area for one point or empty line. + return; + } + + final Rect contentRect = computator.getContentRectMinusAllMargins(); + final float baseRawValue = Math.min(contentRect.bottom, Math.max(computator.computeRawY(baseValue), + contentRect.top)); + //That checks works only if the last point is the right most one. + final float left = Math.max(computator.computeRawX(line.getValues().get(0).getX()), contentRect.left); + final float right = Math.min(computator.computeRawX(line.getValues().get(lineSize - 1).getX()), + contentRect.right); + + path.lineTo(right, baseRawValue); + path.lineTo(left, baseRawValue); + path.close(); + + linePaint.setStyle(Paint.Style.FILL); + linePaint.setAlpha(line.getAreaTransparency()); + canvas.drawPath(path, linePaint); + linePaint.setStyle(Paint.Style.STROKE); + } + + private boolean isInArea(float x, float y, float touchX, float touchY, float radius) { + float diffX = touchX - x; + float diffY = touchY - y; + return Math.pow(diffX, 2) + Math.pow(diffY, 2) <= 2 * Math.pow(radius, 2); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/renderer/PieChartRenderer.java b/hellocharts-library/src/lecho/lib/hellocharts/renderer/PieChartRenderer.java index 44793b5e..36ac1925 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/renderer/PieChartRenderer.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/renderer/PieChartRenderer.java @@ -248,7 +248,7 @@ private void drawSlices(Canvas canvas) { final float angle = Math.abs(sliceValue.getValue()) * sliceScale; if (isTouched() && selectedValue.getFirstIndex() == sliceIndex) { drawSlice(canvas, sliceValue, lastAngle, angle, MODE_HIGHLIGHT); - }else{ + } else { drawSlice(canvas, sliceValue, lastAngle, angle, MODE_DRAW); } lastAngle += angle; diff --git a/hellocharts-library/src/lecho/lib/hellocharts/renderer/PreviewColumnChartRenderer.java b/hellocharts-library/src/lecho/lib/hellocharts/renderer/PreviewColumnChartRenderer.java index 7245717b..8817b569 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/renderer/PreviewColumnChartRenderer.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/renderer/PreviewColumnChartRenderer.java @@ -15,41 +15,41 @@ * preview area. */ public class PreviewColumnChartRenderer extends ColumnChartRenderer { - private static final int DEFAULT_PREVIEW_TRANSPARENCY = 64; - private static final int FULL_ALPHA = 255; - private static final int DEFAULT_PREVIEW_STROKE_WIDTH_DP = 2; - - private Paint previewPaint = new Paint(); - - public PreviewColumnChartRenderer(Context context, Chart chart, ColumnChartDataProvider dataProvider) { - super(context, chart, dataProvider); - previewPaint.setAntiAlias(true); - previewPaint.setColor(Color.LTGRAY); - previewPaint.setStrokeWidth(ChartUtils.dp2px(density, DEFAULT_PREVIEW_STROKE_WIDTH_DP)); - } - - @Override - public void drawUnclipped(Canvas canvas) { - super.drawUnclipped(canvas); - final Viewport currentViewport = computator.getCurrentViewport(); - final float left = computator.computeRawX(currentViewport.left); - final float top = computator.computeRawY(currentViewport.top); - final float right = computator.computeRawX(currentViewport.right); - final float bottom = computator.computeRawY(currentViewport.bottom); - previewPaint.setAlpha(DEFAULT_PREVIEW_TRANSPARENCY); - previewPaint.setStyle(Paint.Style.FILL); - canvas.drawRect(left, top, right, bottom, previewPaint); - previewPaint.setStyle(Paint.Style.STROKE); - previewPaint.setAlpha(FULL_ALPHA); - canvas.drawRect(left, top, right, bottom, previewPaint); - } - - public int getPreviewColor() { - return previewPaint.getColor(); - } - - public void setPreviewColor(int color) { - previewPaint.setColor(color); - } + private static final int DEFAULT_PREVIEW_TRANSPARENCY = 64; + private static final int FULL_ALPHA = 255; + private static final int DEFAULT_PREVIEW_STROKE_WIDTH_DP = 2; + + private Paint previewPaint = new Paint(); + + public PreviewColumnChartRenderer(Context context, Chart chart, ColumnChartDataProvider dataProvider) { + super(context, chart, dataProvider); + previewPaint.setAntiAlias(true); + previewPaint.setColor(Color.LTGRAY); + previewPaint.setStrokeWidth(ChartUtils.dp2px(density, DEFAULT_PREVIEW_STROKE_WIDTH_DP)); + } + + @Override + public void drawUnclipped(Canvas canvas) { + super.drawUnclipped(canvas); + final Viewport currentViewport = computator.getCurrentViewport(); + final float left = computator.computeRawX(currentViewport.left); + final float top = computator.computeRawY(currentViewport.top); + final float right = computator.computeRawX(currentViewport.right); + final float bottom = computator.computeRawY(currentViewport.bottom); + previewPaint.setAlpha(DEFAULT_PREVIEW_TRANSPARENCY); + previewPaint.setStyle(Paint.Style.FILL); + canvas.drawRect(left, top, right, bottom, previewPaint); + previewPaint.setStyle(Paint.Style.STROKE); + previewPaint.setAlpha(FULL_ALPHA); + canvas.drawRect(left, top, right, bottom, previewPaint); + } + + public int getPreviewColor() { + return previewPaint.getColor(); + } + + public void setPreviewColor(int color) { + previewPaint.setColor(color); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/renderer/PreviewLineChartRenderer.java b/hellocharts-library/src/lecho/lib/hellocharts/renderer/PreviewLineChartRenderer.java index a8f5337c..7f6cd2c6 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/renderer/PreviewLineChartRenderer.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/renderer/PreviewLineChartRenderer.java @@ -15,40 +15,40 @@ * preview area. */ public class PreviewLineChartRenderer extends LineChartRenderer { - private static final int DEFAULT_PREVIEW_TRANSPARENCY = 64; - private static final int FULL_ALPHA = 255; - private static final int DEFAULT_PREVIEW_STROKE_WIDTH_DP = 2; - - private Paint previewPaint = new Paint(); - - public PreviewLineChartRenderer(Context context, Chart chart, LineChartDataProvider dataProvider) { - super(context, chart, dataProvider); - previewPaint.setAntiAlias(true); - previewPaint.setColor(Color.LTGRAY); - previewPaint.setStrokeWidth(ChartUtils.dp2px(density, DEFAULT_PREVIEW_STROKE_WIDTH_DP)); - } - - @Override - public void drawUnclipped(Canvas canvas) { - super.drawUnclipped(canvas); - final Viewport currentViewport = computator.getCurrentViewport(); - final float left = computator.computeRawX(currentViewport.left); - final float top = computator.computeRawY(currentViewport.top); - final float right = computator.computeRawX(currentViewport.right); - final float bottom = computator.computeRawY(currentViewport.bottom); - previewPaint.setAlpha(DEFAULT_PREVIEW_TRANSPARENCY); - previewPaint.setStyle(Paint.Style.FILL); - canvas.drawRect(left, top, right, bottom, previewPaint); - previewPaint.setStyle(Paint.Style.STROKE); - previewPaint.setAlpha(FULL_ALPHA); - canvas.drawRect(left, top, right, bottom, previewPaint); - } - - public int getPreviewColor() { - return previewPaint.getColor(); - } - - public void setPreviewColor(int color) { - previewPaint.setColor(color); - } + private static final int DEFAULT_PREVIEW_TRANSPARENCY = 64; + private static final int FULL_ALPHA = 255; + private static final int DEFAULT_PREVIEW_STROKE_WIDTH_DP = 2; + + private Paint previewPaint = new Paint(); + + public PreviewLineChartRenderer(Context context, Chart chart, LineChartDataProvider dataProvider) { + super(context, chart, dataProvider); + previewPaint.setAntiAlias(true); + previewPaint.setColor(Color.LTGRAY); + previewPaint.setStrokeWidth(ChartUtils.dp2px(density, DEFAULT_PREVIEW_STROKE_WIDTH_DP)); + } + + @Override + public void drawUnclipped(Canvas canvas) { + super.drawUnclipped(canvas); + final Viewport currentViewport = computator.getCurrentViewport(); + final float left = computator.computeRawX(currentViewport.left); + final float top = computator.computeRawY(currentViewport.top); + final float right = computator.computeRawX(currentViewport.right); + final float bottom = computator.computeRawY(currentViewport.bottom); + previewPaint.setAlpha(DEFAULT_PREVIEW_TRANSPARENCY); + previewPaint.setStyle(Paint.Style.FILL); + canvas.drawRect(left, top, right, bottom, previewPaint); + previewPaint.setStyle(Paint.Style.STROKE); + previewPaint.setAlpha(FULL_ALPHA); + canvas.drawRect(left, top, right, bottom, previewPaint); + } + + public int getPreviewColor() { + return previewPaint.getColor(); + } + + public void setPreviewColor(int color) { + previewPaint.setColor(color); + } } \ No newline at end of file diff --git a/hellocharts-library/src/lecho/lib/hellocharts/util/ChartUtils.java b/hellocharts-library/src/lecho/lib/hellocharts/util/ChartUtils.java index 39e8d44d..40e6ea04 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/util/ChartUtils.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/util/ChartUtils.java @@ -6,65 +6,65 @@ public abstract class ChartUtils { - public static final int DEFAULT_COLOR = Color.parseColor("#DFDFDF"); - public static final int DEFAULT_DARKEN_COLOR = Color.parseColor("#DDDDDD"); - public static final int COLOR_BLUE = Color.parseColor("#33B5E5"); - public static final int COLOR_VIOLET = Color.parseColor("#AA66CC"); - public static final int COLOR_GREEN = Color.parseColor("#99CC00"); - public static final int COLOR_ORANGE = Color.parseColor("#FFBB33"); - public static final int COLOR_RED = Color.parseColor("#FF4444"); - public static final int[] COLORS = new int[]{COLOR_BLUE, COLOR_VIOLET, COLOR_GREEN, COLOR_ORANGE, COLOR_RED}; - private static final float DARKEN_SATURATION = 1.1f; - private static final float DARKEN_INTENSITY = 0.9f; - private static int COLOR_INDEX = 0; + public static final int DEFAULT_COLOR = Color.parseColor("#DFDFDF"); + public static final int DEFAULT_DARKEN_COLOR = Color.parseColor("#DDDDDD"); + public static final int COLOR_BLUE = Color.parseColor("#33B5E5"); + public static final int COLOR_VIOLET = Color.parseColor("#AA66CC"); + public static final int COLOR_GREEN = Color.parseColor("#99CC00"); + public static final int COLOR_ORANGE = Color.parseColor("#FFBB33"); + public static final int COLOR_RED = Color.parseColor("#FF4444"); + public static final int[] COLORS = new int[]{COLOR_BLUE, COLOR_VIOLET, COLOR_GREEN, COLOR_ORANGE, COLOR_RED}; + private static final float DARKEN_SATURATION = 1.1f; + private static final float DARKEN_INTENSITY = 0.9f; + private static int COLOR_INDEX = 0; - public static final int pickColor() { - return COLORS[(int) Math.round(Math.random() * (COLORS.length - 1))]; - } + public static final int pickColor() { + return COLORS[(int) Math.round(Math.random() * (COLORS.length - 1))]; + } - public static final int nextColor() { - if (COLOR_INDEX >= COLORS.length) { - COLOR_INDEX = 0; - } - return COLORS[COLOR_INDEX++]; - } + public static final int nextColor() { + if (COLOR_INDEX >= COLORS.length) { + COLOR_INDEX = 0; + } + return COLORS[COLOR_INDEX++]; + } - public static int dp2px(float density, int dp) { - if (dp == 0) { - return 0; - } - return (int) (dp * density + 0.5f); + public static int dp2px(float density, int dp) { + if (dp == 0) { + return 0; + } + return (int) (dp * density + 0.5f); - } + } - public static int px2dp(float density, int px) { - return (int) Math.ceil(px / density); - } + public static int px2dp(float density, int px) { + return (int) Math.ceil(px / density); + } - public static int sp2px(float scaledDensity, int sp) { - if(sp == 0){ + public static int sp2px(float scaledDensity, int sp) { + if (sp == 0) { return 0; } - return (int) (sp * scaledDensity + 0.5f); - } + return (int) (sp * scaledDensity + 0.5f); + } - public static int px2sp(float scaledDensity, int px) { - return (int) Math.ceil(px / scaledDensity); - } + public static int px2sp(float scaledDensity, int px) { + return (int) Math.ceil(px / scaledDensity); + } - public static int mm2px(Context context, int mm) { - return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, mm, context.getResources() - .getDisplayMetrics()) + 0.5f); - } + public static int mm2px(Context context, int mm) { + return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, mm, context.getResources() + .getDisplayMetrics()) + 0.5f); + } - public static int darkenColor(int color) { - float[] hsv = new float[3]; - int alpha = Color.alpha(color); - Color.colorToHSV(color, hsv); - hsv[1] = Math.min(hsv[1] * DARKEN_SATURATION, 1.0f); - hsv[2] = hsv[2] * DARKEN_INTENSITY; - int tempColor = Color.HSVToColor(hsv); - return Color.argb(alpha, Color.red(tempColor), Color.green(tempColor), Color.blue(tempColor)); - } + public static int darkenColor(int color) { + float[] hsv = new float[3]; + int alpha = Color.alpha(color); + Color.colorToHSV(color, hsv); + hsv[1] = Math.min(hsv[1] * DARKEN_SATURATION, 1.0f); + hsv[2] = hsv[2] * DARKEN_INTENSITY; + int tempColor = Color.HSVToColor(hsv); + return Color.argb(alpha, Color.red(tempColor), Color.green(tempColor), Color.blue(tempColor)); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/util/FloatUtils.java b/hellocharts-library/src/lecho/lib/hellocharts/util/FloatUtils.java index 91f5244f..8e1285a7 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/util/FloatUtils.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/util/FloatUtils.java @@ -1,195 +1,195 @@ package lecho.lib.hellocharts.util; public class FloatUtils { - public static final int POW10[] = {1, 10, 100, 1000, 10000, 100000, 1000000}; - - /** - * Returns next bigger float value considering precision of the argument. - */ - public static float nextUpF(float f) { - if (Float.isNaN(f) || f == Float.POSITIVE_INFINITY) { - return f; - } else { - f += 0.0f; - return Float.intBitsToFloat(Float.floatToRawIntBits(f) + ((f >= 0.0f) ? +1 : -1)); - } - } - - /** - * Returns next smaller float value considering precision of the argument. - */ - public static float nextDownF(float f) { - if (Float.isNaN(f) || f == Float.NEGATIVE_INFINITY) { - return f; - } else { - if (f == 0.0f) { - return -Float.MIN_VALUE; - } else { - return Float.intBitsToFloat(Float.floatToRawIntBits(f) + ((f > 0.0f) ? -1 : +1)); - } - } - } - - /** - * Returns next bigger double value considering precision of the argument. - */ - public static double nextUp(double d) { - if (Double.isNaN(d) || d == Double.POSITIVE_INFINITY) { - return d; - } else { - d += 0.0; - return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + ((d >= 0.0) ? +1 : -1)); - } - } - - /** - * Returns next smaller float value considering precision of the argument. - */ - public static double nextDown(double d) { - if (Double.isNaN(d) || d == Double.NEGATIVE_INFINITY) { - return d; - } else { - if (d == 0.0f) { - return -Float.MIN_VALUE; - } else { - return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + ((d > 0.0f) ? -1 : +1)); - } - } - } - - /** - * Method checks if two float numbers are similar. - */ - public static boolean almostEqual(float a, float b, float absoluteDiff, float relativeDiff) { - float diff = Math.abs(a - b); - if (diff <= absoluteDiff) { - return true; - } - - a = Math.abs(a); - b = Math.abs(b); - float largest = (a > b) ? a : b; - - if (diff <= largest * relativeDiff) { - return true; - } - - return false; - } - - /** - * Rounds the given number to the given number of significant digits. Based on an answer on Stack Overflow. - */ - public static float roundToOneSignificantFigure(double num) { - final float d = (float) Math.ceil((float) Math.log10(num < 0 ? -num : num)); - final int power = 1 - (int) d; - final float magnitude = (float) Math.pow(10, power); - final long shifted = Math.round(num * magnitude); - return shifted / magnitude; - } - - /** - * Formats a float value to the given number of decimals. Returns the length of the string. The string begins at - * [endIndex] - [return value] and ends at [endIndex]. It's up to you to check indexes correctness. - * Parameter [endIndex] can be helpful when you want to append some text to formatted value. - * - * @return number of characters of formatted value - */ - public static int formatFloat(final char[] formattedValue, float value, int endIndex, int digits, char separator) { - if (digits >= POW10.length) { - formattedValue[endIndex - 1] = '.'; - return 1; - } - boolean negative = false; - if (value == 0) { - formattedValue[endIndex - 1] = '0'; - return 1; - } - if (value < 0) { - negative = true; - value = -value; - } - if (digits > POW10.length) { - digits = POW10.length - 1; - } - value *= POW10[digits]; - long lval = Math.round(value); - int index = endIndex - 1; - int charsNumber = 0; - while (lval != 0 || charsNumber < (digits + 1)) { - int digit = (int) (lval % 10); - lval = lval / 10; - formattedValue[index--] = (char) (digit + '0'); - charsNumber++; - if (charsNumber == digits) { - formattedValue[index--] = separator; - charsNumber++; - } - } - if (formattedValue[index + 1] == separator) { - formattedValue[index--] = '0'; - charsNumber++; - } - if (negative) { - formattedValue[index--] = '-'; - charsNumber++; - } - return charsNumber; - } - - /** - * Computes the set of axis labels to show given start and stop boundaries and an ideal number of stops between - * these boundaries. - * - * @param start The minimum extreme (e.g. the left edge) for the axis. - * @param stop The maximum extreme (e.g. the right edge) for the axis. - * @param steps The ideal number of stops to create. This should be based on available screen space; the more space - * there is, the more stops should be shown. - * @param outValues The destination {@link AxisAutoValues} object to populate. - */ - public static void computeAutoGeneratedAxisValues(float start, float stop, int steps, AxisAutoValues outValues) { - double range = stop - start; - if (steps == 0 || range <= 0) { - outValues.values = new float[]{}; - outValues.valuesNumber = 0; - return; - } - - double rawInterval = range / steps; - double interval = roundToOneSignificantFigure(rawInterval); - double intervalMagnitude = Math.pow(10, (int) Math.log10(interval)); - int intervalSigDigit = (int) (interval / intervalMagnitude); - if (intervalSigDigit > 5) { - // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 - interval = Math.floor(10 * intervalMagnitude); - } - - double first = Math.ceil(start / interval) * interval; - double last = nextUp(Math.floor(stop / interval) * interval); - - double intervalValue; - int valueIndex; - int valuesNum = 0; - for (intervalValue = first; intervalValue <= last; intervalValue += interval) { - ++valuesNum; - } - - outValues.valuesNumber = valuesNum; - - if (outValues.values.length < valuesNum) { - // Ensure stops contains at least numStops elements. - outValues.values = new float[valuesNum]; - } - - for (intervalValue = first, valueIndex = 0; valueIndex < valuesNum; intervalValue += interval, ++valueIndex) { - outValues.values[valueIndex] = (float) intervalValue; - } - - if (interval < 1) { - outValues.decimals = (int) Math.ceil(-Math.log10(interval)); - } else { - outValues.decimals = 0; - } - } + public static final int POW10[] = {1, 10, 100, 1000, 10000, 100000, 1000000}; + + /** + * Returns next bigger float value considering precision of the argument. + */ + public static float nextUpF(float f) { + if (Float.isNaN(f) || f == Float.POSITIVE_INFINITY) { + return f; + } else { + f += 0.0f; + return Float.intBitsToFloat(Float.floatToRawIntBits(f) + ((f >= 0.0f) ? +1 : -1)); + } + } + + /** + * Returns next smaller float value considering precision of the argument. + */ + public static float nextDownF(float f) { + if (Float.isNaN(f) || f == Float.NEGATIVE_INFINITY) { + return f; + } else { + if (f == 0.0f) { + return -Float.MIN_VALUE; + } else { + return Float.intBitsToFloat(Float.floatToRawIntBits(f) + ((f > 0.0f) ? -1 : +1)); + } + } + } + + /** + * Returns next bigger double value considering precision of the argument. + */ + public static double nextUp(double d) { + if (Double.isNaN(d) || d == Double.POSITIVE_INFINITY) { + return d; + } else { + d += 0.0; + return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + ((d >= 0.0) ? +1 : -1)); + } + } + + /** + * Returns next smaller float value considering precision of the argument. + */ + public static double nextDown(double d) { + if (Double.isNaN(d) || d == Double.NEGATIVE_INFINITY) { + return d; + } else { + if (d == 0.0f) { + return -Float.MIN_VALUE; + } else { + return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + ((d > 0.0f) ? -1 : +1)); + } + } + } + + /** + * Method checks if two float numbers are similar. + */ + public static boolean almostEqual(float a, float b, float absoluteDiff, float relativeDiff) { + float diff = Math.abs(a - b); + if (diff <= absoluteDiff) { + return true; + } + + a = Math.abs(a); + b = Math.abs(b); + float largest = (a > b) ? a : b; + + if (diff <= largest * relativeDiff) { + return true; + } + + return false; + } + + /** + * Rounds the given number to the given number of significant digits. Based on an answer on Stack Overflow. + */ + public static float roundToOneSignificantFigure(double num) { + final float d = (float) Math.ceil((float) Math.log10(num < 0 ? -num : num)); + final int power = 1 - (int) d; + final float magnitude = (float) Math.pow(10, power); + final long shifted = Math.round(num * magnitude); + return shifted / magnitude; + } + + /** + * Formats a float value to the given number of decimals. Returns the length of the string. The string begins at + * [endIndex] - [return value] and ends at [endIndex]. It's up to you to check indexes correctness. + * Parameter [endIndex] can be helpful when you want to append some text to formatted value. + * + * @return number of characters of formatted value + */ + public static int formatFloat(final char[] formattedValue, float value, int endIndex, int digits, char separator) { + if (digits >= POW10.length) { + formattedValue[endIndex - 1] = '.'; + return 1; + } + boolean negative = false; + if (value == 0) { + formattedValue[endIndex - 1] = '0'; + return 1; + } + if (value < 0) { + negative = true; + value = -value; + } + if (digits > POW10.length) { + digits = POW10.length - 1; + } + value *= POW10[digits]; + long lval = Math.round(value); + int index = endIndex - 1; + int charsNumber = 0; + while (lval != 0 || charsNumber < (digits + 1)) { + int digit = (int) (lval % 10); + lval = lval / 10; + formattedValue[index--] = (char) (digit + '0'); + charsNumber++; + if (charsNumber == digits) { + formattedValue[index--] = separator; + charsNumber++; + } + } + if (formattedValue[index + 1] == separator) { + formattedValue[index--] = '0'; + charsNumber++; + } + if (negative) { + formattedValue[index--] = '-'; + charsNumber++; + } + return charsNumber; + } + + /** + * Computes the set of axis labels to show given start and stop boundaries and an ideal number of stops between + * these boundaries. + * + * @param start The minimum extreme (e.g. the left edge) for the axis. + * @param stop The maximum extreme (e.g. the right edge) for the axis. + * @param steps The ideal number of stops to create. This should be based on available screen space; the more space + * there is, the more stops should be shown. + * @param outValues The destination {@link AxisAutoValues} object to populate. + */ + public static void computeAutoGeneratedAxisValues(float start, float stop, int steps, AxisAutoValues outValues) { + double range = stop - start; + if (steps == 0 || range <= 0) { + outValues.values = new float[]{}; + outValues.valuesNumber = 0; + return; + } + + double rawInterval = range / steps; + double interval = roundToOneSignificantFigure(rawInterval); + double intervalMagnitude = Math.pow(10, (int) Math.log10(interval)); + int intervalSigDigit = (int) (interval / intervalMagnitude); + if (intervalSigDigit > 5) { + // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 + interval = Math.floor(10 * intervalMagnitude); + } + + double first = Math.ceil(start / interval) * interval; + double last = nextUp(Math.floor(stop / interval) * interval); + + double intervalValue; + int valueIndex; + int valuesNum = 0; + for (intervalValue = first; intervalValue <= last; intervalValue += interval) { + ++valuesNum; + } + + outValues.valuesNumber = valuesNum; + + if (outValues.values.length < valuesNum) { + // Ensure stops contains at least numStops elements. + outValues.values = new float[valuesNum]; + } + + for (intervalValue = first, valueIndex = 0; valueIndex < valuesNum; intervalValue += interval, ++valueIndex) { + outValues.values[valueIndex] = (float) intervalValue; + } + + if (interval < 1) { + outValues.decimals = (int) Math.ceil(-Math.log10(interval)); + } else { + outValues.decimals = 0; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/view/AbstractChartView.java b/hellocharts-library/src/lecho/lib/hellocharts/view/AbstractChartView.java index 2f38d011..3c8f95dd 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/view/AbstractChartView.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/view/AbstractChartView.java @@ -1,7 +1,13 @@ package lecho.lib.hellocharts.view; -import lecho.lib.hellocharts.computator.ChartComputator; -import lecho.lib.hellocharts.listener.ViewportChangeListener; +import android.content.Context; +import android.graphics.Canvas; +import android.os.Build; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + import lecho.lib.hellocharts.animation.ChartAnimationListener; import lecho.lib.hellocharts.animation.ChartDataAnimator; import lecho.lib.hellocharts.animation.ChartDataAnimatorV14; @@ -9,478 +15,472 @@ import lecho.lib.hellocharts.animation.ChartViewportAnimator; import lecho.lib.hellocharts.animation.ChartViewportAnimatorV14; import lecho.lib.hellocharts.animation.ChartViewportAnimatorV8; +import lecho.lib.hellocharts.computator.ChartComputator; import lecho.lib.hellocharts.gesture.ChartTouchHandler; import lecho.lib.hellocharts.gesture.ContainerScrollType; import lecho.lib.hellocharts.gesture.ZoomType; +import lecho.lib.hellocharts.listener.ViewportChangeListener; import lecho.lib.hellocharts.model.SelectedValue; import lecho.lib.hellocharts.model.Viewport; import lecho.lib.hellocharts.renderer.AxesRenderer; import lecho.lib.hellocharts.renderer.ChartRenderer; import lecho.lib.hellocharts.util.ChartUtils; -import android.content.Context; -import android.graphics.Canvas; -import android.os.Build; -import android.support.v4.view.ViewCompat; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; - /** * Abstract class for charts views. * * @author Leszek Wach */ public abstract class AbstractChartView extends View implements Chart { - protected ChartComputator chartComputator; - protected AxesRenderer axesRenderer; - protected ChartTouchHandler touchHandler; - protected ChartRenderer chartRenderer; - protected ChartDataAnimator dataAnimator; - protected ChartViewportAnimator viewportAnimator; - protected boolean isInteractive = true; - protected boolean isContainerScrollEnabled = false; - protected ContainerScrollType containerScrollType; - - public AbstractChartView(Context context) { - this(context, null, 0); - } - - public AbstractChartView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public AbstractChartView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - chartComputator = new ChartComputator(); - touchHandler = new ChartTouchHandler(context, this); - axesRenderer = new AxesRenderer(context, this); - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - this.dataAnimator = new ChartDataAnimatorV8(this); - this.viewportAnimator = new ChartViewportAnimatorV8(this); - } else { - this.viewportAnimator = new ChartViewportAnimatorV14(this); - this.dataAnimator = new ChartDataAnimatorV14(this); - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - @Override - protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { - super.onSizeChanged(width, height, oldWidth, oldHeight); - chartComputator.setContentRect(getWidth(), getHeight(), getPaddingLeft(), getPaddingTop(), getPaddingRight(), - getPaddingBottom()); - chartRenderer.onChartSizeChanged(); - axesRenderer.onChartSizeChanged(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - if (isEnabled()) { - axesRenderer.drawInBackground(canvas); - int clipRestoreCount = canvas.save(); - canvas.clipRect(chartComputator.getContentRectMinusAllMargins()); - chartRenderer.draw(canvas); - canvas.restoreToCount(clipRestoreCount); - chartRenderer.drawUnclipped(canvas); - axesRenderer.drawInForeground(canvas); - } else { - canvas.drawColor(ChartUtils.DEFAULT_COLOR); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - super.onTouchEvent(event); - - if (isInteractive) { - - boolean needInvalidate; - - if (isContainerScrollEnabled) { - needInvalidate = touchHandler.handleTouchEvent(event, getParent(), containerScrollType); - } else { - needInvalidate = touchHandler.handleTouchEvent(event); - } - - if (needInvalidate) { - ViewCompat.postInvalidateOnAnimation(this); - } - - return true; - } else { - - return false; - } - } - - @Override - public void computeScroll() { - super.computeScroll(); - if (isInteractive) { - if (touchHandler.computeScroll()) { - ViewCompat.postInvalidateOnAnimation(this); - } - } - } - - @Override - public void startDataAnimation() { - dataAnimator.startAnimation(Long.MIN_VALUE); - } - - @Override - public void startDataAnimation(long duration) { - dataAnimator.startAnimation(duration); - } - - @Override - public void cancelDataAnimation() { - dataAnimator.cancelAnimation(); - } - - @Override - public void animationDataUpdate(float scale) { - getChartData().update(scale); - chartRenderer.onChartViewportChanged(); - ViewCompat.postInvalidateOnAnimation(this); - } - - @Override - public void animationDataFinished() { - getChartData().finish(); - chartRenderer.onChartViewportChanged(); - ViewCompat.postInvalidateOnAnimation(this); - } - - @Override - public void setDataAnimationListener(ChartAnimationListener animationListener) { - dataAnimator.setChartAnimationListener(animationListener); - } - - @Override - public void setViewportAnimationListener(ChartAnimationListener animationListener) { - viewportAnimator.setChartAnimationListener(animationListener); - } - - @Override - public void setViewportChangeListener(ViewportChangeListener viewportChangeListener) { - chartComputator.setViewportChangeListener(viewportChangeListener); - } - - @Override - public ChartRenderer getChartRenderer() { - return chartRenderer; - } - - @Override - public void setChartRenderer(ChartRenderer renderer) { - chartRenderer = renderer; - resetRendererAndTouchHandler(); - ViewCompat.postInvalidateOnAnimation(this); - } - - @Override - public AxesRenderer getAxesRenderer() { - return axesRenderer; - } - - @Override - public ChartComputator getChartComputator() { - return chartComputator; - } - - @Override - public ChartTouchHandler getTouchHandler() { - return touchHandler; - } - - @Override - public boolean isInteractive() { - return isInteractive; - } - - @Override - public void setInteractive(boolean isInteractive) { - this.isInteractive = isInteractive; - } - - @Override - public boolean isZoomEnabled() { - return touchHandler.isZoomEnabled(); - } - - @Override - public void setZoomEnabled(boolean isZoomEnabled) { - touchHandler.setZoomEnabled(isZoomEnabled); - } - - @Override - public boolean isScrollEnabled() { - return touchHandler.isScrollEnabled(); - } - - @Override - public void setScrollEnabled(boolean isScrollEnabled) { - touchHandler.setScrollEnabled(isScrollEnabled); - } - - @Override - public void moveTo(float x, float y) { - Viewport scrollViewport = computeScrollViewport(x, y); - setCurrentViewport(scrollViewport); - } - - @Override - public void moveToWithAnimation(float x, float y) { - Viewport scrollViewport = computeScrollViewport(x, y); - setCurrentViewportWithAnimation(scrollViewport); - } - - private Viewport computeScrollViewport(float x, float y){ - Viewport maxViewport = getMaximumViewport(); - Viewport currentViewport = getCurrentViewport(); - Viewport scrollViewport = new Viewport(currentViewport); - - if (maxViewport.contains(x, y)) { - final float width = currentViewport.width(); - final float height = currentViewport.height(); - - final float halfWidth = width / 2; - final float halfHeight = height / 2; - - float left = x - halfWidth; - float top = y + halfHeight; - - left = Math.max(maxViewport.left, Math.min(left, maxViewport.right - width)); - top = Math.max(maxViewport.bottom + height, Math.min(top, maxViewport.top)); - - scrollViewport.set(left, top, left + height, top - height); - } - - return scrollViewport; - } - - @Override - public boolean isValueTouchEnabled() { - return touchHandler.isValueTouchEnabled(); - } - - @Override - public void setValueTouchEnabled(boolean isValueTouchEnabled) { - touchHandler.setValueTouchEnabled(isValueTouchEnabled); - - } - - @Override - public ZoomType getZoomType() { - return touchHandler.getZoomType(); - } - - @Override - public void setZoomType(ZoomType zoomType) { - touchHandler.setZoomType(zoomType); - } - - @Override - public float getMaxZoom() { - return chartComputator.getMaxZoom(); - } - - @Override - public void setMaxZoom(float maxZoom) { - chartComputator.setMaxZoom(maxZoom); - ViewCompat.postInvalidateOnAnimation(this); - } - - @Override - public float getZoomLevel() { - Viewport maxViewport = getMaximumViewport(); - Viewport currentViewport = getCurrentViewport(); - - return Math.max(maxViewport.width() / currentViewport.width(), maxViewport.height() / currentViewport.height()); - - } - - @Override - public void setZoomLevel(float x, float y, float zoomLevel) { - Viewport zoomViewport = computeZoomViewport(x, y, zoomLevel); - setCurrentViewport(zoomViewport); - } - - @Override - public void setZoomLevelWithAnimation(float x, float y, float zoomLevel) { - Viewport zoomViewport = computeZoomViewport(x, y, zoomLevel); - setCurrentViewportWithAnimation(zoomViewport); - } - - private Viewport computeZoomViewport(float x, float y, float zoomLevel) { - final Viewport maxViewport = getMaximumViewport(); - Viewport zoomViewport = new Viewport(getMaximumViewport()); - - if (maxViewport.contains(x, y)) { - - if (zoomLevel < 1) { - zoomLevel = 1; - } else if (zoomLevel > getMaxZoom()) { - zoomLevel = getMaxZoom(); - } - - final float newWidth = zoomViewport.width() / zoomLevel; - final float newHeight = zoomViewport.height() / zoomLevel; - - final float halfWidth = newWidth / 2; - final float halfHeight = newHeight / 2; - - float left = x - halfWidth; - float right = x + halfWidth; - float top = y + halfHeight; - float bottom = y - halfHeight; - - if (left < maxViewport.left) { - left = maxViewport.left; - right = left + newWidth; - } else if (right > maxViewport.right) { - right = maxViewport.right; - left = right - newWidth; - } - - if (top > maxViewport.top) { - top = maxViewport.top; - bottom = top - newHeight; - } else if (bottom < maxViewport.bottom) { - bottom = maxViewport.bottom; - top = bottom + newHeight; - } - - ZoomType zoomType = getZoomType(); - if (ZoomType.HORIZONTAL_AND_VERTICAL == zoomType) { - zoomViewport.set(left, top, right, bottom); - } else if (ZoomType.HORIZONTAL == zoomType) { - zoomViewport.left = left; - zoomViewport.right = right; - } else if (ZoomType.VERTICAL == zoomType) { - zoomViewport.top = top; - zoomViewport.bottom = bottom; - } - - } - return zoomViewport; - } - - @Override - public void setMaximumViewport(Viewport maxViewport) { - chartRenderer.setMaximumViewport(maxViewport); - ViewCompat.postInvalidateOnAnimation(this); - } - - @Override - public Viewport getMaximumViewport() { - return chartRenderer.getMaximumViewport(); - } - - @Override - public void setCurrentViewport(Viewport targetViewport) { - if (null != targetViewport) { - chartRenderer.setCurrentViewport(targetViewport); - } - ViewCompat.postInvalidateOnAnimation(this); - } - - @Override - public void setCurrentViewportWithAnimation(Viewport targetViewport) { - if (null != targetViewport) { - viewportAnimator.cancelAnimation(); - viewportAnimator.startAnimation(getCurrentViewport(), targetViewport); - } - ViewCompat.postInvalidateOnAnimation(this); - } - - @Override - public void setCurrentViewportWithAnimation(Viewport targetViewport, long duration) { - if (null != targetViewport) { - viewportAnimator.cancelAnimation(); - viewportAnimator.startAnimation(getCurrentViewport(), targetViewport, duration); - } - ViewCompat.postInvalidateOnAnimation(this); - } - - @Override - public Viewport getCurrentViewport() { - return getChartRenderer().getCurrentViewport(); - } - - @Override - public void resetViewports() { - chartRenderer.setMaximumViewport(null); - chartRenderer.setCurrentViewport(null); - } - - @Override - public boolean isViewportCalculationEnabled() { - return chartRenderer.isViewportCalculationEnabled(); - } - - @Override - public void setViewportCalculationEnabled(boolean isEnabled) { - chartRenderer.setViewportCalculationEnabled(isEnabled); - } - - @Override - public boolean isValueSelectionEnabled() { - return touchHandler.isValueSelectionEnabled(); - } - - @Override - public void setValueSelectionEnabled(boolean isValueSelectionEnabled) { - touchHandler.setValueSelectionEnabled(isValueSelectionEnabled); - } - - @Override - public void selectValue(SelectedValue selectedValue) { - chartRenderer.selectValue(selectedValue); - callTouchListener(); - ViewCompat.postInvalidateOnAnimation(this); - } - - @Override - public SelectedValue getSelectedValue() { - return chartRenderer.getSelectedValue(); - } - - @Override - public boolean isContainerScrollEnabled() { - return isContainerScrollEnabled; - } - - @Override - public void setContainerScrollEnabled(boolean isContainerScrollEnabled, ContainerScrollType containerScrollType) { - this.isContainerScrollEnabled = isContainerScrollEnabled; - this.containerScrollType = containerScrollType; - } - - protected void onChartDataChange(){ - chartComputator.resetContentRect(); - chartRenderer.onChartDataChanged(); - axesRenderer.onChartDataChanged(); - ViewCompat.postInvalidateOnAnimation(this); - } - - /** - * You should call this method in derived classes, most likely from constructor if you changed chart/axis renderer, - * touch handler or chart computator - */ - protected void resetRendererAndTouchHandler(){ - this.chartRenderer.resetRenderer(); - this.axesRenderer.resetRenderer(); - this.touchHandler.resetTouchHandler(); - } + protected ChartComputator chartComputator; + protected AxesRenderer axesRenderer; + protected ChartTouchHandler touchHandler; + protected ChartRenderer chartRenderer; + protected ChartDataAnimator dataAnimator; + protected ChartViewportAnimator viewportAnimator; + protected boolean isInteractive = true; + protected boolean isContainerScrollEnabled = false; + protected ContainerScrollType containerScrollType; + + public AbstractChartView(Context context) { + this(context, null, 0); + } + + public AbstractChartView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AbstractChartView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + chartComputator = new ChartComputator(); + touchHandler = new ChartTouchHandler(context, this); + axesRenderer = new AxesRenderer(context, this); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + this.dataAnimator = new ChartDataAnimatorV8(this); + this.viewportAnimator = new ChartViewportAnimatorV8(this); + } else { + this.viewportAnimator = new ChartViewportAnimatorV14(this); + this.dataAnimator = new ChartDataAnimatorV14(this); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { + super.onSizeChanged(width, height, oldWidth, oldHeight); + chartComputator.setContentRect(getWidth(), getHeight(), getPaddingLeft(), getPaddingTop(), getPaddingRight(), + getPaddingBottom()); + chartRenderer.onChartSizeChanged(); + axesRenderer.onChartSizeChanged(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (isEnabled()) { + axesRenderer.drawInBackground(canvas); + int clipRestoreCount = canvas.save(); + canvas.clipRect(chartComputator.getContentRectMinusAllMargins()); + chartRenderer.draw(canvas); + canvas.restoreToCount(clipRestoreCount); + chartRenderer.drawUnclipped(canvas); + axesRenderer.drawInForeground(canvas); + } else { + canvas.drawColor(ChartUtils.DEFAULT_COLOR); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + + if (isInteractive) { + + boolean needInvalidate; + + if (isContainerScrollEnabled) { + needInvalidate = touchHandler.handleTouchEvent(event, getParent(), containerScrollType); + } else { + needInvalidate = touchHandler.handleTouchEvent(event); + } + + if (needInvalidate) { + ViewCompat.postInvalidateOnAnimation(this); + } + + return true; + } else { + + return false; + } + } + + @Override + public void computeScroll() { + super.computeScroll(); + if (isInteractive) { + if (touchHandler.computeScroll()) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + } + + @Override + public void startDataAnimation() { + dataAnimator.startAnimation(Long.MIN_VALUE); + } + + @Override + public void startDataAnimation(long duration) { + dataAnimator.startAnimation(duration); + } + + @Override + public void cancelDataAnimation() { + dataAnimator.cancelAnimation(); + } + + @Override + public void animationDataUpdate(float scale) { + getChartData().update(scale); + chartRenderer.onChartViewportChanged(); + ViewCompat.postInvalidateOnAnimation(this); + } + + @Override + public void animationDataFinished() { + getChartData().finish(); + chartRenderer.onChartViewportChanged(); + ViewCompat.postInvalidateOnAnimation(this); + } + + @Override + public void setDataAnimationListener(ChartAnimationListener animationListener) { + dataAnimator.setChartAnimationListener(animationListener); + } + + @Override + public void setViewportAnimationListener(ChartAnimationListener animationListener) { + viewportAnimator.setChartAnimationListener(animationListener); + } + + @Override + public void setViewportChangeListener(ViewportChangeListener viewportChangeListener) { + chartComputator.setViewportChangeListener(viewportChangeListener); + } + + @Override + public ChartRenderer getChartRenderer() { + return chartRenderer; + } + + @Override + public void setChartRenderer(ChartRenderer renderer) { + chartRenderer = renderer; + resetRendererAndTouchHandler(); + ViewCompat.postInvalidateOnAnimation(this); + } + + @Override + public AxesRenderer getAxesRenderer() { + return axesRenderer; + } + + @Override + public ChartComputator getChartComputator() { + return chartComputator; + } + + @Override + public ChartTouchHandler getTouchHandler() { + return touchHandler; + } + + @Override + public boolean isInteractive() { + return isInteractive; + } + + @Override + public void setInteractive(boolean isInteractive) { + this.isInteractive = isInteractive; + } + + @Override + public boolean isZoomEnabled() { + return touchHandler.isZoomEnabled(); + } + + @Override + public void setZoomEnabled(boolean isZoomEnabled) { + touchHandler.setZoomEnabled(isZoomEnabled); + } + + @Override + public boolean isScrollEnabled() { + return touchHandler.isScrollEnabled(); + } + + @Override + public void setScrollEnabled(boolean isScrollEnabled) { + touchHandler.setScrollEnabled(isScrollEnabled); + } + + @Override + public void moveTo(float x, float y) { + Viewport scrollViewport = computeScrollViewport(x, y); + setCurrentViewport(scrollViewport); + } + + @Override + public void moveToWithAnimation(float x, float y) { + Viewport scrollViewport = computeScrollViewport(x, y); + setCurrentViewportWithAnimation(scrollViewport); + } + + private Viewport computeScrollViewport(float x, float y) { + Viewport maxViewport = getMaximumViewport(); + Viewport currentViewport = getCurrentViewport(); + Viewport scrollViewport = new Viewport(currentViewport); + + if (maxViewport.contains(x, y)) { + final float width = currentViewport.width(); + final float height = currentViewport.height(); + + final float halfWidth = width / 2; + final float halfHeight = height / 2; + + float left = x - halfWidth; + float top = y + halfHeight; + + left = Math.max(maxViewport.left, Math.min(left, maxViewport.right - width)); + top = Math.max(maxViewport.bottom + height, Math.min(top, maxViewport.top)); + + scrollViewport.set(left, top, left + height, top - height); + } + + return scrollViewport; + } + + @Override + public boolean isValueTouchEnabled() { + return touchHandler.isValueTouchEnabled(); + } + + @Override + public void setValueTouchEnabled(boolean isValueTouchEnabled) { + touchHandler.setValueTouchEnabled(isValueTouchEnabled); + + } + + @Override + public ZoomType getZoomType() { + return touchHandler.getZoomType(); + } + + @Override + public void setZoomType(ZoomType zoomType) { + touchHandler.setZoomType(zoomType); + } + + @Override + public float getMaxZoom() { + return chartComputator.getMaxZoom(); + } + + @Override + public void setMaxZoom(float maxZoom) { + chartComputator.setMaxZoom(maxZoom); + ViewCompat.postInvalidateOnAnimation(this); + } + + @Override + public float getZoomLevel() { + Viewport maxViewport = getMaximumViewport(); + Viewport currentViewport = getCurrentViewport(); + + return Math.max(maxViewport.width() / currentViewport.width(), maxViewport.height() / currentViewport.height()); + + } + + @Override + public void setZoomLevel(float x, float y, float zoomLevel) { + Viewport zoomViewport = computeZoomViewport(x, y, zoomLevel); + setCurrentViewport(zoomViewport); + } + + @Override + public void setZoomLevelWithAnimation(float x, float y, float zoomLevel) { + Viewport zoomViewport = computeZoomViewport(x, y, zoomLevel); + setCurrentViewportWithAnimation(zoomViewport); + } + + private Viewport computeZoomViewport(float x, float y, float zoomLevel) { + final Viewport maxViewport = getMaximumViewport(); + Viewport zoomViewport = new Viewport(getMaximumViewport()); + + if (maxViewport.contains(x, y)) { + + if (zoomLevel < 1) { + zoomLevel = 1; + } else if (zoomLevel > getMaxZoom()) { + zoomLevel = getMaxZoom(); + } + + final float newWidth = zoomViewport.width() / zoomLevel; + final float newHeight = zoomViewport.height() / zoomLevel; + + final float halfWidth = newWidth / 2; + final float halfHeight = newHeight / 2; + + float left = x - halfWidth; + float right = x + halfWidth; + float top = y + halfHeight; + float bottom = y - halfHeight; + + if (left < maxViewport.left) { + left = maxViewport.left; + right = left + newWidth; + } else if (right > maxViewport.right) { + right = maxViewport.right; + left = right - newWidth; + } + + if (top > maxViewport.top) { + top = maxViewport.top; + bottom = top - newHeight; + } else if (bottom < maxViewport.bottom) { + bottom = maxViewport.bottom; + top = bottom + newHeight; + } + + ZoomType zoomType = getZoomType(); + if (ZoomType.HORIZONTAL_AND_VERTICAL == zoomType) { + zoomViewport.set(left, top, right, bottom); + } else if (ZoomType.HORIZONTAL == zoomType) { + zoomViewport.left = left; + zoomViewport.right = right; + } else if (ZoomType.VERTICAL == zoomType) { + zoomViewport.top = top; + zoomViewport.bottom = bottom; + } + + } + return zoomViewport; + } + + @Override + public void setMaximumViewport(Viewport maxViewport) { + chartRenderer.setMaximumViewport(maxViewport); + ViewCompat.postInvalidateOnAnimation(this); + } + + @Override + public Viewport getMaximumViewport() { + return chartRenderer.getMaximumViewport(); + } + + @Override + public void setCurrentViewport(Viewport targetViewport) { + if (null != targetViewport) { + chartRenderer.setCurrentViewport(targetViewport); + } + ViewCompat.postInvalidateOnAnimation(this); + } + + @Override + public void setCurrentViewportWithAnimation(Viewport targetViewport) { + if (null != targetViewport) { + viewportAnimator.cancelAnimation(); + viewportAnimator.startAnimation(getCurrentViewport(), targetViewport); + } + ViewCompat.postInvalidateOnAnimation(this); + } + + @Override + public void setCurrentViewportWithAnimation(Viewport targetViewport, long duration) { + if (null != targetViewport) { + viewportAnimator.cancelAnimation(); + viewportAnimator.startAnimation(getCurrentViewport(), targetViewport, duration); + } + ViewCompat.postInvalidateOnAnimation(this); + } + + @Override + public Viewport getCurrentViewport() { + return getChartRenderer().getCurrentViewport(); + } + + @Override + public void resetViewports() { + chartRenderer.setMaximumViewport(null); + chartRenderer.setCurrentViewport(null); + } + + @Override + public boolean isViewportCalculationEnabled() { + return chartRenderer.isViewportCalculationEnabled(); + } + + @Override + public void setViewportCalculationEnabled(boolean isEnabled) { + chartRenderer.setViewportCalculationEnabled(isEnabled); + } + + @Override + public boolean isValueSelectionEnabled() { + return touchHandler.isValueSelectionEnabled(); + } + + @Override + public void setValueSelectionEnabled(boolean isValueSelectionEnabled) { + touchHandler.setValueSelectionEnabled(isValueSelectionEnabled); + } + + @Override + public void selectValue(SelectedValue selectedValue) { + chartRenderer.selectValue(selectedValue); + callTouchListener(); + ViewCompat.postInvalidateOnAnimation(this); + } + + @Override + public SelectedValue getSelectedValue() { + return chartRenderer.getSelectedValue(); + } + + @Override + public boolean isContainerScrollEnabled() { + return isContainerScrollEnabled; + } + + @Override + public void setContainerScrollEnabled(boolean isContainerScrollEnabled, ContainerScrollType containerScrollType) { + this.isContainerScrollEnabled = isContainerScrollEnabled; + this.containerScrollType = containerScrollType; + } + + protected void onChartDataChange() { + chartComputator.resetContentRect(); + chartRenderer.onChartDataChanged(); + axesRenderer.onChartDataChanged(); + ViewCompat.postInvalidateOnAnimation(this); + } + + /** + * You should call this method in derived classes, most likely from constructor if you changed chart/axis renderer, + * touch handler or chart computator + */ + protected void resetRendererAndTouchHandler() { + this.chartRenderer.resetRenderer(); + this.axesRenderer.resetRenderer(); + this.touchHandler.resetTouchHandler(); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/view/BubbleChartView.java b/hellocharts-library/src/lecho/lib/hellocharts/view/BubbleChartView.java index 10335e55..7b275f06 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/view/BubbleChartView.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/view/BubbleChartView.java @@ -1,5 +1,10 @@ package lecho.lib.hellocharts.view; +import android.content.Context; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.util.Log; + import lecho.lib.hellocharts.BuildConfig; import lecho.lib.hellocharts.listener.BubbleChartOnValueSelectListener; import lecho.lib.hellocharts.listener.DummyBubbleChartOnValueSelectListener; @@ -9,94 +14,89 @@ import lecho.lib.hellocharts.model.SelectedValue; import lecho.lib.hellocharts.provider.BubbleChartDataProvider; import lecho.lib.hellocharts.renderer.BubbleChartRenderer; -import android.content.Context; -import android.support.v4.view.ViewCompat; -import android.util.AttributeSet; -import android.util.Log; /** * BubbleChart, supports circle bubbles and square bubbles. - * + * * @author lecho - * */ public class BubbleChartView extends AbstractChartView implements BubbleChartDataProvider { - private static final String TAG = "BubbleChartView"; - protected BubbleChartData data; - protected BubbleChartOnValueSelectListener onValueTouchListener = new DummyBubbleChartOnValueSelectListener(); - - protected BubbleChartRenderer bubbleChartRenderer; - - public BubbleChartView(Context context) { - this(context, null, 0); - } - - public BubbleChartView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public BubbleChartView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - bubbleChartRenderer = new BubbleChartRenderer(context, this, this); - setChartRenderer(bubbleChartRenderer); - setBubbleChartData(BubbleChartData.generateDummyData()); - } - - @Override - public void setBubbleChartData(BubbleChartData data) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Setting data for BubbleChartView"); - } - - if (null == data) { - this.data = BubbleChartData.generateDummyData(); - } else { - this.data = data; - } - - super.onChartDataChange(); - } - - @Override - public BubbleChartData getBubbleChartData() { - return data; - } - - @Override - public ChartData getChartData() { - return data; - } - - @Override - public void callTouchListener() { - SelectedValue selectedValue = chartRenderer.getSelectedValue(); - - if (selectedValue.isSet()) { - BubbleValue value = data.getValues().get(selectedValue.getFirstIndex()); - onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), value); - } else { - onValueTouchListener.onValueDeselected(); - } - } - - public BubbleChartOnValueSelectListener getOnValueTouchListener() { - return onValueTouchListener; - } - - public void setOnValueTouchListener(BubbleChartOnValueSelectListener touchListener) { - if (null != touchListener) { - this.onValueTouchListener = touchListener; - } - } - - /** - * Removes empty spaces, top-bottom for portrait orientation and left-right for landscape. This method has to be - * called after view View#onSizeChanged() method is called and chart data is set. This method may be inaccurate. - * - * @see BubbleChartRenderer#removeMargins() - */ - public void removeMargins() { - bubbleChartRenderer.removeMargins(); - ViewCompat.postInvalidateOnAnimation(this); - } + private static final String TAG = "BubbleChartView"; + protected BubbleChartData data; + protected BubbleChartOnValueSelectListener onValueTouchListener = new DummyBubbleChartOnValueSelectListener(); + + protected BubbleChartRenderer bubbleChartRenderer; + + public BubbleChartView(Context context) { + this(context, null, 0); + } + + public BubbleChartView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BubbleChartView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + bubbleChartRenderer = new BubbleChartRenderer(context, this, this); + setChartRenderer(bubbleChartRenderer); + setBubbleChartData(BubbleChartData.generateDummyData()); + } + + @Override + public void setBubbleChartData(BubbleChartData data) { + if (BuildConfig.DEBUG) { + Log.d(TAG, "Setting data for BubbleChartView"); + } + + if (null == data) { + this.data = BubbleChartData.generateDummyData(); + } else { + this.data = data; + } + + super.onChartDataChange(); + } + + @Override + public BubbleChartData getBubbleChartData() { + return data; + } + + @Override + public ChartData getChartData() { + return data; + } + + @Override + public void callTouchListener() { + SelectedValue selectedValue = chartRenderer.getSelectedValue(); + + if (selectedValue.isSet()) { + BubbleValue value = data.getValues().get(selectedValue.getFirstIndex()); + onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), value); + } else { + onValueTouchListener.onValueDeselected(); + } + } + + public BubbleChartOnValueSelectListener getOnValueTouchListener() { + return onValueTouchListener; + } + + public void setOnValueTouchListener(BubbleChartOnValueSelectListener touchListener) { + if (null != touchListener) { + this.onValueTouchListener = touchListener; + } + } + + /** + * Removes empty spaces, top-bottom for portrait orientation and left-right for landscape. This method has to be + * called after view View#onSizeChanged() method is called and chart data is set. This method may be inaccurate. + * + * @see BubbleChartRenderer#removeMargins() + */ + public void removeMargins() { + bubbleChartRenderer.removeMargins(); + ViewCompat.postInvalidateOnAnimation(this); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/view/Chart.java b/hellocharts-library/src/lecho/lib/hellocharts/view/Chart.java index 436cd704..609fa6cc 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/view/Chart.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/view/Chart.java @@ -1,11 +1,11 @@ package lecho.lib.hellocharts.view; -import lecho.lib.hellocharts.computator.ChartComputator; -import lecho.lib.hellocharts.listener.ViewportChangeListener; import lecho.lib.hellocharts.animation.ChartAnimationListener; +import lecho.lib.hellocharts.computator.ChartComputator; import lecho.lib.hellocharts.gesture.ChartTouchHandler; import lecho.lib.hellocharts.gesture.ContainerScrollType; import lecho.lib.hellocharts.gesture.ZoomType; +import lecho.lib.hellocharts.listener.ViewportChangeListener; import lecho.lib.hellocharts.model.ChartData; import lecho.lib.hellocharts.model.SelectedValue; import lecho.lib.hellocharts.model.Viewport; @@ -19,283 +19,274 @@ */ public interface Chart { - /** - * Returns generic chart data. For specific class call get*ChartData method from data provider implementation. - */ - public ChartData getChartData(); - - public ChartRenderer getChartRenderer(); - - public void setChartRenderer(ChartRenderer renderer); - - public AxesRenderer getAxesRenderer(); - - public ChartComputator getChartComputator(); - - public ChartTouchHandler getTouchHandler(); - - /** - * Updates chart data with given scale. Called during chart data animation update. - */ - public void animationDataUpdate(float scale); - - /** - * Called when data animation finished. - */ - public void animationDataFinished(); - - /** - * Starts chart data animation for given duration. Before you call this method you should change target values of - * chart data. - */ - public void startDataAnimation(); - - /** - * Starts chart data animation for given duration. If duration is negative the default value of 500ms will be used. - * Before you call this method you should change target values of chart data. - */ - public void startDataAnimation(long duration); - - /** - * Stops chart data animation. All chart data values are set to their target values. - */ - public void cancelDataAnimation(); - - /** - * Return true if auto viewports recalculations are enabled, false otherwise. - */ - public boolean isViewportCalculationEnabled(); - - /** - * Set true to enable viewports(max and current) recalculations during animations or after set*ChartData method is - * called. If you disable viewports calculations viewports will not change until you change them manually or enable - * calculations again. Disabled viewport calculations is usefull if you want show only part of chart by setting - * custom viewport and don't want any operation to change that viewport - */ - public void setViewportCalculationEnabled(boolean isEnabled); - - /** - * Set listener for data animation to be notified when data animation started and finished. By default that flag is - * set to true so be careful with animation and custom viewports. - */ - public void setDataAnimationListener(ChartAnimationListener animationListener); - - /** - * Set listener for viewport animation to be notified when viewport animation started and finished. - */ - public void setViewportAnimationListener(ChartAnimationListener animationListener); - - /** - * Set listener for current viewport changes. It will be called when viewport change either by gesture or - * programmatically. Note! This method works only for preview charts. It is intentionally disabled for other types - * of charts to avoid unnecessary method calls during invalidation. - * - */ - public void setViewportChangeListener(ViewportChangeListener viewportChangeListener); - - public void callTouchListener(); - - /** - * Returns true if chart is interactive. - * - * @see #setInteractive(boolean) - */ - public boolean isInteractive(); - - /** - * Set true to allow user use touch gestures. If set to false user will not be able zoom, scroll or select/touch - * value. By default true. - */ - public void setInteractive(boolean isInteractive); - - /** - * Returns true if pitch to zoom and double tap zoom is enabled. - * - * @see #setZoomEnabled(boolean) - */ - public boolean isZoomEnabled(); - - /** - * Set true to enable zoom, false to disable, by default true; - */ - public void setZoomEnabled(boolean isZoomEnabled); - - /** - * Returns true if scrolling is enabled. - * - * @see #setScrollEnabled(boolean) - */ - public boolean isScrollEnabled(); - - /** - * Set true to enable touch scroll/fling, false to disable touch scroll/fling, by default true; - */ - public void setScrollEnabled(boolean isScrollEnabled); - - /** - * Move/Srcoll viewport to position x,y(that position must be within maximum chart viewport). If possible viewport - * will be centered at this point. Width and height of viewport will not be modified. - * - * @see #setCurrentViewport(lecho.lib.hellocharts.model.Viewport) - */ - public void moveTo(float x, float y); - - /** - * Animate viewport to position x,y(that position must be within maximum chart viewport). If possible viewport - * will be centered at this point. Width and height of viewport will not be modified. - * - * @see #setCurrentViewport(lecho.lib.hellocharts.model.Viewport) ; - */ - public void moveToWithAnimation(float x, float y); - - /** - * Returns current zoom type for this chart. - * - * @see #setZoomType(ZoomType) - */ - public ZoomType getZoomType(); - - /** - * Set zoom type, available options: ZoomType.HORIZONTAL_AND_VERTICAL, ZoomType.HORIZONTAL, ZoomType.VERTICAL. By - * default HORIZONTAL_AND_VERTICAL. - */ - public void setZoomType(ZoomType zoomType); - - /** - * Returns current maximum zoom value. - * - */ - public float getMaxZoom(); - - /** - * Set max zoom value. Default maximum zoom is 20. - * - */ - public void setMaxZoom(float maxZoom); - - /** - * Returns current zoom level. - */ - public float getZoomLevel(); - - /** - * Programatically zoom chart to given point(viewport point). Call this method after chart data had been set. - * - * @param x - * x within chart maximum viewport - * @param y - * y within chart maximum viewport - * @param zoomLevel - * value from 1 to maxZoom(default 20). 1 means chart has no zoom. - */ - public void setZoomLevel(float x, float y, float zoomLevel); - - /** - * Programatically zoom chart to given point(viewport point) with animation. Call this method after chart data had been set. - * - * @param x - * x within chart maximum viewport - * @param y - * y within chart maximum viewport - * @param zoomLevel - * value from 1 to maxZoom(default 20). 1 means chart has no zoom. - */ - public void setZoomLevelWithAnimation(float x, float y, float zoomLevel); - - /** - * Return true if chart value can be touched. - * - * @see #setValueTouchEnabled(boolean) - */ - public boolean isValueTouchEnabled(); - - /** - * Set true if you want allow user to click value on chart, set false to disable that option. By default true. - */ - public void setValueTouchEnabled(boolean isValueTouchEnabled); - - /** - * Returns maximum viewport for this chart. Don't modify it directly, use {@link #setMaximumViewport(Viewport)} - * instead. - * - * @see #setMaximumViewport(Viewport) - */ - public Viewport getMaximumViewport(); - - /** - * Set maximum viewport. If you set bigger maximum viewport data will be more concentrate and there will be more - * empty spaces on sides. - * - * Note. MaxViewport have to be set after chartData has been set. - */ - public void setMaximumViewport(Viewport maxViewport); - - /** - * Returns current viewport. Don't modify it directly, use {@link #setCurrentViewport(Viewport)} instead. - * - * @see #setCurrentViewport(Viewport) - */ - public Viewport getCurrentViewport(); - - /** - * Sets current viewport. - * - * Note. viewport have to be set after chartData has been set. - */ - public void setCurrentViewport(Viewport targetViewport); - - /** - * Sets current viewport with animation. - * - * Note. viewport have to be set after chartData has been set. - */ - public void setCurrentViewportWithAnimation(Viewport targetViewport); - - /** - * Sets current viewport with animation. - * - * Note. viewport have to be set after chartData has been set. - */ - public void setCurrentViewportWithAnimation(Viewport targetViewport, long duration); - - /** - * Reset maximum viewport and current viewport. Values for both viewports will be auto-calculated using current - * chart data ranges. - */ - public void resetViewports(); - - /** - * Return true if value selection mode is enabled. - * - * @see #setValueSelectionEnabled(boolean) - */ - public boolean isValueSelectionEnabled(); - - /** - * Set true if you want value selection with touch - value will stay selected until you touch somewhere else on the - * chart area. By default false and value is automatically unselected when user stop pressing on it. - */ - public void setValueSelectionEnabled(boolean isValueSelectionEnabled); - - /** - * Select single value on chart. If indexes are not valid IndexOutOfBoundsException will be thrown. - */ - public void selectValue(SelectedValue selectedValue); - - /** - * Return currently selected value indexes. - */ - public SelectedValue getSelectedValue(); - - /** - * @see #setContainerScrollEnabled(boolean, ContainerScrollType) - */ - public boolean isContainerScrollEnabled(); - - /** - * Set isContainerScrollEnabled to true and containerScrollType to HORIZONTAL or VERTICAL if you are using chart - * within scroll container. - */ - public void setContainerScrollEnabled(boolean isContainerScrollEnabled, ContainerScrollType containerScrollType); + /** + * Returns generic chart data. For specific class call get*ChartData method from data provider implementation. + */ + public ChartData getChartData(); + + public ChartRenderer getChartRenderer(); + + public void setChartRenderer(ChartRenderer renderer); + + public AxesRenderer getAxesRenderer(); + + public ChartComputator getChartComputator(); + + public ChartTouchHandler getTouchHandler(); + + /** + * Updates chart data with given scale. Called during chart data animation update. + */ + public void animationDataUpdate(float scale); + + /** + * Called when data animation finished. + */ + public void animationDataFinished(); + + /** + * Starts chart data animation for given duration. Before you call this method you should change target values of + * chart data. + */ + public void startDataAnimation(); + + /** + * Starts chart data animation for given duration. If duration is negative the default value of 500ms will be used. + * Before you call this method you should change target values of chart data. + */ + public void startDataAnimation(long duration); + + /** + * Stops chart data animation. All chart data values are set to their target values. + */ + public void cancelDataAnimation(); + + /** + * Return true if auto viewports recalculations are enabled, false otherwise. + */ + public boolean isViewportCalculationEnabled(); + + /** + * Set true to enable viewports(max and current) recalculations during animations or after set*ChartData method is + * called. If you disable viewports calculations viewports will not change until you change them manually or enable + * calculations again. Disabled viewport calculations is usefull if you want show only part of chart by setting + * custom viewport and don't want any operation to change that viewport + */ + public void setViewportCalculationEnabled(boolean isEnabled); + + /** + * Set listener for data animation to be notified when data animation started and finished. By default that flag is + * set to true so be careful with animation and custom viewports. + */ + public void setDataAnimationListener(ChartAnimationListener animationListener); + + /** + * Set listener for viewport animation to be notified when viewport animation started and finished. + */ + public void setViewportAnimationListener(ChartAnimationListener animationListener); + + /** + * Set listener for current viewport changes. It will be called when viewport change either by gesture or + * programmatically. Note! This method works only for preview charts. It is intentionally disabled for other types + * of charts to avoid unnecessary method calls during invalidation. + */ + public void setViewportChangeListener(ViewportChangeListener viewportChangeListener); + + public void callTouchListener(); + + /** + * Returns true if chart is interactive. + * + * @see #setInteractive(boolean) + */ + public boolean isInteractive(); + + /** + * Set true to allow user use touch gestures. If set to false user will not be able zoom, scroll or select/touch + * value. By default true. + */ + public void setInteractive(boolean isInteractive); + + /** + * Returns true if pitch to zoom and double tap zoom is enabled. + * + * @see #setZoomEnabled(boolean) + */ + public boolean isZoomEnabled(); + + /** + * Set true to enable zoom, false to disable, by default true; + */ + public void setZoomEnabled(boolean isZoomEnabled); + + /** + * Returns true if scrolling is enabled. + * + * @see #setScrollEnabled(boolean) + */ + public boolean isScrollEnabled(); + + /** + * Set true to enable touch scroll/fling, false to disable touch scroll/fling, by default true; + */ + public void setScrollEnabled(boolean isScrollEnabled); + + /** + * Move/Srcoll viewport to position x,y(that position must be within maximum chart viewport). If possible viewport + * will be centered at this point. Width and height of viewport will not be modified. + * + * @see #setCurrentViewport(lecho.lib.hellocharts.model.Viewport) + */ + public void moveTo(float x, float y); + + /** + * Animate viewport to position x,y(that position must be within maximum chart viewport). If possible viewport + * will be centered at this point. Width and height of viewport will not be modified. + * + * @see #setCurrentViewport(lecho.lib.hellocharts.model.Viewport) ; + */ + public void moveToWithAnimation(float x, float y); + + /** + * Returns current zoom type for this chart. + * + * @see #setZoomType(ZoomType) + */ + public ZoomType getZoomType(); + + /** + * Set zoom type, available options: ZoomType.HORIZONTAL_AND_VERTICAL, ZoomType.HORIZONTAL, ZoomType.VERTICAL. By + * default HORIZONTAL_AND_VERTICAL. + */ + public void setZoomType(ZoomType zoomType); + + /** + * Returns current maximum zoom value. + */ + public float getMaxZoom(); + + /** + * Set max zoom value. Default maximum zoom is 20. + */ + public void setMaxZoom(float maxZoom); + + /** + * Returns current zoom level. + */ + public float getZoomLevel(); + + /** + * Programatically zoom chart to given point(viewport point). Call this method after chart data had been set. + * + * @param x x within chart maximum viewport + * @param y y within chart maximum viewport + * @param zoomLevel value from 1 to maxZoom(default 20). 1 means chart has no zoom. + */ + public void setZoomLevel(float x, float y, float zoomLevel); + + /** + * Programatically zoom chart to given point(viewport point) with animation. Call this method after chart data had been set. + * + * @param x x within chart maximum viewport + * @param y y within chart maximum viewport + * @param zoomLevel value from 1 to maxZoom(default 20). 1 means chart has no zoom. + */ + public void setZoomLevelWithAnimation(float x, float y, float zoomLevel); + + /** + * Return true if chart value can be touched. + * + * @see #setValueTouchEnabled(boolean) + */ + public boolean isValueTouchEnabled(); + + /** + * Set true if you want allow user to click value on chart, set false to disable that option. By default true. + */ + public void setValueTouchEnabled(boolean isValueTouchEnabled); + + /** + * Returns maximum viewport for this chart. Don't modify it directly, use {@link #setMaximumViewport(Viewport)} + * instead. + * + * @see #setMaximumViewport(Viewport) + */ + public Viewport getMaximumViewport(); + + /** + * Set maximum viewport. If you set bigger maximum viewport data will be more concentrate and there will be more + * empty spaces on sides. + *

+ * Note. MaxViewport have to be set after chartData has been set. + */ + public void setMaximumViewport(Viewport maxViewport); + + /** + * Returns current viewport. Don't modify it directly, use {@link #setCurrentViewport(Viewport)} instead. + * + * @see #setCurrentViewport(Viewport) + */ + public Viewport getCurrentViewport(); + + /** + * Sets current viewport. + *

+ * Note. viewport have to be set after chartData has been set. + */ + public void setCurrentViewport(Viewport targetViewport); + + /** + * Sets current viewport with animation. + *

+ * Note. viewport have to be set after chartData has been set. + */ + public void setCurrentViewportWithAnimation(Viewport targetViewport); + + /** + * Sets current viewport with animation. + *

+ * Note. viewport have to be set after chartData has been set. + */ + public void setCurrentViewportWithAnimation(Viewport targetViewport, long duration); + + /** + * Reset maximum viewport and current viewport. Values for both viewports will be auto-calculated using current + * chart data ranges. + */ + public void resetViewports(); + + /** + * Return true if value selection mode is enabled. + * + * @see #setValueSelectionEnabled(boolean) + */ + public boolean isValueSelectionEnabled(); + + /** + * Set true if you want value selection with touch - value will stay selected until you touch somewhere else on the + * chart area. By default false and value is automatically unselected when user stop pressing on it. + */ + public void setValueSelectionEnabled(boolean isValueSelectionEnabled); + + /** + * Select single value on chart. If indexes are not valid IndexOutOfBoundsException will be thrown. + */ + public void selectValue(SelectedValue selectedValue); + + /** + * Return currently selected value indexes. + */ + public SelectedValue getSelectedValue(); + + /** + * @see #setContainerScrollEnabled(boolean, ContainerScrollType) + */ + public boolean isContainerScrollEnabled(); + + /** + * Set isContainerScrollEnabled to true and containerScrollType to HORIZONTAL or VERTICAL if you are using chart + * within scroll container. + */ + public void setContainerScrollEnabled(boolean isContainerScrollEnabled, ContainerScrollType containerScrollType); } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/view/ColumnChartView.java b/hellocharts-library/src/lecho/lib/hellocharts/view/ColumnChartView.java index 0b01c821..4f3dc7c6 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/view/ColumnChartView.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/view/ColumnChartView.java @@ -1,7 +1,6 @@ package lecho.lib.hellocharts.view; import android.content.Context; -import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.Log; @@ -20,70 +19,70 @@ * @author Leszek Wach */ public class ColumnChartView extends AbstractChartView implements ColumnChartDataProvider { - private static final String TAG = "ColumnChartView"; - private ColumnChartData data; - private ColumnChartOnValueSelectListener onValueTouchListener = new DummyColumnChartOnValueSelectListener(); - - public ColumnChartView(Context context) { - this(context, null, 0); - } - - public ColumnChartView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ColumnChartView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - setChartRenderer(new ColumnChartRenderer(context, this, this)); - setColumnChartData(ColumnChartData.generateDummyData()); - } - - @Override - public ColumnChartData getColumnChartData() { - return data; - } - - @Override - public void setColumnChartData(ColumnChartData data) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Setting data for ColumnChartView"); - } - - if (null == data) { - this.data = ColumnChartData.generateDummyData(); - } else { - this.data = data; - } - - super.onChartDataChange(); - - } - - @Override - public ColumnChartData getChartData() { - return data; - } - - @Override - public void callTouchListener() { - SelectedValue selectedValue = chartRenderer.getSelectedValue(); - - if (selectedValue.isSet()) { - SubcolumnValue value = data.getColumns().get(selectedValue.getFirstIndex()).getValues() - .get(selectedValue.getSecondIndex()); - onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), selectedValue.getSecondIndex(), value); - } else { - onValueTouchListener.onValueDeselected(); - } - } - - public ColumnChartOnValueSelectListener getOnValueTouchListener() { - return onValueTouchListener; - } - - public void setOnValueTouchListener(ColumnChartOnValueSelectListener touchListener) { - if (null != touchListener) { - this.onValueTouchListener = touchListener; - } - } + private static final String TAG = "ColumnChartView"; + private ColumnChartData data; + private ColumnChartOnValueSelectListener onValueTouchListener = new DummyColumnChartOnValueSelectListener(); + + public ColumnChartView(Context context) { + this(context, null, 0); + } + + public ColumnChartView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ColumnChartView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setChartRenderer(new ColumnChartRenderer(context, this, this)); + setColumnChartData(ColumnChartData.generateDummyData()); + } + + @Override + public ColumnChartData getColumnChartData() { + return data; + } + + @Override + public void setColumnChartData(ColumnChartData data) { + if (BuildConfig.DEBUG) { + Log.d(TAG, "Setting data for ColumnChartView"); + } + + if (null == data) { + this.data = ColumnChartData.generateDummyData(); + } else { + this.data = data; + } + + super.onChartDataChange(); + + } + + @Override + public ColumnChartData getChartData() { + return data; + } + + @Override + public void callTouchListener() { + SelectedValue selectedValue = chartRenderer.getSelectedValue(); + + if (selectedValue.isSet()) { + SubcolumnValue value = data.getColumns().get(selectedValue.getFirstIndex()).getValues() + .get(selectedValue.getSecondIndex()); + onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), selectedValue.getSecondIndex(), value); + } else { + onValueTouchListener.onValueDeselected(); + } + } + + public ColumnChartOnValueSelectListener getOnValueTouchListener() { + return onValueTouchListener; + } + + public void setOnValueTouchListener(ColumnChartOnValueSelectListener touchListener) { + if (null != touchListener) { + this.onValueTouchListener = touchListener; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/view/ComboLineColumnChartView.java b/hellocharts-library/src/lecho/lib/hellocharts/view/ComboLineColumnChartView.java index 5fcde582..589db95a 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/view/ComboLineColumnChartView.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/view/ComboLineColumnChartView.java @@ -1,7 +1,6 @@ package lecho.lib.hellocharts.view; import android.content.Context; -import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.Log; @@ -27,117 +26,117 @@ * @author Leszek Wach */ public class ComboLineColumnChartView extends AbstractChartView implements ComboLineColumnChartDataProvider { - private static final String TAG = "ComboLineColumnChartView"; - protected ComboLineColumnChartData data; - protected ColumnChartDataProvider columnChartDataProvider = new ComboColumnChartDataProvider(); - protected LineChartDataProvider lineChartDataProvider = new ComboLineChartDataProvider(); - protected ComboLineColumnChartOnValueSelectListener onValueTouchListener = new DummyCompoLineColumnChartOnValueSelectListener(); - - public ComboLineColumnChartView(Context context) { - this(context, null, 0); - } - - public ComboLineColumnChartView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ComboLineColumnChartView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - setChartRenderer(new ComboLineColumnChartRenderer(context, this, columnChartDataProvider, lineChartDataProvider)); - setComboLineColumnChartData(ComboLineColumnChartData.generateDummyData()); - } - - @Override - public ComboLineColumnChartData getComboLineColumnChartData() { - return data; - } - - @Override - public void setComboLineColumnChartData(ComboLineColumnChartData data) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Setting data for ComboLineColumnChartView"); - } - - if (null == data) { - this.data = null;// generateDummyData(); - } else { - this.data = data; - } - - super.onChartDataChange(); - } - - @Override - public ChartData getChartData() { - return data; - } - - @Override - public void callTouchListener() { - SelectedValue selectedValue = chartRenderer.getSelectedValue(); - - if (selectedValue.isSet()) { - - if (SelectedValueType.COLUMN.equals(selectedValue.getType())) { - - SubcolumnValue value = data.getColumnChartData().getColumns().get(selectedValue.getFirstIndex()) - .getValues().get(selectedValue.getSecondIndex()); - onValueTouchListener.onColumnValueSelected(selectedValue.getFirstIndex(), - selectedValue.getSecondIndex(), value); - - } else if (SelectedValueType.LINE.equals(selectedValue.getType())) { - - PointValue value = data.getLineChartData().getLines().get(selectedValue.getFirstIndex()).getValues() - .get(selectedValue.getSecondIndex()); - onValueTouchListener.onPointValueSelected(selectedValue.getFirstIndex(), selectedValue.getSecondIndex(), - value); - - } else { - throw new IllegalArgumentException("Invalid selected value type " + selectedValue.getType().name()); - } - } else { - onValueTouchListener.onValueDeselected(); - } - } - - public ComboLineColumnChartOnValueSelectListener getOnValueTouchListener() { - return onValueTouchListener; - } - - public void setOnValueTouchListener(ComboLineColumnChartOnValueSelectListener touchListener) { - if (null != touchListener) { - this.onValueTouchListener = touchListener; - } - } - - private class ComboLineChartDataProvider implements LineChartDataProvider { - - @Override - public LineChartData getLineChartData() { - return ComboLineColumnChartView.this.data.getLineChartData(); - } - - @Override - public void setLineChartData(LineChartData data) { - ComboLineColumnChartView.this.data.setLineChartData(data); - - } - - } - - private class ComboColumnChartDataProvider implements ColumnChartDataProvider { - - @Override - public ColumnChartData getColumnChartData() { - return ComboLineColumnChartView.this.data.getColumnChartData(); - } + private static final String TAG = "ComboLineColumnChartView"; + protected ComboLineColumnChartData data; + protected ColumnChartDataProvider columnChartDataProvider = new ComboColumnChartDataProvider(); + protected LineChartDataProvider lineChartDataProvider = new ComboLineChartDataProvider(); + protected ComboLineColumnChartOnValueSelectListener onValueTouchListener = new DummyCompoLineColumnChartOnValueSelectListener(); + + public ComboLineColumnChartView(Context context) { + this(context, null, 0); + } + + public ComboLineColumnChartView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ComboLineColumnChartView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setChartRenderer(new ComboLineColumnChartRenderer(context, this, columnChartDataProvider, lineChartDataProvider)); + setComboLineColumnChartData(ComboLineColumnChartData.generateDummyData()); + } + + @Override + public ComboLineColumnChartData getComboLineColumnChartData() { + return data; + } + + @Override + public void setComboLineColumnChartData(ComboLineColumnChartData data) { + if (BuildConfig.DEBUG) { + Log.d(TAG, "Setting data for ComboLineColumnChartView"); + } + + if (null == data) { + this.data = null;// generateDummyData(); + } else { + this.data = data; + } + + super.onChartDataChange(); + } + + @Override + public ChartData getChartData() { + return data; + } + + @Override + public void callTouchListener() { + SelectedValue selectedValue = chartRenderer.getSelectedValue(); + + if (selectedValue.isSet()) { + + if (SelectedValueType.COLUMN.equals(selectedValue.getType())) { + + SubcolumnValue value = data.getColumnChartData().getColumns().get(selectedValue.getFirstIndex()) + .getValues().get(selectedValue.getSecondIndex()); + onValueTouchListener.onColumnValueSelected(selectedValue.getFirstIndex(), + selectedValue.getSecondIndex(), value); + + } else if (SelectedValueType.LINE.equals(selectedValue.getType())) { + + PointValue value = data.getLineChartData().getLines().get(selectedValue.getFirstIndex()).getValues() + .get(selectedValue.getSecondIndex()); + onValueTouchListener.onPointValueSelected(selectedValue.getFirstIndex(), selectedValue.getSecondIndex(), + value); + + } else { + throw new IllegalArgumentException("Invalid selected value type " + selectedValue.getType().name()); + } + } else { + onValueTouchListener.onValueDeselected(); + } + } + + public ComboLineColumnChartOnValueSelectListener getOnValueTouchListener() { + return onValueTouchListener; + } + + public void setOnValueTouchListener(ComboLineColumnChartOnValueSelectListener touchListener) { + if (null != touchListener) { + this.onValueTouchListener = touchListener; + } + } + + private class ComboLineChartDataProvider implements LineChartDataProvider { + + @Override + public LineChartData getLineChartData() { + return ComboLineColumnChartView.this.data.getLineChartData(); + } + + @Override + public void setLineChartData(LineChartData data) { + ComboLineColumnChartView.this.data.setLineChartData(data); + + } + + } + + private class ComboColumnChartDataProvider implements ColumnChartDataProvider { + + @Override + public ColumnChartData getColumnChartData() { + return ComboLineColumnChartView.this.data.getColumnChartData(); + } - @Override - public void setColumnChartData(ColumnChartData data) { - ComboLineColumnChartView.this.data.setColumnChartData(data); + @Override + public void setColumnChartData(ColumnChartData data) { + ComboLineColumnChartView.this.data.setColumnChartData(data); - } + } - } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/view/LineChartView.java b/hellocharts-library/src/lecho/lib/hellocharts/view/LineChartView.java index 1a51461f..24b36430 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/view/LineChartView.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/view/LineChartView.java @@ -1,5 +1,9 @@ package lecho.lib.hellocharts.view; +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; + import lecho.lib.hellocharts.BuildConfig; import lecho.lib.hellocharts.listener.DummyLineChartOnValueSelectListener; import lecho.lib.hellocharts.listener.LineChartOnValueSelectListener; @@ -9,82 +13,77 @@ import lecho.lib.hellocharts.model.SelectedValue; import lecho.lib.hellocharts.provider.LineChartDataProvider; import lecho.lib.hellocharts.renderer.LineChartRenderer; -import android.content.Context; -import android.support.v4.view.ViewCompat; -import android.util.AttributeSet; -import android.util.Log; /** * LineChart, supports cubic lines, filled lines, circle and square points. Point radius and stroke width can be * adjusted using LineChartData attributes. - * + * * @author Leszek Wach - * */ public class LineChartView extends AbstractChartView implements LineChartDataProvider { - private static final String TAG = "LineChartView"; - protected LineChartData data; - protected LineChartOnValueSelectListener onValueTouchListener = new DummyLineChartOnValueSelectListener(); + private static final String TAG = "LineChartView"; + protected LineChartData data; + protected LineChartOnValueSelectListener onValueTouchListener = new DummyLineChartOnValueSelectListener(); - public LineChartView(Context context) { - this(context, null, 0); - } + public LineChartView(Context context) { + this(context, null, 0); + } - public LineChartView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } + public LineChartView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } - public LineChartView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - setChartRenderer(new LineChartRenderer(context, this, this)); - setLineChartData(LineChartData.generateDummyData()); - } + public LineChartView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setChartRenderer(new LineChartRenderer(context, this, this)); + setLineChartData(LineChartData.generateDummyData()); + } - @Override - public void setLineChartData(LineChartData data) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Setting data for LineChartView"); - } + @Override + public void setLineChartData(LineChartData data) { + if (BuildConfig.DEBUG) { + Log.d(TAG, "Setting data for LineChartView"); + } - if (null == data) { - this.data = LineChartData.generateDummyData(); - } else { - this.data = data; - } + if (null == data) { + this.data = LineChartData.generateDummyData(); + } else { + this.data = data; + } - super.onChartDataChange(); - } + super.onChartDataChange(); + } - @Override - public LineChartData getLineChartData() { - return data; - } + @Override + public LineChartData getLineChartData() { + return data; + } - @Override - public ChartData getChartData() { - return data; - } + @Override + public ChartData getChartData() { + return data; + } - @Override - public void callTouchListener() { - SelectedValue selectedValue = chartRenderer.getSelectedValue(); + @Override + public void callTouchListener() { + SelectedValue selectedValue = chartRenderer.getSelectedValue(); - if (selectedValue.isSet()) { - PointValue point = data.getLines().get(selectedValue.getFirstIndex()).getValues() - .get(selectedValue.getSecondIndex()); - onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), selectedValue.getSecondIndex(), point); - } else { - onValueTouchListener.onValueDeselected(); - } - } + if (selectedValue.isSet()) { + PointValue point = data.getLines().get(selectedValue.getFirstIndex()).getValues() + .get(selectedValue.getSecondIndex()); + onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), selectedValue.getSecondIndex(), point); + } else { + onValueTouchListener.onValueDeselected(); + } + } - public LineChartOnValueSelectListener getOnValueTouchListener() { - return onValueTouchListener; - } + public LineChartOnValueSelectListener getOnValueTouchListener() { + return onValueTouchListener; + } - public void setOnValueTouchListener(LineChartOnValueSelectListener touchListener) { - if (null != touchListener) { - this.onValueTouchListener = touchListener; - } - } + public void setOnValueTouchListener(LineChartOnValueSelectListener touchListener) { + if (null != touchListener) { + this.onValueTouchListener = touchListener; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/view/PieChartView.java b/hellocharts-library/src/lecho/lib/hellocharts/view/PieChartView.java index a2a15e0c..3977f057 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/view/PieChartView.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/view/PieChartView.java @@ -1,5 +1,13 @@ package lecho.lib.hellocharts.view; +import android.content.Context; +import android.graphics.RectF; +import android.os.Build; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; + import lecho.lib.hellocharts.BuildConfig; import lecho.lib.hellocharts.animation.PieChartRotationAnimator; import lecho.lib.hellocharts.animation.PieChartRotationAnimatorV14; @@ -7,188 +15,178 @@ import lecho.lib.hellocharts.gesture.PieChartTouchHandler; import lecho.lib.hellocharts.listener.DummyPieChartOnValueSelectListener; import lecho.lib.hellocharts.listener.PieChartOnValueSelectListener; -import lecho.lib.hellocharts.model.SliceValue; import lecho.lib.hellocharts.model.ChartData; import lecho.lib.hellocharts.model.PieChartData; import lecho.lib.hellocharts.model.SelectedValue; +import lecho.lib.hellocharts.model.SliceValue; import lecho.lib.hellocharts.provider.PieChartDataProvider; import lecho.lib.hellocharts.renderer.PieChartRenderer; -import android.content.Context; -import android.graphics.RectF; -import android.os.Build; -import android.support.v4.view.ViewCompat; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; /** - * * PieChart is a little different than others charts. It doesn't have axes. It doesn't support viewport so changing * viewport wont work. Instead it support "Circle Oval". Pinch-to-Zoom and double tap zoom wont work either. Instead of * scroll there is chart rotation if isChartRotationEnabled is set to true. PieChart looks the best when it has the same * width and height, drawing chart on rectangle with proportions other than 1:1 will left some empty spaces. - * + * * @author Leszek Wach - * */ public class PieChartView extends AbstractChartView implements PieChartDataProvider { - private static final String TAG = "PieChartView"; - protected PieChartData data; - protected PieChartOnValueSelectListener onValueTouchListener = new DummyPieChartOnValueSelectListener(); - protected PieChartRenderer pieChartRenderer; - protected PieChartRotationAnimator rotationAnimator; - - public PieChartView(Context context) { - this(context, null, 0); - } - - public PieChartView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PieChartView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - pieChartRenderer = new PieChartRenderer(context, this, this); - touchHandler = new PieChartTouchHandler(context, this); - setChartRenderer(pieChartRenderer); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - this.rotationAnimator = new PieChartRotationAnimatorV8(this); - } else { - this.rotationAnimator = new PieChartRotationAnimatorV14(this); - } - setPieChartData(PieChartData.generateDummyData()); - } - - @Override - public void setPieChartData(PieChartData data) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Setting data for ColumnChartView"); - } - - if (null == data) { - this.data = PieChartData.generateDummyData(); - } else { - this.data = data; - } - - super.onChartDataChange(); - } - - @Override - public PieChartData getPieChartData() { - return data; - } - - @Override - public ChartData getChartData() { - return data; - } - - @Override - public void callTouchListener() { - SelectedValue selectedValue = chartRenderer.getSelectedValue(); - - if (selectedValue.isSet()) { - SliceValue sliceValue = data.getValues().get(selectedValue.getFirstIndex()); - onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), sliceValue); - } else { - onValueTouchListener.onValueDeselected(); - } - } - - public PieChartOnValueSelectListener getOnValueTouchListener() { - return onValueTouchListener; - } - - public void setOnValueTouchListener(PieChartOnValueSelectListener touchListener) { - if (null != touchListener) { - this.onValueTouchListener = touchListener; - } - } - - /** - * Returns rectangle that will constraint pie chart area. - */ - public RectF getCircleOval() { - return pieChartRenderer.getCircleOval(); - } - - /** - * Use this to change pie chart area. Because by default CircleOval is calculated onSizeChanged() you must call this - * method after size of PieChartView is calculated. In most cases it will probably be easier to use - * {@link #setCircleFillRatio(float)} to change chart area or just use view padding. - */ - public void setCircleOval(RectF orginCircleOval) { - pieChartRenderer.setCircleOval(orginCircleOval); - ViewCompat.postInvalidateOnAnimation(this); - } - - /** - * Returns pie chart rotation, 0 rotation means that 0 degrees is at 3 o'clock. Don't confuse with - * {@link View#getRotation()}. - * - * @return - */ - public int getChartRotation() { - return pieChartRenderer.getChartRotation(); - } - - /** - * Set pie chart rotation. Don't confuse with {@link View#getRotation()}. - * - * @param rotation - * - * @see #getChartRotation() - */ - public void setChartRotation(int rotation, boolean isAnimated) { - if (isAnimated) { - rotationAnimator.cancelAnimation(); - rotationAnimator.startAnimation(pieChartRenderer.getChartRotation(), rotation); - } else { - pieChartRenderer.setChartRotation(rotation); - } - ViewCompat.postInvalidateOnAnimation(this); - } - - public boolean isChartRotationEnabled() { - if (touchHandler instanceof PieChartTouchHandler) { - return ((PieChartTouchHandler) touchHandler).isRotationEnabled(); - } else { - return false; - } - } - - /** - * Set false if you don't wont the chart to be rotated by touch gesture. Rotating programmatically will still work. - * - * @param isRotationEnabled - */ - public void setChartRotationEnabled(boolean isRotationEnabled) { - if (touchHandler instanceof PieChartTouchHandler) { - ((PieChartTouchHandler) touchHandler).setRotationEnabled(isRotationEnabled); - } - } - - /** - * Returns SliceValue that is under given angle, selectedValue (if not null) will be hold slice index. - */ - public SliceValue getValueForAngle(int angle, SelectedValue selectedValue) { - return pieChartRenderer.getValueForAngle(angle, selectedValue); - } - - /** - * @see #setCircleFillRatio(float) - */ - public float getCircleFillRatio() { - return pieChartRenderer.getCircleFillRatio(); - } - - /** - * Set how much of view area should be taken by chart circle. Value should be between 0 and 1. Default is 1 so - * circle will have radius equals min(View.width, View.height). - */ - public void setCircleFillRatio(float fillRatio) { - pieChartRenderer.setCircleFillRatio(fillRatio); - ViewCompat.postInvalidateOnAnimation(this); - } + private static final String TAG = "PieChartView"; + protected PieChartData data; + protected PieChartOnValueSelectListener onValueTouchListener = new DummyPieChartOnValueSelectListener(); + protected PieChartRenderer pieChartRenderer; + protected PieChartRotationAnimator rotationAnimator; + + public PieChartView(Context context) { + this(context, null, 0); + } + + public PieChartView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public PieChartView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + pieChartRenderer = new PieChartRenderer(context, this, this); + touchHandler = new PieChartTouchHandler(context, this); + setChartRenderer(pieChartRenderer); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + this.rotationAnimator = new PieChartRotationAnimatorV8(this); + } else { + this.rotationAnimator = new PieChartRotationAnimatorV14(this); + } + setPieChartData(PieChartData.generateDummyData()); + } + + @Override + public void setPieChartData(PieChartData data) { + if (BuildConfig.DEBUG) { + Log.d(TAG, "Setting data for ColumnChartView"); + } + + if (null == data) { + this.data = PieChartData.generateDummyData(); + } else { + this.data = data; + } + + super.onChartDataChange(); + } + + @Override + public PieChartData getPieChartData() { + return data; + } + + @Override + public ChartData getChartData() { + return data; + } + + @Override + public void callTouchListener() { + SelectedValue selectedValue = chartRenderer.getSelectedValue(); + + if (selectedValue.isSet()) { + SliceValue sliceValue = data.getValues().get(selectedValue.getFirstIndex()); + onValueTouchListener.onValueSelected(selectedValue.getFirstIndex(), sliceValue); + } else { + onValueTouchListener.onValueDeselected(); + } + } + + public PieChartOnValueSelectListener getOnValueTouchListener() { + return onValueTouchListener; + } + + public void setOnValueTouchListener(PieChartOnValueSelectListener touchListener) { + if (null != touchListener) { + this.onValueTouchListener = touchListener; + } + } + + /** + * Returns rectangle that will constraint pie chart area. + */ + public RectF getCircleOval() { + return pieChartRenderer.getCircleOval(); + } + + /** + * Use this to change pie chart area. Because by default CircleOval is calculated onSizeChanged() you must call this + * method after size of PieChartView is calculated. In most cases it will probably be easier to use + * {@link #setCircleFillRatio(float)} to change chart area or just use view padding. + */ + public void setCircleOval(RectF orginCircleOval) { + pieChartRenderer.setCircleOval(orginCircleOval); + ViewCompat.postInvalidateOnAnimation(this); + } + + /** + * Returns pie chart rotation, 0 rotation means that 0 degrees is at 3 o'clock. Don't confuse with + * {@link View#getRotation()}. + * + * @return + */ + public int getChartRotation() { + return pieChartRenderer.getChartRotation(); + } + + /** + * Set pie chart rotation. Don't confuse with {@link View#getRotation()}. + * + * @param rotation + * @see #getChartRotation() + */ + public void setChartRotation(int rotation, boolean isAnimated) { + if (isAnimated) { + rotationAnimator.cancelAnimation(); + rotationAnimator.startAnimation(pieChartRenderer.getChartRotation(), rotation); + } else { + pieChartRenderer.setChartRotation(rotation); + } + ViewCompat.postInvalidateOnAnimation(this); + } + + public boolean isChartRotationEnabled() { + if (touchHandler instanceof PieChartTouchHandler) { + return ((PieChartTouchHandler) touchHandler).isRotationEnabled(); + } else { + return false; + } + } + + /** + * Set false if you don't wont the chart to be rotated by touch gesture. Rotating programmatically will still work. + * + * @param isRotationEnabled + */ + public void setChartRotationEnabled(boolean isRotationEnabled) { + if (touchHandler instanceof PieChartTouchHandler) { + ((PieChartTouchHandler) touchHandler).setRotationEnabled(isRotationEnabled); + } + } + + /** + * Returns SliceValue that is under given angle, selectedValue (if not null) will be hold slice index. + */ + public SliceValue getValueForAngle(int angle, SelectedValue selectedValue) { + return pieChartRenderer.getValueForAngle(angle, selectedValue); + } + + /** + * @see #setCircleFillRatio(float) + */ + public float getCircleFillRatio() { + return pieChartRenderer.getCircleFillRatio(); + } + + /** + * Set how much of view area should be taken by chart circle. Value should be between 0 and 1. Default is 1 so + * circle will have radius equals min(View.width, View.height). + */ + public void setCircleFillRatio(float fillRatio) { + pieChartRenderer.setCircleFillRatio(fillRatio); + ViewCompat.postInvalidateOnAnimation(this); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/view/PreviewColumnChartView.java b/hellocharts-library/src/lecho/lib/hellocharts/view/PreviewColumnChartView.java index c1a89194..927beef4 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/view/PreviewColumnChartView.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/view/PreviewColumnChartView.java @@ -1,57 +1,56 @@ package lecho.lib.hellocharts.view; +import android.content.Context; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.util.Log; + import lecho.lib.hellocharts.BuildConfig; import lecho.lib.hellocharts.computator.PreviewChartComputator; import lecho.lib.hellocharts.gesture.PreviewChartTouchHandler; import lecho.lib.hellocharts.model.ColumnChartData; -import lecho.lib.hellocharts.renderer.AxesRenderer; import lecho.lib.hellocharts.renderer.PreviewColumnChartRenderer; -import android.content.Context; -import android.support.v4.view.ViewCompat; -import android.util.AttributeSet; -import android.util.Log; /** * Preview chart that can be used as overview for other ColumnChart. When you change Viewport of this chart, visible * area of other chart will change. For that you need also to use * {@link Chart#setViewportChangeListener(lecho.lib.hellocharts.listener.ViewportChangeListener)} - * + * * @author Leszek Wach - * */ public class PreviewColumnChartView extends ColumnChartView { - private static final String TAG = "ColumnChartView"; - - protected PreviewColumnChartRenderer previewChartRenderer; - - public PreviewColumnChartView(Context context) { - this(context, null, 0); - } - - public PreviewColumnChartView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PreviewColumnChartView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - chartComputator = new PreviewChartComputator(); - previewChartRenderer = new PreviewColumnChartRenderer(context, this, this); - touchHandler = new PreviewChartTouchHandler(context, this); - setChartRenderer(previewChartRenderer); - setColumnChartData(ColumnChartData.generateDummyData()); - } - - public void setPreviewColor(int color) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Changing preview area color"); - } - - previewChartRenderer.setPreviewColor(color); - ViewCompat.postInvalidateOnAnimation(this); - } - - public int getPreviewColor() { - return previewChartRenderer.getPreviewColor(); - } + private static final String TAG = "ColumnChartView"; + + protected PreviewColumnChartRenderer previewChartRenderer; + + public PreviewColumnChartView(Context context) { + this(context, null, 0); + } + + public PreviewColumnChartView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public PreviewColumnChartView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + chartComputator = new PreviewChartComputator(); + previewChartRenderer = new PreviewColumnChartRenderer(context, this, this); + touchHandler = new PreviewChartTouchHandler(context, this); + setChartRenderer(previewChartRenderer); + setColumnChartData(ColumnChartData.generateDummyData()); + } + + public void setPreviewColor(int color) { + if (BuildConfig.DEBUG) { + Log.d(TAG, "Changing preview area color"); + } + + previewChartRenderer.setPreviewColor(color); + ViewCompat.postInvalidateOnAnimation(this); + } + + public int getPreviewColor() { + return previewChartRenderer.getPreviewColor(); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/view/PreviewLineChartView.java b/hellocharts-library/src/lecho/lib/hellocharts/view/PreviewLineChartView.java index a97c3f14..35e0e5cf 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/view/PreviewLineChartView.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/view/PreviewLineChartView.java @@ -1,56 +1,56 @@ package lecho.lib.hellocharts.view; +import android.content.Context; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.util.Log; + import lecho.lib.hellocharts.BuildConfig; import lecho.lib.hellocharts.computator.PreviewChartComputator; import lecho.lib.hellocharts.gesture.PreviewChartTouchHandler; import lecho.lib.hellocharts.model.LineChartData; import lecho.lib.hellocharts.renderer.PreviewLineChartRenderer; -import android.content.Context; -import android.support.v4.view.ViewCompat; -import android.util.AttributeSet; -import android.util.Log; /** * Preview chart that can be used as overview for other LineChart. When you change Viewport of this chart, visible area * of other chart will change. For that you need also to use * {@link Chart#setViewportChangeListener(lecho.lib.hellocharts.listener.ViewportChangeListener)} - * + * * @author Leszek Wach - * */ public class PreviewLineChartView extends LineChartView { - private static final String TAG = "PreviewLineChartView"; - - protected PreviewLineChartRenderer previewChartRenderer; - - public PreviewLineChartView(Context context) { - this(context, null, 0); - } - - public PreviewLineChartView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PreviewLineChartView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - chartComputator = new PreviewChartComputator(); - previewChartRenderer = new PreviewLineChartRenderer(context, this, this); - touchHandler = new PreviewChartTouchHandler(context, this); - setChartRenderer(previewChartRenderer); - setLineChartData(LineChartData.generateDummyData()); - } - - public void setPreviewColor(int color) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Changing preview area color"); - } - - previewChartRenderer.setPreviewColor(color); - ViewCompat.postInvalidateOnAnimation(this); - } - - public int getPreviewColor() { - return previewChartRenderer.getPreviewColor(); - } + private static final String TAG = "PreviewLineChartView"; + + protected PreviewLineChartRenderer previewChartRenderer; + + public PreviewLineChartView(Context context) { + this(context, null, 0); + } + + public PreviewLineChartView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public PreviewLineChartView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + chartComputator = new PreviewChartComputator(); + previewChartRenderer = new PreviewLineChartRenderer(context, this, this); + touchHandler = new PreviewChartTouchHandler(context, this); + setChartRenderer(previewChartRenderer); + setLineChartData(LineChartData.generateDummyData()); + } + + public void setPreviewColor(int color) { + if (BuildConfig.DEBUG) { + Log.d(TAG, "Changing preview area color"); + } + + previewChartRenderer.setPreviewColor(color); + ViewCompat.postInvalidateOnAnimation(this); + } + + public int getPreviewColor() { + return previewChartRenderer.getPreviewColor(); + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/view/hack/HackyDrawerLayout.java b/hellocharts-library/src/lecho/lib/hellocharts/view/hack/HackyDrawerLayout.java index 14b8bf87..123b0651 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/view/hack/HackyDrawerLayout.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/view/hack/HackyDrawerLayout.java @@ -7,30 +7,29 @@ /** * Hacky fix for issue with DrawerLayout https://github.com/chrisbanes/PhotoView/issues/72 - * */ public class HackyDrawerLayout extends DrawerLayout { - public HackyDrawerLayout(Context context) { - super(context); - } + public HackyDrawerLayout(Context context) { + super(context); + } - public HackyDrawerLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } + public HackyDrawerLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } - public HackyDrawerLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } + public HackyDrawerLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - try { - return super.onInterceptTouchEvent(ev); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + try { + return super.onInterceptTouchEvent(ev); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } } diff --git a/hellocharts-library/src/lecho/lib/hellocharts/view/hack/HackyViewPager.java b/hellocharts-library/src/lecho/lib/hellocharts/view/hack/HackyViewPager.java index e7cb23cc..d3a679d3 100644 --- a/hellocharts-library/src/lecho/lib/hellocharts/view/hack/HackyViewPager.java +++ b/hellocharts-library/src/lecho/lib/hellocharts/view/hack/HackyViewPager.java @@ -7,33 +7,33 @@ /** * Hacky fix for Issue #4 and http://code.google.com/p/android/issues/detail?id=18990 - * + *

* ScaleGestureDetector seems to mess up the touch events, which means that ViewGroups which make use of * onInterceptTouchEvent throw a lot of IllegalArgumentException: pointerIndex out of range. - * + *

* There's not much I can do in my code for now, but we can mask the result by just catching the problem and ignoring * it. - * + * * @author Chris Banes */ public class HackyViewPager extends ViewPager { - public HackyViewPager(Context context) { - super(context); - } + public HackyViewPager(Context context) { + super(context); + } - public HackyViewPager(final Context context, final AttributeSet attrs) { - super(context, attrs); - } + public HackyViewPager(final Context context, final AttributeSet attrs) { + super(context, attrs); + } - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - try { - return super.onInterceptTouchEvent(ev); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + try { + return super.onInterceptTouchEvent(ev); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } }