Creating a circle with clickable dots in Android - android

I am trying to build a circle containing numerous dots that eventually will be clickable (as much as 108 dots to fill out the border of a circle).
What i have done so far is to create 108 imageviews like this:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/circle_1"
android:src="#drawable/dot_complete"
android:layout_marginLeft="383dp"
android:layout_marginTop="214dp"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/circle_2"
android:src="#drawable/dot_complete"
android:layout_marginLeft="382dp"
android:layout_marginTop="214dp"
/>
<!-- And so on all the way up to 108 -->
The result looks like this
However i suspect this is a very bad method, so my question is what would be the better way to do this, considering i need to have onclickListener on each dot in order to show its info.
Thank you

I had a similar class laying around, with a small modification it can display three different types of drawables as "dots". The only thing you would have to do is to write the touch management.
Drawing 108 dots (three different types):
public class DotsView extends View {
private static final int dots = 108;
private static final int dotRadius = 20;
private Bitmap testBitmap1;
private Bitmap testBitmap2;
private Bitmap testBitmap3;
private RectF dotRect;
private Paint paint;
private int[] dotsStates = new int[dots];
public DotsView(Context context) {
super(context);
setupView(context);
}
public DotsView(Context context, AttributeSet attrs) {
super(context, attrs);
setupView(context);
}
public DotsView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setupView(context);
}
private void setupView(Context context) {
setWillNotDraw(false);
paint = new Paint();
paint.setAntiAlias(true);
test();
}
private void test() {
//THIS METHOD IS JUST A TEST THAT CHANGES THE DRAWABLES USED FOR SOME DOTS
for (int i = 2; i < 20; ++i) {
dotsStates[i] = 1;
}
for (int i = 50; i < 55; ++i) {
dotsStates[i] = 2;
}
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
initBitmaps();
invalidate();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
destroyBitmaps();
}
private void initBitmaps() {
testBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.test_1);
testBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.test_2);
testBitmap3 = BitmapFactory.decodeResource(getResources(), R.drawable.test_3);
dotRect = new RectF(0, 0, dotRadius, dotRadius);
}
private boolean isBitmapValid(Bitmap bitmap) {
return bitmap != null && !bitmap.isRecycled();
}
private void destroyBitmaps() {
if (isBitmapValid(testBitmap1)) {
testBitmap1.recycle();
testBitmap1 = null;
}
if (isBitmapValid(testBitmap2)) {
testBitmap2.recycle();
testBitmap2 = null;
}
if (isBitmapValid(testBitmap3)) {
testBitmap3.recycle();
testBitmap3 = null;
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isBitmapValid(testBitmap1) && isBitmapValid(testBitmap2) && isBitmapValid(testBitmap3)) {
// apply padding to canvas:
final int width = canvas.getWidth();
final int height = canvas.getHeight();
final int squareSide = Math.min(width, height);
canvas.translate(width / 2f, height / 2f); // moving to the center of the View
final float outerRadius = squareSide / 2f;
final float innerRadius = outerRadius - dotRadius;
final float angleFactor = 360f / dots;
for (int i = 0; i < dots; ++i) {
canvas.save(); // creating a "checkpoint"
canvas.rotate(angleFactor * i);
canvas.translate(innerRadius, 0); //moving to the edge of the big circle
canvas.drawBitmap(dotsStates[i] == 0 ?
testBitmap1 :
dotsStates[i] == 1 ?
testBitmap2 : testBitmap3,
null, dotRect, paint);
canvas.restore(); //restoring a "checkpoint"
}
}
}
}

Your approach is super heavyweight. I'd instead recommend making a custom View class, within which you do these things:
Override the onDraw method to draw your circles directly onto the view's Canvas
Implement an onTouchEvent listener, checking the coordinates of the touch against the positions/radii of the circles you created - thus finding the circle (if any) which was tapped
Trigger a custom event like onCircleTapped(View v, int circleId) so that the containing view/activity/fragment can handle the event properly.

