In my Android app, I have to customize a seekbar and I wonder how I can set a seekbar thumb above its progress line instead of center by default?
You can check it out Discrete Seekbar
seekBar.setMin(0);
seekBar.setMax(yourArray.length);
seekBar.setOnProgressChangeListener(new DiscreteSeekBar.OnProgressChangeListener() {
int onProgressChanged =0;
#Override
public void onProgressChanged(DiscreteSeekBar seekBar, int value, boolean fromUser) {
onProgressChanged = value;
}
#Override
public void onStartTrackingTouch(DiscreteSeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(DiscreteSeekBar seekBar) {
}
});
I looked for this info in many posts and get some ideas from there and here, and created SeekBayHint, which creates text of progress above arrow:
SeekBar
MainClass:
package your_package;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import your_package.R;
public class SeekBarHint extends android.support.v7.widget.AppCompatSeekBar {
private Paint mTextPaint;
private Rect mTextBounds = new Rect();
private Bitmap mBitmapIconArrowDown;
/** for this class yPosition must be as minHeight in layoutFile for this seekBar*/
private static int sTextYPositionIndent = 20;
private float mTextSizeDecrease = 1.75f;
public static void setTextYPositionIndent(int textYPositionIndent) {
sTextYPositionIndent = textYPositionIndent;
}
public SeekBarHint(Context context) {
super(context);
mBitmapIconArrowDown = BitmapFactory.decodeResource(context.getResources(),
R.drawable.progress_seek_bar_arrow_down);
mTextPaint = new Paint();
mTextPaint.setColor(getResources().getColor(R.color.colorPrimary));
}
public SeekBarHint(Context context, AttributeSet attrs) {
super(context, attrs);
mBitmapIconArrowDown = BitmapFactory.decodeResource(context.getResources(),
R.drawable.progress_seek_bar_arrow_down);
mTextPaint = new Paint();
mTextPaint.setColor(getResources().getColor(R.color.colorPrimary));
}
public SeekBarHint(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mBitmapIconArrowDown = BitmapFactory.decodeResource(context.getResources(),
R.drawable.progress_seek_bar_arrow_down);
mTextPaint = new Paint();
mTextPaint.setColor(getResources().getColor(R.color.colorPrimary));
}
#Override
protected synchronized void onDraw(Canvas canvas) {
// first draw the regular progress bar, then custom draw our textString
super.onDraw(canvas);
// now progress position and convert to textString.
String textString = Integer.toString(getProgress()) + "%";
// now get size of seek bar.
float width = getWidth();
float height = getHeight();
// set textString size.
mTextPaint.setTextSize(height / mTextSizeDecrease);
// get size of textString.
mTextPaint.getTextBounds(textString, 0, textString.length(), mTextBounds);
// calculate where to start printing textString.
float position = (width / getMax()) * getProgress();
// get start and end points of where textString will be printed.
float textXStart = position - mTextBounds.centerX();
float textXEnd = position + mTextBounds.centerX();
// check does not start drawing text outside seek bar.
if (textXStart < 0)
textXStart = 0;
if (textXEnd > width)
textXStart -= (textXEnd - width);
// calculate y textString print position.
float yPosition = (height / 2) - mTextBounds.centerY();
canvas.drawText(textString, textXStart, yPosition - sTextYPositionIndent, mTextPaint);
// arrow draw logic
// check does not start drawing arrow outside seek bar
int seekBarAbsoluteWidth = getWidth() - getPaddingLeft() - getPaddingRight();
int thumbPos = (getPaddingLeft() / 2) + (seekBarAbsoluteWidth * getProgress() / getMax());
// set height and width for new bitmap
int arrowHeight = Math.round(mTextBounds.height()/2f);
int arrowWidth = mTextBounds.width()/3;
Bitmap scaledBitmapIconArrowDown = Bitmap
.createScaledBitmap(mBitmapIconArrowDown, arrowWidth, arrowHeight, true);
canvas.drawBitmap(scaledBitmapIconArrowDown, thumbPos, yPosition, null);
}
}
To support different dimension use this code in OnCreateView (if seekBar used in fragment) or in OnCreate (if seekBar used in activity):
DisplayMetrics metrics = getResources().getDisplayMetrics();
int dpi = metrics.densityDpi;
if(dpi < 230){
SeekBarHint.setTextYPositionIndent(5);
} else if (dpi < 310){
SeekBarHint.setTextYPositionIndent(15);
} else if (dpi < 470){
SeekBarHint.setTextYPositionIndent(10);
}
Related
I'm new to android and I'm trying to make a circular progress bar with nice style.
Therefore I found a very simple solution how to make a circular progress bar, using shape="ring"
My code is
mainpage_layout.xml :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="250dp"
android:layout_height="250dp"
android:max="100"
android:progress="80"
android:rotation="-90"
android:layout_centerInParent="true"
android:progressDrawable="#drawable/circular_progress" />
</RelativeLayout>
circular_progress.xml:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:useLevel="true"
android:innerRadiusRatio="2.3"
android:shape="ring"
android:thickness="7dp" >
<solid android:color="#color/colorPrimary" />
</shape>
and its looks like this
I want to style it like this awesome progress bar :
Edit
This is the padded background for the progress bar that i want to make
to add some background to the progress bar and to make the edges of it be rounded instead of squared.
Is there is an easy solution to do that or should I must give up on using this shape="ring" thing.
Thank you very much,
Asaf.
You can use https://github.com/korre/android-circular-progress-bar
There you have a method useRoundedCorners you need to pass false to make it not round by-default it is round at the edge
There is a custom class you can actually take from that library(It is more than enough),
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
public class CircularProgressBar extends View {
private int mViewWidth;
private int mViewHeight;
private final float mStartAngle = -90; // Always start from top (default is: "3 o'clock on a watch.")
private float mSweepAngle = 0; // How long to sweep from mStartAngle
private float mMaxSweepAngle = 360; // Max degrees to sweep = full circle
private int mStrokeWidth = 20; // Width of outline
private int mAnimationDuration = 400; // Animation duration for progress change
private int mMaxProgress = 100; // Max progress to use
private boolean mDrawText = true; // Set to true if progress text should be drawn
private boolean mRoundedCorners = true; // Set to true if rounded corners should be applied to outline ends
private int mProgressColor = Color.BLACK; // Outline color
private int mTextColor = Color.BLACK; // Progress text color
private final Paint mPaint; // Allocate paint outside onDraw to avoid unnecessary object creation
public CircularProgressBar(Context context) {
this(context, null);
}
public CircularProgressBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircularProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
initMeasurments();
drawOutlineArc(canvas);
if (mDrawText) {
drawText(canvas);
}
}
private void initMeasurments() {
mViewWidth = getWidth();
mViewHeight = getHeight();
}
private void drawOutlineArc(Canvas canvas) {
final int diameter = Math.min(mViewWidth, mViewHeight) - (mStrokeWidth * 2);
final RectF outerOval = new RectF(mStrokeWidth, mStrokeWidth, diameter, diameter);
mPaint.setColor(mProgressColor);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setAntiAlias(true);
mPaint.setStrokeCap(mRoundedCorners ? Paint.Cap.ROUND : Paint.Cap.BUTT);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(outerOval, mStartAngle, mSweepAngle, false, mPaint);
}
private void drawText(Canvas canvas) {
mPaint.setTextSize(Math.min(mViewWidth, mViewHeight) / 5f);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setStrokeWidth(0);
mPaint.setColor(mTextColor);
// Center text
int xPos = (canvas.getWidth() / 2);
int yPos = (int) ((canvas.getHeight() / 2) - ((mPaint.descent() + mPaint.ascent()) / 2)) ;
canvas.drawText(calcProgressFromSweepAngle(mSweepAngle) + "%", xPos, yPos, mPaint);
}
private float calcSweepAngleFromProgress(int progress) {
return (mMaxSweepAngle / mMaxProgress) * progress;
}
private int calcProgressFromSweepAngle(float sweepAngle) {
return (int) ((sweepAngle * mMaxProgress) / mMaxSweepAngle);
}
/**
* Set progress of the circular progress bar.
* #param progress progress between 0 and 100.
*/
public void setProgress(int progress) {
ValueAnimator animator = ValueAnimator.ofFloat(mSweepAngle, calcSweepAngleFromProgress(progress));
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(mAnimationDuration);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mSweepAngle = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
animator.start();
}
public void setProgressColor(int color) {
mProgressColor = color;
invalidate();
}
public void setProgressWidth(int width) {
mStrokeWidth = width;
invalidate();
}
public void setTextColor(int color) {
mTextColor = color;
invalidate();
}
public void showProgressText(boolean show) {
mDrawText = show;
invalidate();
}
/**
* Toggle this if you don't want rounded corners on progress bar.
* Default is true.
* #param roundedCorners true if you want rounded corners of false otherwise.
*/
public void useRoundedCorners(boolean roundedCorners) {
mRoundedCorners = roundedCorners;
invalidate();
}
}
Then you can set the view in your xml like
<yourPackageName.CircularProgressBar
android:id="#+id/circularProgress"
android:layout_width="180dp"
android:layout_height="180dp"/>
And in your class you can call it like this,
CircularProgressBar circularProgressBar = (CircularProgressBar) findViewById(R.id.circularProgress);
circularProgressBar.setProgress(50);
circularProgressBar.setProgressColor(Color.BLUE);
Can any one suggest me how to do circle animation and progress button like Uber driver pickup request in Android. If you can provide some code that will be good.
You can refer to this.
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import com.circularseekbar.R;
public class CircularProgressBar extends View {
private Context mContext;
private Paint circleColor, innerColor, circleRing;
private int angle = 0, startAngle = 270, barWidth = 50, maxProgress = 100;
private int width, height, progress=1, tmpProgress=1, progressPercent;
private float innerRadius, outerRadius, adjustmentFactor=100;//The radius of the inner circle
private float cx, cy; //The circle's center X, Y coordinate
private float left, right, top, bottom, startPointX, startPointY, markPointX, markPointY;
private float dx, dy;//The X and Y coordinate for the top left corner of the marking drawable
private Bitmap progressMark, progressMarkPressed;
private RectF rect = new RectF();
{
circleColor = new Paint();
innerColor = new Paint();
circleRing = new Paint();
circleColor.setColor(Color.parseColor("#ff33b5e5")); // Set default
// progress
// color to holo
// blue.
innerColor.setColor(Color.BLACK); // Set default background color to
// black
circleRing.setColor(Color.GRAY);// Set default background color to Gray
circleColor.setAntiAlias(true);
innerColor.setAntiAlias(true);
circleRing.setAntiAlias(true);
circleColor.setStrokeWidth(25);
innerColor.setStrokeWidth(15);
circleRing.setStrokeWidth(10);
circleColor.setStyle(Paint.Style.FILL);
}
public CircularProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
}
public CircularProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
public CircularProgressBar(Context context) {
super(context);
mContext = context;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getWidth(); // Get View Width
height = getHeight();// Get View Height
int size = (width > height) ? height : width; // Choose the smaller
// between width and
// height to make a
// square
cx = width / 2; // Center X for circle
cy = height / 2; // Center Y for circle
outerRadius = size / 2; // Radius of the outer circle
innerRadius = outerRadius - barWidth; // Radius of the inner circle
left = cx - outerRadius; // Calculate left bound of our rect
right = cx + outerRadius;// Calculate right bound of our rect
top = cy - outerRadius;// Calculate top bound of our rect
bottom = cy + outerRadius;// Calculate bottom bound of our rect
startPointX = cx; // 12 O'clock X coordinate
startPointY = cy - outerRadius;// 12 O'clock Y coordinate
markPointX = startPointX;// Initial locatino of the marker X coordinate
markPointY = startPointY;// Initial locatino of the marker Y coordinate
rect.set(left, top, right, bottom); // assign size to rect
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawCircle(cx, cy, outerRadius, circleRing);
canvas.drawArc(rect, startAngle, angle, true, circleColor);
canvas.drawCircle(cx, cy, innerRadius, innerColor);
super.onDraw(canvas);
}
public int getAngle() {
return angle;
}
public void setAngle(int angle) {
//System.out.println("Angel "+angle);
this.angle = angle;
float donePercent = (((float) this.angle) / 360) * 100;
float progress = (donePercent / 100) * getMaxProgress();
setProgressPercent(Math.round(donePercent));
setProgress(Math.round(progress));
}
public int getBarWidth() {
return barWidth;
}
public void setBarWidth(int barWidth) {
this.barWidth = barWidth;
}
public int getMaxProgress() {
return maxProgress;
}
public void setMaxProgress(int maxProgress) {
this.maxProgress = maxProgress;
}
public int getProgress() {
return progress;
}
public void setProgress(int progress) {
if (this.progress != progress) {
this.progress = progress;
int newPercent = (this.progress * 100) / this.maxProgress;
int newAngle = (newPercent * 360) / 100;
this.setAngle(newAngle);
this.setProgressPercent(newPercent);
}
}
long mAnimStartTime;
Handler mHandler = new Handler();
Runnable mTick = new Runnable() {
public void run() {
invalidate();
update();
mHandler.postDelayed(this, 100); // 20ms == 60fps
}
};
public void update(){
if(IS_ACTIVE){
setProgress(tmpProgress);
if(tmpProgress>maxProgress){
stopAnimation();
}
tmpProgress++;
}
}
boolean IS_ACTIVE=false;
public void startAnimation() {
IS_ACTIVE=true;
tmpProgress=1;
mHandler.removeCallbacks(mTick);
mHandler.post(mTick);
}
public void stopAnimation() {
IS_ACTIVE=false;
progress=1;
mHandler.removeCallbacks(mTick);
}
public int getProgressPercent() {
return progressPercent;
}
public void setProgressPercent(int progressPercent) {
this.progressPercent = progressPercent;
}
public void setRingBackgroundColor(int color) {
circleRing.setColor(color);
}
public void setBackGroundColor(int color) {
innerColor.setColor(color);
}
public void setProgressColor(int color) {
circleColor.setColor(color);
}
public float getAdjustmentFactor() {
return adjustmentFactor;
}
public void setAdjustmentFactor(float adjustmentFactor) {
this.adjustmentFactor = adjustmentFactor;
}
}
I created my own TextView by extending the TextView class in order to improve its display. I created various Paint and stuff to add a kind of margin. Then text has to be displayed right after the margin. If I set
android:layout_width="fill_parent"
the display is ok and my line is fully filled with a white background (as defined in my layout).
BUT if I set
android:layout_width="wrap_content"
the display goes wrong and the end of the text of my TextView is cropped. I guess this is due to the fact that I made a Translate in the onDraw method of my TextView but I don't know how to fix it.
Please note that I need the set wrap_content because I want to add another TextBox right after, and a LinearLayout around both, but for the moment the other TextBox erase a part of the content of the first one.
The code of my new TextBox is the following one :
package com.flo.ui;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.widget.TextView;
import com.flo.musicalnotes.R;
public class NoteItemTextView extends TextView {
// Properties
private Paint marginPaint;
private Paint linePaint;
private Paint circlePaint;
private int paperColor;
private float margin;
private float marginEnd;
private float textStart;
// Initialization
public NoteItemTextView(Context context) {
super(context);
this.Init(context);
}
public NoteItemTextView(Context context, AttributeSet attrs)
{
super(context, attrs);
this.Init(context);
}
private void Init(Context context)
{
// Resources retrieval
Resources myResources = getResources();
// Brush definition
this.marginPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.marginPaint.setColor(myResources.getColor(R.color.marginColor));
this.marginPaint.setStrokeWidth((float) 1.8);
this.linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.linePaint.setColor(myResources.getColor(R.color.underlineColor));
this.circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.circlePaint.setColor(myResources.getColor(R.color.marginColor));
this.circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
// various resources
this.paperColor = myResources.getColor(R.color.bgColor);
this.margin = myResources.getDimension(R.dimen.marginSize);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int ot = getResources().getConfiguration().orientation;
switch(ot)
{
case Configuration.ORIENTATION_LANDSCAPE:
this.marginEnd = this.margin + metrics.widthPixels / 100;
this.textStart = this.marginEnd + metrics.widthPixels / 100;
case Configuration.ORIENTATION_PORTRAIT:
this.marginEnd = this.margin + metrics.heightPixels / 100;
this.textStart = this.marginEnd + metrics.heightPixels / 100;
default:
this.marginEnd = this.margin + 5;
this.textStart = this.marginEnd + 10;
}
}
//#Override
protected void onDraw(Canvas canvas) {
// paper color
canvas.drawColor(this.paperColor);
// lines drawing
canvas.drawLine(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight(), this.linePaint);
// marge drawing
canvas.drawLine(this.margin, 0, this.margin, getMeasuredHeight(), this.marginPaint);
canvas.drawLine(this.marginEnd, 0, this.marginEnd, getMeasuredHeight(), this.marginPaint);
double x = (this.textStart + this.marginEnd) / 1.8;
float y1 = getMeasuredHeight() / 3;
float y2 = getMeasuredHeight() * 2 / 3;
float radius = (float) 2.5;
canvas.drawCircle((float) x, y1, radius, this.circlePaint);
canvas.drawCircle((float) x, y2, radius, this.circlePaint);
canvas.save();
canvas.translate(this.textStart, 0);
super.onDraw(canvas);
canvas.restore();
}
}
Thanks for your help !
Try to add this code to your custom textview class
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = getMeasuredHeight();
int width = getMeasuredWidth();
Log.e(getClass().getSimpleName() , String.format("height x %s ::: width x %s ",height , width));
float density = getResources().getDisplayMetrics().density;
//Extra space after last letter.
float px = 2 * density;
int adjustedWidth = (int) (width + textStart + px);
setMeasuredDimension(adjustedWidth, height);
}
add this to your textview
android:paddingRight="25dp"
How to customize a ProgressBar to look like a Thermometer ? with the possibility to change color.
My suggestion was to rotate the progressBar 90° to become vertical then have it overlay an image of an empty Thermometer but it's bad and messy solution.
I Think the best will be to either to extends View or ProgressBar class and customize the draw method but I have no idea how to draw Thermometer, any Help would be appreciated.
I created something like this for a project
package com.janslab.thermometer.widgets;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.Scroller;
import com.janslab.thermometer.R;
public class DummyThermometer extends View {
private Paint mInnerCirclePaint;
private Paint mOuterCirclePaint;
private Paint mFirstOuterCirclePaint;
//thermometer arc paint
private Paint mFirstOuterArcPaint;
//thermometer lines paints
private Paint mInnerLinePaint;
private Paint mOuterLinePaint;
private Paint mFirstOuterLinePaint;
//thermometer radii
private int mOuterRadius;
private int mInnerRadius;
private int mFirstOuterRadius;
//thermometer colors
private int mThermometerColor = Color.rgb(200, 115, 205);
//circles and lines variables
private float mLastCellWidth;
private int mStageHeight;
private float mCellWidth;
private float mStartCenterY; //center of first cell
private float mEndCenterY; //center of last cell
private float mStageCenterX;
private float mXOffset;
private float mYOffset;
// I 1st Cell I 2nd Cell I 3rd Cell I
private static final int NUMBER_OF_CELLS = 3; //three cells in all ie.stageHeight divided into 3 equal cells
//animation variables
private float mIncrementalTempValue;
private boolean mIsAnimating;
private Animator mAnimator;
public DummyThermometer(Context context) {
this(context, null);
}
public DummyThermometer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DummyThermometer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (attrs != null) {
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Thermometer, defStyle, 0);
mThermometerColor = a.getColor(R.styleable.Thermometer_therm_color, mThermometerColor);
a.recycle();
}
init();
}
private void init() {
mInnerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mInnerCirclePaint.setColor(mThermometerColor);
mInnerCirclePaint.setStyle(Paint.Style.FILL);
mInnerCirclePaint.setStrokeWidth(17f);
mOuterCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOuterCirclePaint.setColor(Color.WHITE);
mOuterCirclePaint.setStyle(Paint.Style.FILL);
mOuterCirclePaint.setStrokeWidth(32f);
mFirstOuterCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFirstOuterCirclePaint.setColor(mThermometerColor);
mFirstOuterCirclePaint.setStyle(Paint.Style.FILL);
mFirstOuterCirclePaint.setStrokeWidth(60f);
mFirstOuterArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFirstOuterArcPaint.setColor(mThermometerColor);
mFirstOuterArcPaint.setStyle(Paint.Style.STROKE);
mFirstOuterArcPaint.setStrokeWidth(30f);
mInnerLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mInnerLinePaint.setColor(mThermometerColor);
mInnerLinePaint.setStyle(Paint.Style.FILL);
mInnerLinePaint.setStrokeWidth(17f);
mOuterLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOuterLinePaint.setColor(Color.WHITE);
mOuterLinePaint.setStyle(Paint.Style.FILL);
mFirstOuterLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFirstOuterLinePaint.setColor(mThermometerColor);
mFirstOuterLinePaint.setStyle(Paint.Style.FILL);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mStageCenterX = getWidth() / 2;
mStageHeight = getHeight();
mCellWidth = mStageHeight / NUMBER_OF_CELLS;
//center of first cell
mStartCenterY = mCellWidth / 2;
//move to 3rd cell
mLastCellWidth = (NUMBER_OF_CELLS * mCellWidth);
//center of last(3rd) cell
mEndCenterY = mLastCellWidth - (mCellWidth / 2);
// mOuterRadius is 1/4 of mCellWidth
mOuterRadius = (int) (0.25 * mCellWidth);
mInnerRadius = (int) (0.656 * mOuterRadius);
mFirstOuterRadius = (int) (1.344 * mOuterRadius);
mFirstOuterLinePaint.setStrokeWidth(mFirstOuterRadius);
mOuterLinePaint.setStrokeWidth(mFirstOuterRadius / 2);
mFirstOuterArcPaint.setStrokeWidth(mFirstOuterRadius / 4);
mXOffset = mFirstOuterRadius / 4;
mXOffset = mXOffset / 2;
//get the d/f btn firstOuterLine and innerAnimatedline
mYOffset = (mStartCenterY + (float) 0.875 * mOuterRadius) - (mStartCenterY + mInnerRadius);
mYOffset = mYOffset / 2;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawFirstOuterCircle(canvas);
drawOuterCircle(canvas);
drawInnerCircle(canvas);
drawFirstOuterLine(canvas);
drawOuterLine(canvas);
animateInnerLine(canvas);
drawFirstOuterCornerArc(canvas);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//take care of paddingTop and paddingBottom
int paddingY = getPaddingBottom() + getPaddingTop();
//get height and width
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
height += paddingY;
setMeasuredDimension(width, height);
}
private void drawInnerCircle(Canvas canvas) {
drawCircle(canvas, mInnerRadius, mInnerCirclePaint);
}
private void drawOuterCircle(Canvas canvas) {
drawCircle(canvas, mOuterRadius, mOuterCirclePaint);
}
private void drawFirstOuterCircle(Canvas canvas) {
drawCircle(canvas, mFirstOuterRadius, mFirstOuterCirclePaint);
}
private void drawCircle(Canvas canvas, float radius, Paint paint) {
canvas.drawCircle(mStageCenterX, mEndCenterY, radius, paint);
}
private void drawOuterLine(Canvas canvas) {
float startY = mEndCenterY - (float) (0.875 * mOuterRadius);
float stopY = mStartCenterY + (float) (0.875 * mOuterRadius);
drawLine(canvas, startY, stopY, mOuterLinePaint);
}
private void drawFirstOuterLine(Canvas canvas) {
float startY = mEndCenterY - (float) (0.875 * mFirstOuterRadius);
float stopY = mStartCenterY + (float) (0.875 * mOuterRadius);
drawLine(canvas, startY, stopY, mFirstOuterLinePaint);
}
private void drawLine(Canvas canvas, float startY, float stopY, Paint paint) {
canvas.drawLine(mStageCenterX, startY, mStageCenterX, stopY, paint);
}
//simulate temperature measurement for now
private void animateInnerLine(Canvas canvas) {
if (mAnimator == null)
measureTemperature();
if (!mIsAnimating) {
mIncrementalTempValue = mEndCenterY + (float) (0.875 * mInnerRadius);
mIsAnimating = true;
} else {
mIncrementalTempValue = mEndCenterY + (float) (0.875 * mInnerRadius) - mIncrementalTempValue;
}
if (mIncrementalTempValue > mStartCenterY + mInnerRadius) {
float startY = mEndCenterY + (float) (0.875 * mInnerRadius);
drawLine(canvas, startY, mIncrementalTempValue, mInnerCirclePaint);
} else {
float startY = mEndCenterY + (float) (0.875 * mInnerRadius);
float stopY = mStartCenterY + mInnerRadius;
drawLine(canvas, startY, stopY, mInnerCirclePaint);
mIsAnimating = false;
stopMeasurement();
}
}
private void drawFirstOuterCornerArc(Canvas canvas) {
float y = mStartCenterY - (float) (0.875 * mFirstOuterRadius);
RectF rectF = new RectF(mStageCenterX - mFirstOuterRadius / 2 + mXOffset, y + mFirstOuterRadius, mStageCenterX + mFirstOuterRadius / 2 - mXOffset, y + (2 * mFirstOuterRadius) + mYOffset);
canvas.drawArc(rectF, -180, 180, false, mFirstOuterArcPaint);
}
public void setThermometerColor(int thermometerColor) {
this.mThermometerColor = thermometerColor;
mInnerCirclePaint.setColor(mThermometerColor);
mFirstOuterCirclePaint.setColor(mThermometerColor);
mFirstOuterArcPaint.setColor(mThermometerColor);
mInnerLinePaint.setColor(mThermometerColor);
mFirstOuterLinePaint.setColor(mThermometerColor);
invalidate();
}
//simulate temperature measurement for now
private void measureTemperature() {
mAnimator = new Animator();
mAnimator.start();
}
private class Animator implements Runnable {
private Scroller mScroller;
private final static int ANIM_START_DELAY = 1000;
private final static int ANIM_DURATION = 4000;
private boolean mRestartAnimation = false;
public Animator() {
mScroller = new Scroller(getContext(), new AccelerateDecelerateInterpolator());
}
public void run() {
if (mAnimator != this)
return;
if (mRestartAnimation) {
int startY = (int) (mStartCenterY - (float) (0.875 * mInnerRadius));
int dy = (int) (mEndCenterY + mInnerRadius);
mScroller.startScroll(0, startY, 0, dy, ANIM_DURATION);
mRestartAnimation = false;
}
boolean isScrolling = mScroller.computeScrollOffset();
mIncrementalTempValue = mScroller.getCurrY();
if (isScrolling) {
invalidate();
post(this);
} else {
stop();
}
}
public void start() {
mRestartAnimation = true;
postDelayed(this, ANIM_START_DELAY);
}
public void stop() {
removeCallbacks(this);
mAnimator = null;
}
}
private void stopMeasurement() {
if (mAnimator != null)
mAnimator.stop();
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
measureTemperature();
}
#Override
protected void onDetachedFromWindow() {
stopMeasurement();
super.onDetachedFromWindow();
}
#Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
switch (visibility) {
case View.VISIBLE:
measureTemperature();
break;
default:
stopMeasurement();
break;
}
}
}
attrs.xml file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Thermometer">
<attr name="therm_color" format="color" />
</declare-styleable>
</resources>
First I would provide 2 setters, one for color and one for the temperature value, normalized from 0 ... 1, where 0 means no visible bar, and 1 means a fully visible bar.
public void setColor(int color) {
mColor = color;
invalidate(); // important, this triggers onDraw
}
public void setValue(float value) {
mValue = -(value - 1);
invalidate(); // important, this triggers onDraw
}
Notice for value, I reverse the value, since we draw the bar from bottom up, instead from top down. It makes sense in the canvas.drawRect method.
If your CustomView may have custom sizes, set your size of the progressBar (I refer to the inner bar as progressBar) in onSizeChanged, as this gets called when the View has changed it's size.
If it is a fixed size, you can just provide those values statically in an init function or the constructor.
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mProgressRect = new Rect(
/*your bar left offset relative to base bitmap*/,
/*your bar top offset relative to base bitmap*/,
/*your bar total width*/,
/*your max bar height*/
);
}
Then in ondraw, take these values into account and draw accordingly.
First draw the Bitmap, depending on your selected color (I would provide the thermometer base as a Bitmap, as long as it does not have to be completely dynamically drawn (special requirements)
Then draw the progress bar, with an height based on mValue * totalHeight of the bar, using the color provided in the setter.
For example:
#Override
protected void onDraw(Canvas canvas) {
// draw your thermometer base, bitmap based on color value
canvas.drawBitmap( /*your base thermometer bitmap here*/ );
// draw the "progress"
canvas.drawRect(mProgressRect.left, mProgressRect.top + (mValue * mProgressRect.bottom - mProgressRect.top), mProgressRect.right, mProgressRect.bottom, mPaint);
}
Hope that helps.
P.S.:
If you want to have the thermometer base image also dynamically drawn, it's a slightly different story, it would involve creating a path first and draw it with a Paint object, instead of drawing the bitmap.
EDIT:
Even better, if you want a simple solution for the "roundness" of the bar, draw a line instead a rect.
Define a line paint object like this:
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(20); // thickness of your bar
then in onDraw, instead drawRect:
// draw the "progress"
canvas.drawLine(mProgressRect.left, mProgressRect.top + (mValue * mProgressRect.bottom - mProgressRect.top), mProgressRect.left, mProgressRect.bottom, mPaint);
Be sure to adjust your mProgressRectaccordingly.
I want to change the circle radius dependent on the slider position
I created both individually.
while apply the circle method to the the Progress bar.
facing error.
Actually I call the Progress-Bar(Seek Bar) with
setContentView(R.layout.main);
and for drawing circle as you know I have to used setContentView(demoview);
Query: I want to merge both layout as display into image.
I didn't have idea will it possible or not?
Any guidance,tutorial appreciable.
Thanks for give your valuable time for my query.
Hope you have solution.
Check this out..
I am not using android seekbar, instead i am drawing a similar seekbar on canvas..
Check if this code can help u..
package com.test;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class SampleComp extends View implements OnTouchListener {
private Paint paint = null;
private int width = 0;
private int height = 0;
private int barStartX = 20;
private int barStartY = 20;
private int barEndX;
private int barEndY = 30;
private int radius;
private int heightAvailableForCircle;
private int widthAvailableForCircle;
private int maxRadius;
private int totalSeekBarLength;
private int currentSeekBarLength = 10;
private int whatPercentOfSeekBarIsSelected = 50;
public SampleComp(Context context) {
super(context);
paint = new Paint();
paint.setAntiAlias(true);
setOnTouchListener(this);
}
public SampleComp(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setAntiAlias(true);
}
public SampleComp(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = new Paint();
paint.setAntiAlias(true);
}
#Override
public void onDraw(Canvas canvas){
barEndX = getWidth() - 20;
paint.setColor(Color.WHITE);
setWidth(canvas.getWidth());
setHeight(canvas.getHeight());
setHeightAvailableForCircle(getViewHeight() - barEndY);
setWidthAvailableForCircle(getViewWidth() - 40);
System.out.println("heightAvailableForCircle: "+getAvailableHeightForCircle());
System.out.println("widthAvailableForCircle: "+getWidthAvailableForCircle());
totalSeekBarLength = barEndX - barStartX;
System.out.println("SEEK LEN: "+totalSeekBarLength);
canvas.drawRect(barStartX, barStartY, barEndX, barEndY, paint);
paint.setColor(Color.BLUE);
setMaxRadius(heightAvailableForCircle, widthAvailableForCircle);
whatPercentOfSeekBarIsSelected = getSelectedSeekBarPercentage(totalSeekBarLength,getCurrentSeekBarLenghtSelected());
System.out.println("whatPercentOfSeekBarIsSelected: "+whatPercentOfSeekBarIsSelected);
System.out.println("!!!!!: "+canvas.getWidth());
System.out.println("####: "+getViewWidth());
System.out.println("^^^^^^^^^************: "+ (whatPercentOfSeekBarIsSelected * (getViewWidth() - 40)) / 100);
canvas.drawRect(barStartX, barStartY, ( (whatPercentOfSeekBarIsSelected * (getViewWidth() - 40)) / 100) + 20,
barEndY, paint);
paint.setColor(Color.GRAY);
setRadius(whatPercentOfSeekBarIsSelected);
canvas.drawCircle( (canvas.getWidth())/2, (canvas.getHeight() - 30)/2, radius, paint);
}
private void setRadius(int per){
this.radius = (getMaxRadius() * per)/100;
}
private int getSelectedSeekBarPercentage(int total, int current){
int per = 0;
per = ( (current * 100) / total);
return per;
}
private void setRadius(int total, int current){
System.out.println("total: "+total);
System.out.println("current: "+current);
this.radius = ( ( (getMaxRadius()/2) * current) / 100);
System.out.println("radius: "+this.radius);
}
private void setMaxRadius(int h, int w){
this.maxRadius = h < w ? h/2 : w/2 ;
}
private int getMaxRadius(){
return this.maxRadius;
}
private void setWidth(int w){
this.width = w;
}
private void setHeight(int h){
this.height = h;
}
private int getViewWidth(){
return this.width;
}
private int getViewHeight() {
return this.height;
}
private void setHeightAvailableForCircle(int availableHeightForCircle){
this.heightAvailableForCircle = availableHeightForCircle;
}
private int getAvailableHeightForCircle(){
return this.heightAvailableForCircle;
}
private void setWidthAvailableForCircle(int wid){
this.widthAvailableForCircle = wid;
}
private int getWidthAvailableForCircle(){
return this.widthAvailableForCircle;
}
private void setCurrentSeekBarLength(int x){
this.currentSeekBarLength = x;
}
private int getCurrentSeekBarLenghtSelected(){
return this.currentSeekBarLength;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
System.out.println("x: "+x);
System.out.println("y: "+y);
if(x >= 10 && x<= barEndX && y >= 10 && y <= 30){
System.out.println("TRUE");
setCurrentSeekBarLength(x - 20);
invalidate();
}
return false;
}
}
And this is my Activity class:
package com.test;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.widget.ImageView;
public class SampleActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleComp(this));
}
}
Can u be little more specific regarding what u want to achieve. Ur question isn't that clear. What i can make out is, u want to increase/decrease the radius of the circle based on the progress bar value.
Have a look at the android custom component guide. You basically have to derive your circle component from android.view.View. Then you can add it to your layout like every other component.