I am trying to create a seekbar. It has to look like below in picture.
Here is my seekbar in layout:
<SeekBar
android:id="#+id/progress_bar"
android:layout_width="match_parent"
android:layout_height="#dimen/progress_bar_height"
android:minHeight="#dimen/progress_bar_height"
android:maxHeight="#dimen/progress_bar_height"
android:progressDrawable="#drawable/progressbar_seek_bar"
android:thumbOffset="0dp"
android:thumb="#drawable/progressbar_thumb"
android:max="100"/>
Here is progressbar_thumb drawable XML:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:gravity="center_horizontal">
<bitmap android:src="#drawable/img_grabber_body"/>
</item>
<item>
<bitmap android:src="#drawable/img_grabber_knob_long"/>
</item>
</layer-list>
There are two different images in thumb one vertical line and round knob. My problem is how to use such thumbs in Android seekbar. If I set the thumb in XML it considers width of thumb as width of round knob. Because of this it leaves some space between thumb and progress as you see in below images. I can customize view to show complete thumb but how to remove this extra space around the thumb?
I was facing a similar problem.
I found really useful this code.
You just have to change where the text is drawn in the onDraw(Canvas canvas)
Hope this could help.
EDIT 1:
Here is my code that put the label with the progression percentage under the seekbar thumb. You just have to change values of label_x and label_y in the onDraw(Canvas canvas) to change the position where the label is drawn.
public class SeekBarWithHint extends android.support.v7.widget.AppCompatSeekBar {
private final int MIN_PROGRESS_VALUE = 0;
private final int MAX_PROGRESS_VALUE = 100;
private Paint mSeekBarHintPaint;
private int mHintTextColor;
private float mHintTextSize;
public SeekBarWithHint(Context context) {
super(context);
init();
}
public SeekBarWithHint(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.SeekBarWithHint,
0, 0);
try {
mHintTextColor = a.getColor(R.styleable.SeekBarWithHint_hint_text_color, 0);
mHintTextSize = a.getDimension(R.styleable.SeekBarWithHint_hint_text_size, 0);
} finally {
a.recycle();
}
init();
}
public SeekBarWithHint(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setMax(MAX_PROGRESS_VALUE);
mSeekBarHintPaint = new TextPaint();
mSeekBarHintPaint.setColor(mHintTextColor);
mSeekBarHintPaint.setTextSize(mHintTextSize);
}
#Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
int label_x = (int) (getThumb().getBounds().centerX());
int label_y = getHeight();
canvas.drawText(String.valueOf(getProgress()) + "%", getProgress() > 95 ? label_x - 55 : label_x, label_y, mSeekBarHintPaint);
}
private void animateProgression(int progress) {
final ObjectAnimator animation = ObjectAnimator.ofInt(this, "progress", MIN_PROGRESS_VALUE, progress);
animation.setDuration(3000);
animation.setInterpolator(new DecelerateInterpolator());
animation.start();
this.setProgress(progress);
this.clearAnimation();
}
public void setSeekbarProgress(int progress) {
animateProgression(progress);
}
}
EDIT 2:
Better use this DescreteSeekBar that do EXACTLY what you are looking for.
Related
I'm making a custom View that displays a spinner to show that the camera is focusing.
public class ApertureAnimation extends View
{
private final static String TAG = "ApertureAnimation";
private AnimationDrawable animationDrawable;
public ApertureAnimation(Context context)
{
super(context);
init(null, 0);
}
public ApertureAnimation(Context context, AttributeSet attrs)
{
super(context, attrs);
init(attrs, 0);
}
public ApertureAnimation(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle)
{
animationDrawable = (AnimationDrawable) getResources().getDrawable(R.drawable.apperature_focus, null);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int contentWidth = getWidth() - paddingLeft - paddingRight;
int contentHeight = getHeight() - paddingTop - paddingBottom;
if(animationDrawable != null)
{
animationDrawable.setBounds(paddingLeft, paddingTop, paddingLeft + contentWidth, paddingTop + contentHeight);
animationDrawable.start();
animationDrawable.draw(canvas);
}
}
public void start()
{
if(animationDrawable != null)
animationDrawable.start();
}
public void stop()
{
if(animationDrawable != null)
animationDrawable.stop();
}
}
apperature_focus.xml
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="#drawable/apperature_0" android:duration="200" />
<item android:drawable="#drawable/apperature_30" android:duration="200" />
<item android:drawable="#drawable/apperature_60" android:duration="200" />
</animation-list>
The issue I'm having is that only the first frame shows when I call start(). I am able to get the animation to work if I use an ImageView outside of a class extending View, like this https://developer.android.com/guide/topics/graphics/drawable-animation, but when I try to bring that code into this custom UI component it doesn't work.
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);
}
};
}
I need to customize a seek bar in such a way that, at pre determined time say , 30sec, I should have a dot on the seek bar.
This duration varies for each and every video, so how to I place a dot point on the seek bar at a particular second
Here're some possibilities:
Put 'dot' view just above SeekBar one. Will not explain it here in details, because it's trivial android-layout task;
Extend SeekBar, like the following (refer to this good explanation about custom views):
/**
* Seek bar with dots on it on specific time / percent
*/
public class DottedSeekBar extends SeekBar {
/** Int values which corresponds to dots */
private int[] mDotsPositions = null;
/** Drawable for dot */
private Bitmap mDotBitmap = null;
public DottedSeekBar(final Context context) {
super(context);
init(null);
}
public DottedSeekBar(final Context context, final AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public DottedSeekBar(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
/**
* Initializes Seek bar extended attributes from xml
*
* #param attributeSet {#link AttributeSet}
*/
private void init(final AttributeSet attributeSet) {
final TypedArray attrsArray = getContext().obtainStyledAttributes(attributeSet, R.styleable.DottedSeekBar, 0, 0);
final int dotsArrayResource = attrsArray.getResourceId(R.styleable.DottedSeekBar_dots_positions, 0);
if (0 != dotsArrayResource) {
mDotsPositions = getResources().getIntArray(dotsArrayResource);
}
final int dotDrawableId = attrsArray.getResourceId(R.styleable.DottedSeekBar_dots_drawable, 0);
if (0 != dotDrawableId) {
mDotBitmap = BitmapFactory.decodeResource(getResources(), dotDrawableId);
}
}
/**
* #param dots to be displayed on this SeekBar
*/
public void setDots(final int[] dots) {
mDotsPositions = dots;
invalidate();
}
/**
* #param dotsResource resource id to be used for dots drawing
*/
public void setDotsDrawable(final int dotsResource) {
mDotBitmap = BitmapFactory.decodeResource(getResources(), dotsResource);
invalidate();
}
#Override
protected synchronized void onDraw(final Canvas canvas) {
super.onDraw(canvas);
final int width = getMeasuredWidth();
final int step = width / getMax();
if (null != mDotsPositions && 0 != mDotsPositions.length && null != mDotBitmap) {
// draw dots if we have ones
for (int position : mDotsPositions) {
canvas.drawBitmap(mDotBitmap, position * step, 0, null);
}
}
}
}
Don't forget about custom attrs in res/values/attrs.xml:
<resources>
<declare-styleable name="DottedSeekBar">
<attr name="dots_positions" format="reference"/>
<attr name="dots_drawable" format="reference"/>
</declare-styleable>
</resources>
And using the following code:
setContentView(R.layout.main);
final DottedSeekBar bar = (DottedSeekBar) findViewById(R.id.seekBar);
bar.setDots(new int[] {25, 50, 75});
bar.setDotsDrawable(R.drawable.dot);
with main.xml layout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.TestApp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.TestApp.DottedSeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/seekBar" />
</LinearLayout>
or just single main.xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.TestApp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.TestApp.DottedSeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/seekBar"
custom:dots_positions="#array/dots"
custom:dots_drawable="#drawable/dot" />
</LinearLayout>
You could obtain the following image:
Refer to this example for more ideas;
Regarding putting dots on specific 'time': SeekBar is not about time, so it's up to You to provide any time-related logic.
The above answer is completely correct but the onDraw method could have been improved a little.
#Override
protected synchronized void onDraw(final Canvas canvas) {
super.onDraw(canvas);
final float width=getMeasuredWidth()-getPaddingLeft()-getPaddingRight();
final float step=width/(float)(getMax());
if (null != mDotsPositions && 0 != mDotsPositions.length && null != mDotBitmap) {
// draw dots if we have ones
for (int position : mDotsPositions) {
canvas.drawBitmap(mDotBitmap, position * step, 0, null);
}
}
}
I am just trying to implement a customView from scratch i.e by extending the view class and overriding the onDraw() method. Just trying to build a simple view, a view which just draws a circle for now. I am facing some issue in aligning it and i am not able to understand how android is calculating the views dimensions.
Just having only the view i.e setContentView(new MyCustomView(this)) works fine... it takes the entire space and draws the circle. But if i impose any constraints i.e giving margin, or aligning it in centreparent makes my view completely lost and it doesnt draw anything. The issue is the view is getting clipped by its parent but not able to understand why its getting clipped. Any help around this would be greatly appreciated. Here is my code.
Here is my customView
public class MyCustomView extends View {
private Paint myPaint=null;
private boolean useCenters;
private float xCoordinate;
private float yCoordinate;
private float viewWidth;
private float viewHeight;
private int totalTime;
private static float SWEEP_INC ;
private RectF myRect;
private boolean shouldInvalidate;
private float mSweep;
public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initPaintComponents();
}
public MyCustomView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MyCustomView(Context context) {
this(context,null);
}
private void initPaintComponents() {
myPaint = new Paint();
myPaint.setStyle(Paint.Style.STROKE);
myPaint.setStrokeWidth(4);
myPaint.setColor(0x880000FF);
useCenters = false;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
calculateCoordinates();
}
private void calculateCoordinates() {
xCoordinate = getX();
yCoordinate = getY();
viewWidth = getWidth();
viewHeight = getHeight();
myRect = new RectF(xCoordinate+3, yCoordinate+3, xCoordinate+viewWidth-(viewWidth/10), yCoordinate+viewHeight-(viewHeight/10));
Log.i("SAMPLEARC","xcoordinate: "+xCoordinate+" ycoordinate: "+yCoordinate+" view width:"+viewWidth+" view height:"+viewHeight+" measured width: "+getMeasuredWidth()+"measured height:"+getMeasuredHeight());
}
public int getTotalTime() {
return totalTime;
}
public void setTotalTime(int totalTime) {
this.totalTime = totalTime;
SWEEP_INC = (float)6/totalTime;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawArc(myRect, 0, mSweep, useCenters, myPaint);
mSweep += SWEEP_INC;
if(mSweep > 280)
{
myPaint.setColor(0x888800FF);
}
invalidate();
}
}
MyActivity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MyCustomView myView = (MyCustomView) findViewById(R.id.customimg);
myView.setTotalTime(10);
}
main.xml
RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#android:color/white"
com.example.anim.MyCustomView android:id="#+id/customimg"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
if i remove that centerInParent in xml it gets drawn. so callling setMeasureDimentions() in onMeasure() doesnt have any affect either. But the xcoodinate,ycoordinate,viewWidth and viewHeight seems to give the correct values. Just need to understand why the view is getting clipped and how android is calculating the dimensions at runtime. And how do i consider the margin paramters while drawing these customViews. Any help would be greatly appreciated.
Thanks in advance
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:lib="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.ind.Custom_Attribute.LibView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=""
lib:xattr="Custom attribute APPLIED!"
/>
</LinearLayout>
<resources>
<declare-styleable name="CustomAttrs">
<attr name="xattr" format="string" />
</declare-styleable>
</resources>
in values/attrs.xml
I facing problem to draw rectangle at subclass of my android custom view class. Each time super class onDraw method works.But subclass onDraw method never executed. Super class will draw a rectangle and subclass will draw 4 rectangle within the super-class drawn rectangle.I can't fixed this problem.please help me.
Here is my sample code.
SuperClass:
public class ColorFanView extends View{
public ShapeDrawable[] mDrawables;
public ColorFanView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ColorFanView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ColorFanView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvasObject) {
super.onDraw(canvasObject);
int x = 100;
int y = 100;
int width = 80;
int height = 200;
Paint thePaint = new Paint();
thePaint.setColor(Color.WHITE);
RectF rectnagle1 = new RectF(x,y,x+width,y+height);
canvasObject.drawRoundRect(rectnagle1, 10.0f, 10.0f, thePaint);
}
}
Subclass:
public class ColorFanStack extends ColorFanView{
public ColorFanStack(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
public ColorFanStack(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public ColorFanStack(Context context) {
super(context);
initView();
}
public void initView() {
mDrawables = new ShapeDrawable[4];
float[] outerR1 = new float[] { 12, 12, 12, 12, 0, 0, 0, 0 };
mDrawables[0] = new ShapeDrawable(new RoundRectShape(outerR1, null, null));
mDrawables[0].getPaint().setColor(Color.RED);
mDrawables[1] = new ShapeDrawable(new RectShape());
mDrawables[1].getPaint().setColor(Color.WHITE);
mDrawables[2] = new ShapeDrawable(new RectShape());
mDrawables[2].getPaint().setColor(Color.BLUE);
mDrawables[3] = new ShapeDrawable(new RectShape());
mDrawables[3].getPaint().setColor(Color.YELLOW);
}
#Override
protected void onDraw(Canvas canvasObj) {
super.onDraw(canvasObj);
int x = 100;
int y = 100;
int width = 80;
int height = 40;
int canvasSpace =5;
for (Drawable dr : mDrawables) {
dr.setBounds(x, y, x + width, y + height);
dr.draw(canvasObj);
y += height + canvasSpace;
}
}
}
XML
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myViewGroup" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<com.test.colorfan.ColorFanView
android:layout_width="200dip" android:layout_height="400dip"
android:id="#+id/firstView" />
</RelativeLayout>
Please help me regarding this issue. Hopefully, I will get a reply soon.
My guess is that your layout (please edit the question to include your layout), is defining your ColorFanView instances in such a way that they have 0 height or width; therefore, the parent View does not draw them.
EDIT 7/27/2011: Habibur Rahman added his layout XML to the question. This is the new answer:
Your two classes work, but you added the wrong one to your layout (you should have used ColorFanStack instead of ColorFanView). An instance of ColorFanStack will inherit the drawing of ColorFanView (by virtue of the fact that your ColorFanStack.onDraw() method calls super.onDraw()). I think that that was the behavior that you were trying to achieve.
Here is the XML that I used with the classes as you defined them:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.habecker.demo.ColorFanStack
android:layout_width="200dip" android:layout_height="400dip"
android:id="#+id/secondView" />
</RelativeLayout>