You can try this librarytire view
import the barlibrary
create ChartTireView
I hope this will help you.

Related

How to develop box like Rating bar like zomato android app?

I want to develop Rating bar like zomato android application. In which the rating bar box changes it's color according to selection. if only one box is selected then it's color is Read and gradually we select more box it became Green.
Here I attached snap of zomato android app's screen portion which shows Rating exactly I would like to develop this type of Rating and Review System.
That's a simple horizontal LinearLayout with 9 TextViews, each of them having grey as the default color. Aditionally you can create an array of strings, each string being a color code. This is the quick and dirty solution.
When clicking on a TextView, you can find out its position in the LinearLayout and color each view before it with the corresponding color code in the array, along with the selected view, and the others remain grey.
I had a similar problem once with a progress bar consisting of 10 sections, and instead of looking for a library I made something quick like this and it worked just fine.
I found solution for that we need to make Custom Rating bar Here bellow code shows Custom class of Rating bar. which full fill all requirement which mention in Question.
public class MyCustomRatingBar extends android.support.v7.widget.AppCompatRatingBar {
private int[] iconArrayActive = {
R.drawable.ic_square_sel_1,
R.drawable.ic_square_sel_2,
R.drawable.ic_square_sel_3,
R.drawable.ic_square_sel_4,
R.drawable.ic_square_sel_5,
R.drawable.ic_square_sel_6,
R.drawable.ic_square_sel_7,
R.drawable.ic_square_sel_8,
R.drawable.ic_square_sel_9
};
private int[] iconArrayInactive = {
R.drawable.ic_square_unsel,
R.drawable.ic_square_unsel,
R.drawable.ic_square_unsel,
R.drawable.ic_square_unsel,
R.drawable.ic_square_unsel,
R.drawable.ic_square_unsel,
R.drawable.ic_square_unsel,
R.drawable.ic_square_unsel,
R.drawable.ic_square_unsel
};
public MyCustomRatingBar (Context context) {
super(context);
init();
}
public MyCustomRatingBar (Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyCustomRatingBar (Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
this.setMax(9);
this.setNumStars(9);
this.setStepSize(1.0f);
this.setRating(1.0f);
}
private Bitmap getBitmapFromVectorDrawable(Context context, int drawableId) {
Drawable drawable = ContextCompat.getDrawable(context, drawableId);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
drawable = (DrawableCompat.wrap(drawable)).mutate();
}
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
#Override
protected synchronized void onDraw(Canvas canvas) {
int stars = getNumStars();
float rating = getRating();
float x = 0;
Bitmap bitmap;
Paint paint = new Paint();
int W = getWidth();
int H = getHeight();
int icon_size = (W/stars)-0;
int y_pos = (H/2)-icon_size/2;
int delta = ((H > W)?(H):(W))/(stars);
int offset = (W-(icon_size+(stars-1)*delta))/2;
for(int i = 0; i < stars; i++) {
if ((int) rating-1 >= i) {
bitmap = getBitmapFromVectorDrawable(getContext(), iconArrayActive[i]);
} else {
bitmap = getBitmapFromVectorDrawable(getContext(), iconArrayInactive[i]);
}
x = offset+(i*delta);
Bitmap scaled = Bitmap.createScaledBitmap(bitmap, icon_size, icon_size, true);
canvas.drawBitmap(scaled, x, y_pos, paint);
canvas.save();
}
}
}

How to make custom dotted progress bar in android?

Custom progress bar with dots applied animation and given the traversing visual effect. Posting this code here because it can help you to understand and implement new designs too keeping this as reference. Hope this helps you people.
MainActivity.java :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/rect"
android:gravity="center"
>
<com.example.horizontal.canvaslearn.HorizontalDottedProgress
android:layout_width="wrap_content"
android:layout_height="wrap_content"
></com.example.horizontal.canvaslearn.HorizontalDottedProgress>
</LinearLayout>
HorizontalDottedProgress.java :
This is a custom class to create dots with animation applied.
public class HorizontalDottedProgress extends View{
//actual dot radius
private int mDotRadius = 5;
//Bounced Dot Radius
private int mBounceDotRadius = 8;
//to get identified in which position dot has to bounce
private int mDotPosition;
//specify how many dots you need in a progressbar
private int mDotAmount = 10;
public HorizontalDottedProgress(Context context) {
super(context);
}
public HorizontalDottedProgress(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HorizontalDottedProgress(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//Method to draw your customized dot on the canvas
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
//set the color for the dot that you want to draw
paint.setColor(Color.parseColor("#fd583f"));
//function to create dot
createDot(canvas,paint);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
//Animation called when attaching to the window, i.e to your screen
startAnimation();
}
private void createDot(Canvas canvas, Paint paint) {
//here i have setted progress bar with 10 dots , so repeat and wnen i = mDotPosition then increase the radius of dot i.e mBounceDotRadius
for(int i = 0; i < mDotAmount; i++ ){
if(i == mDotPosition){
canvas.drawCircle(10+(i*20), mBounceDotRadius, mBounceDotRadius, paint);
}else {
canvas.drawCircle(10+(i*20), mBounceDotRadius, mDotRadius, paint);
}
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width;
int height;
//calculate the view width
int calculatedWidth = (20*9);
width = calculatedWidth;
height = (mBounceDotRadius*2);
//MUST CALL THIS
setMeasuredDimension(width, height);
}
private void startAnimation() {
BounceAnimation bounceAnimation = new BounceAnimation();
bounceAnimation.setDuration(100);
bounceAnimation.setRepeatCount(Animation.INFINITE);
bounceAnimation.setInterpolator(new LinearInterpolator());
bounceAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
mDotPosition++;
//when mDotPosition == mDotAmount , then start again applying animation from 0th positon , i.e mDotPosition = 0;
if (mDotPosition == mDotAmount) {
mDotPosition = 0;
}
Log.d("INFOMETHOD","----On Animation Repeat----");
}
});
startAnimation(bounceAnimation);
}
private class BounceAnimation extends Animation {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
//call invalidate to redraw your view againg.
invalidate();
}
}
}
snap shot:
I have used the class HorizontalDottedProgress - this is a real solution, but it sometimes draws very small dots. Also this widget doesn't react on setVisibility(Visibility.GONE) and can't be hidden after showing.
That's why I slightly modified (and renamed for myself) this class. Dot sizes and distances are calculated using screen density now. Function onDraw() checks isShown() before drawing.
Then, I've added a possibility to specify some properties (such as color, count and timeout) in layout. In my project I use them in the following manner:
<my.domain.tools.ToolDotProgress
android:id="#+id/dots_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:color="#color/colorAccent"
app:count="5"
app:timeout="300" />
To declare these properties I've added to file res/values/attrs.xml the following code:
<declare-styleable name="ToolDotProgress">
<attr name="color" format="color" />
<attr name="count" format="integer" />
<attr name="timeout" format="integer" />
</declare-styleable>
For more information read the manual: https://developer.android.com/training/custom-views/create-view.html
Here is my variant of this class:
public class ToolDotProgress extends View {
// distance between neighbour dot centres
private int mDotStep = 20;
// actual dot radius
private int mDotRadius = 5;
// Bounced Dot Radius
private int mBigDotRadius = 8;
// to get identified in which position dot has to bounce
private int mDotPosition;
// specify how many dots you need in a progressbar
private static final int MIN_COUNT = 1;
private static final int DEF_COUNT = 10;
private static final int MAX_COUNT = 100;
private int mDotCount = DEF_COUNT;
private static final int MIN_TIMEOUT = 100;
private static final int DEF_TIMEOUT = 500;
private static final int MAX_TIMEOUT = 3000;
private int mTimeout = DEF_TIMEOUT;
private int mDotColor = Color.parseColor("#fd583f");
public ToolDotProgress(Context context) {
super(context);
initDotSize();
}
public ToolDotProgress(Context context, AttributeSet attrs) {
super(context, attrs);
initDotSize();
applyAttrs(context, attrs);
}
public ToolDotProgress(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initDotSize();
applyAttrs(context, attrs);
}
private void initDotSize() {
final float scale = getResources().getDisplayMetrics().density;
mDotStep = (int)(mDotStep * scale);
mDotRadius = (int)(mDotRadius * scale);
mBigDotRadius = (int)(mBigDotRadius * scale);
}
private void applyAttrs(Context context, AttributeSet attrs) {
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs, R.styleable.ToolDotProgress, 0, 0);
try {
mDotColor = a.getColor(R.styleable.ToolDotProgress_color, mDotColor);
mDotCount = a.getInteger(R.styleable.ToolDotProgress_count, mDotCount);
mDotCount = Math.min(Math.max(mDotCount, MIN_COUNT), MAX_COUNT);
mTimeout = a.getInteger(R.styleable.ToolDotProgress_timeout, mTimeout);
mTimeout = Math.min(Math.max(mTimeout, MIN_TIMEOUT), MAX_TIMEOUT);
} finally {
a.recycle();
}
}
//Method to draw your customized dot on the canvas
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isShown()) {
Paint paint = new Paint();
paint.setColor(mDotColor);
createDots(canvas, paint);
}
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
startAnimation();
}
private void createDots(Canvas canvas, Paint paint) {
for (int i = 0; i < mDotCount; i++ ) {
int radius = (i == mDotPosition) ? mBigDotRadius : mDotRadius;
canvas.drawCircle(mDotStep / 2 + (i * mDotStep), mBigDotRadius, radius, paint);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// MUST CALL THIS
setMeasuredDimension(mDotStep * mDotCount, mBigDotRadius * 2);
}
private void startAnimation() {
BounceAnimation bounceAnimation = new BounceAnimation();
bounceAnimation.setDuration(mTimeout);
bounceAnimation.setRepeatCount(Animation.INFINITE);
bounceAnimation.setInterpolator(new LinearInterpolator());
bounceAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
if (++mDotPosition >= mDotCount) {
mDotPosition = 0;
}
}
});
startAnimation(bounceAnimation);
}
private class BounceAnimation extends Animation {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
// call invalidate to redraw your view again
invalidate();
}
}
}
Change onDraw() method to:
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
if(isShown){
Paint paint = new Paint();
//set the color for the dot that you want to draw
paint.setColor(Color.parseColor("#fd583f"));
//function to create dot
createDot(canvas,paint);
}
}

How to display divider value below seekbar in Android?

I have added a seekbar to one of my activities.
Its max value is 5. Now, I want to display the divider values (with increment 1, like 0, 1, 2, 3, 4 and 5) below my seekbar. How can I do that?
Is there any system method to achieve this which I am not able to put my hands on? Any inputs are welcomed.
NOTE : I want to apply any changes programatically, not from xml. The numbers should be separated at equal intervals. I could not edit it that precisely though.
I am supposing you want to display view like below in picture.
if that is the case you have to create your own customSeekbar like give code.
CustomSeekBar.java
public class CustomSeekBar extends SeekBar {
private Paint textPaint;
private Rect textBounds = new Rect();
private String text = "";
public CustomSeekBar(Context context) {
super(context);
textPaint = new Paint();
textPaint.setColor(Color.WHITE);
}
public CustomSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
textPaint = new Paint();
textPaint.setTypeface(Typeface.SANS_SERIF);
textPaint.setColor(Color.BLACK);
}
public CustomSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
textPaint = new Paint();
textPaint.setColor(Color.BLACK);
}
#Override
protected synchronized void onDraw(Canvas canvas) {
// First draw the regular progress bar, then custom draw our text
super.onDraw(canvas);
int progress = getProgress();
text = progress + "";
// Now get size of seek bar.
float width = getWidth();
float height = getHeight();
// Set text size.
textPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
textPaint.setTextSize(40);
// Get size of text.
textPaint.getTextBounds(text, 0, text.length(), textBounds);
// Calculate where to start printing text.
float position = (width / getMax()) * getProgress();
// Get start and end points of where text will be printed.
float textXStart = position - textBounds.centerX();
float textXEnd = position + textBounds.centerX();
// Check does not start drawing outside seek bar.
if (textXStart <= 1) textXStart = 20;
if (textXEnd > width) {
textXStart -= (textXEnd - width + 30);
}
// Calculate y text print position.
float yPosition = height;
canvas.drawText(text, textXStart, yPosition, textPaint);
}
public synchronized void setTextColor(int color) {
super.drawableStateChanged();
textPaint.setColor(color);
drawableStateChanged();
}
}
In your Xml file use your custom file like below
<com.waleedsarwar.customseekbar.CustomSeekBar
android:id="#+id/seekbar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:max="5"
android:paddingBottom="16dp" />
This is another approach. I am extending a linearlayout. I put seekbar and another linearlayout(layout_5) which contains 6 textviews with 0-1-2-3-4-5. Better option would be creating a dynamic image(get width from seekBar) which has these numbers according to segment count.
I force seekbar's indicator to stop at specific points(6 points in your case). Instead of doing this, it is possible to set seekBar's maximum progress value to 5. It will work, but it will not give a good user experience.
public class SegmentedSeekBar extends LinearLayout {
private int[] preDefinedValues;
private int currentProgressIndex;
private SeekBar seekBar;
private int segmentCount = 5:
public SegmentedSeekBar(Context context) {
this(context, null, 0);
}
public SegmentedSeekBar(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.seekBarStyle);
}
public SegmentedSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.SegmentedSeekBar,
0, 0);
try {
segmentCount =
a.getInt(R.styleable.SegmentedSeekBar_segmentCount, -1);
} finally {
a.recycle();
}
init();
}
public void init() {
//this values will be used when you need to set progress
preDefinedValues = new int[segmentCount];
for(int i = 0; i < preDefinedValues.length; i++) {
preDefinedValues[i] = (100/(segmentCount-1)) * i;
}
//Get layout_5
//which is linearlayout with 6 textviews
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View sliderView = inflater.inflate(
getSliderId(segmentCount), null);
//seekbar already inside the linearlayout
seekBar = (SeekBar)sliderView.findViewById(R.id.seek_bar);
//linear layout is vertically align
//so add your 6 textview linearlayout
addView(sliderView);
seekBar.setOnTouchListener(seekBarTouchListener);
}
private int getSliderId(int size) {
return R.layout.layout_5;
}
//this method sets progress which is seen in UI not actual progress
//It uses the conversion that we did in beginning
public synchronized void setProgress(int progress) {
if(preDefinedValues != null && progress < preDefinedValues.length && progress >= 0) {
seekBar.setProgress(preDefinedValues[progress]);
currentProgressIndex = progress;
}
}
//this listener make sure the right progress is seen in ui
//take action when user finish with changing progress
SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
int index = 0;
for(int i = 0; i < preDefinedValues.length; i++) {
//try to find closest preDefinedvalues by comparing with latest value
if(Math.abs(seekBar.getProgress() - preDefinedValues[i]) < Math.abs(seekBar.getProgress() - preDefinedValues[index])) {
index = i;
}
}
setProgress(index);
}
};
}

Is there a prebuilt resource/widget for the "nice" indeterminate progress indicator in Android Wear?

I mean this one:
(doesn't look as nice in the single screenshot, but it's much better than the default indeterminate progress -- in fact its shape and animation is pretty similar to the new "Material" indeterminate progress included in Android L, plus it changes colors).
There are no differences in styles.xml between the 19 and 20 platforms, and while there is a new styles_micro.xml, it doesn't seem to include this.
Needed the same thing as you. Not sure if you found a solution that is part of android wear. Since I did not find a solution I did build my own. It is not perfect but as long as you do not study it frame by frame it should look correct.
I created it as it's own view. The code is not perfect and if someone like to improve it go ahead.
public class ProgressView extends ProgressBar {
RectF rectF;
Paint p;
int start = 0;
int maxvalue = 320;
int value = 320;
int[] currentColor = {0,0,0};
boolean reverse = false;
int nextcolor = 1;
final int[][] colors = {
{224,187,63},
{224,46,25},
{39,105,227},
{51,130,49}
};
public ProgressView(Context context) {
super(context);
init();
}
public ProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ProgressView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init(){
p = new Paint();
p.setStrokeWidth(6);
p.setStrokeCap(Paint.Cap.ROUND);
p.setAntiAlias(true);
p.setStyle(Paint.Style.STROKE);
p.setColor(Color.argb(255,colors[0][0], colors[0][1], colors[0][2]));
currentColor = Arrays.copyOf(colors[0], colors[0].length);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
rectF = new RectF(0+5, 0+5, w-5, h-5);
}
#Override
protected void onDraw(Canvas c){
if(reverse)
start += 15;
else
start += 5;
if(start == 360){
start = 1;
}
if(!reverse)
value -= 10;
else
value += 10;
if(value == 0 || value == maxvalue){
reverse = !reverse;
}
transformColor();
p.setColor(Color.argb(255,currentColor[0], currentColor[1], currentColor[2]));
c.drawArc(rectF, start, maxvalue - value, false, p);
invalidate();
}
private void transformColor(){
changeColors(0);
changeColors(1);
changeColors(2);
if(currentColor[0] == colors[nextcolor][0] && currentColor[1] == colors[nextcolor][1] && currentColor[2] == colors[nextcolor][2]){
if(nextcolor == 3)
nextcolor = 0;
else
nextcolor++;
}
}
private void changeColors(int i){
if(currentColor[i] > colors[nextcolor][i]){
currentColor[i] -= 1;
}
if(currentColor[i] < colors[nextcolor][i]){
currentColor[i] += 1;
}
}
}
I hope this can help someone. You could add more properties to it if you like but this is kind of the minimum solution that I found was working.

Android thread wait until visible

I've made a custom pie chart view that I want to animate starting when the pie chart is visible. Currently what I have is the pie chart animating but by the time you can actually see it on the screen the animation is half over. This is what I have:
public class SinglePieChart extends SurfaceView implements SurfaceHolder.Callback {
// Chart setting variables
private int emptyCircleCol, strokeColor, number, total;
// Paint for drawing custom view
private Paint circlePaint;
private RectF rect;
private Context context;
private AnimThread animThread;
private SurfaceHolder holder;
// animation variables
private float speed;
private float current = 0.0f;
private boolean percentsCalculated = false;
private float degree;
private int viewWidth, viewHeight;
public SinglePieChart(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
context = ctx;
// Paint object for drawing in doDraw
circlePaint = new Paint();
circlePaint.setStyle(Style.STROKE);
circlePaint.setStrokeWidth(3);
circlePaint.setAntiAlias(true);
circlePaint.setDither(true);
rect = new RectF();
//get the attributes specified in attrs.xml using the name we included
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.DashboardChartSmall, 0, 0);
try {
//get the colors specified using the names in attrs.xml
emptyCircleCol = a.getColor(R.styleable.DashboardChartSmall_smCircleColor, 0xFF65676E); // light gray is default
strokeColor = a.getColor(R.styleable.DashboardChartSmall_smColor, 0xFF39B54A); // green is default
// Default number values
total = a.getInteger(R.styleable.DashboardChartSmall_smTotal, 1);
number = a.getInteger(R.styleable.DashboardChartSmall_smNumber, 0);
} finally {
a.recycle();
}
this.setZOrderOnTop(true);
holder = getHolder();
holder.setFormat(PixelFormat.TRANSPARENT);
holder.addCallback(this);
}
protected void calculateValues() {
degree = 360 * number / total;
percentsCalculated = true;
speed = 10 * number / total;
viewWidth = this.getMeasuredWidth();
viewHeight = this.getMeasuredHeight();
float top, left, bottom, right;
if (viewWidth < viewHeight) {
left = 4;
right = viewWidth - 4;
top = ((viewHeight - viewWidth) / 2) + 4;
bottom = viewHeight - top;
} else {
top = 4;
bottom = viewHeight - 4;
left = ((viewWidth - viewHeight) / 2) + 4;
right = viewWidth - left;
}
rect.set(left, top, right, bottom);
}
protected void doDraw(Canvas canvas) {
if (total == 0) {
// Number values are not ready
animThread.setRunning(false);
return;
}
if (!percentsCalculated) {
calculateValues();
}
// set the paint color using the circle color specified
float last = current;
float start = -90;
circlePaint.setColor(strokeColor);
canvas.drawArc(rect, start, (last > degree) ? degree : last, false, circlePaint);
start += (last > number) ? number : last;
last = (last < number) ? 0 : last - number;
circlePaint.setColor(emptyCircleCol);
if (current > 360) {
current = 360;
}
canvas.drawArc(rect, start, 360 - current, false, circlePaint);
current += speed;
if (last > 0 || number == 0) {
// we're done
animThread.setRunning(false);
}
}
public void setNumbers(int num, int tot) {
number = num;
total = tot;
invalidate();
requestLayout();
}
public void setColor(int col) {
strokeColor = col;
}
public void redraw() {
calculateValues();
animThread.setRunning(true);
invalidate();
requestLayout();
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
animThread = new AnimThread(holder, context, this);
animThread.setRunning(true);
animThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
animThread.setRunning(false);
boolean retry = true;
while(retry) {
try {
animThread.join();
retry = false;
} catch(Exception e) {
Log.v("Exception Occured", e.getMessage());
}
}
}
public class AnimThread extends Thread {
boolean mRun;
Canvas mcanvas;
SurfaceHolder surfaceHolder;
Context context;
SinglePieChart msurfacePanel;
public AnimThread(SurfaceHolder sholder, Context ctx, SinglePieChart spanel) {
surfaceHolder = sholder;
context = ctx;
mRun = false;
msurfacePanel = spanel;
}
void setRunning(boolean bRun) {
mRun = bRun;
}
#Override
public void run() {
super.run();
while (mRun) {
mcanvas = surfaceHolder.lockCanvas();
if (mcanvas != null) {
msurfacePanel.doDraw(mcanvas);
surfaceHolder.unlockCanvasAndPost(mcanvas);
}
}
}
}
}
Also if you see any programming errors, memory leaks, poor performing code, please let me know. I'm new to Android.
Here is the layout that uses the SinglePieChart class:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.davidscoville.vokab.views.elements.SinglePieChart
android:id="#+id/smallPieChart"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="#+id/dashSmNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:textSize="25sp"
android:textColor="#FFFFFF" />
</RelativeLayout>
<TextView
android:id="#+id/dashSmLabel"
android:layout_width="match_parent"
android:layout_height="20dp"
android:textSize="14sp"
android:gravity="center"
android:textColor="#FFFFFF" />
</merge>
Alright I'm going with the my pie chart won't automatically animate and it will have a new function that the Activity will trigger to start animating once it's ready. I wish there was an easier way...
Alternatively you can use the animation framework(or nine old androids if you want to support older apis). This will allow you to animate properties on your view, in your case the start and current variables.
I'd set this to happen during onAttachedToWindow.
Note if you aren't doing a lot of other things in this pie chart a surfaceview might be overkill for your needs.

Categories

Resources