views:

202

answers:

4

I am trying to do something similar to this but in Android.

alt text

In Android I can extend the ProgressBar but I am doubting of how to add the TextViews on top. In iphone it was easy because I can use absolute positions, but not here.

Any ideas?

EDIT: I decided to use SeekBar instead of ProgressBar to add the thumb drawable. I commented below. Some points to notice:

  • I am using hardcoded values, actually three but it can be more or less.
  • When the thumb is moved it moves to 50 but it should move to the different options.
  • I am using pixels instead of dpi. I should fix that.
  • I need to solve the lack of animation when the thumb moves.

My progress so far:

public class SliderFrameLayout extends FrameLayout implements OnSeekBarChangeListener {
    private SeekBar mSlider;
    private Context mContext;
    private int mSize = 3;
    private TextView[] mTextViews;
    private String[] mTexts = {"Nafta", "Gas", "Gasoil"};

    public SliderFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        mContext = context;
        setWillNotDraw(false);
        mTextViews = new TextView[mSize];
        addSlider();
        addTextViews();
    }

    private void addTextViews() {
        for ( int i=0 ; i < mSize ; i++ ) {
            TextView tv;
            tv = new TextView(mContext);
            tv.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
                    LayoutParams.WRAP_CONTENT));
            tv.setText(mTexts[i]);
            mTextViews[i] = tv;
            addView(tv);    
        }
    }

    private void addSlider() {
        FrameLayout fl = new FrameLayout(mContext, null);
        LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
        fl.setLayoutParams(params);
        fl.setPadding(30, 30, 30, 0);


        mSlider = new SeekBar(mContext, null);
        LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
        //lp.setMargins(30, 30, 30, 0);
        //mSlider.setPadding(30, 30, 30, 0);
        mSlider.setLayoutParams(lp);
        //mSlider.setMax(mSize-1);
        mSlider.setThumbOffset(30);
        //mSlider.setProgressDrawable(mContext.getResources().getDrawable(R.drawable.slider_track));
        //mSlider.setThumb(mContext.getResources().getDrawable(R.drawable.slider_thumb));
        mSlider.setOnSeekBarChangeListener(this);
        //addView(mSlider);

        fl.addView(mSlider);
        addView(fl);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);


        Rect rectf = new Rect();
        mSlider.getLocalVisibleRect(rectf);

    Log.d("WIDTH        :",String.valueOf(rectf.width()));
    Log.d("HEIGHT       :",String.valueOf(rectf.height()));
    Log.d("left         :",String.valueOf(rectf.left));
    Log.d("right        :",String.valueOf(rectf.right));
    Log.d("top          :",String.valueOf(rectf.top));
    Log.d("bottom       :",String.valueOf(rectf.bottom));

        int sliderWidth = mSlider.getWidth();

        int padding = sliderWidth / (mSize-1);

        for ( int i=0 ; i < mSize ; i++ ) {
            TextView tv = mTextViews[i];
            tv.setPadding(i* padding, 0, 0, 0);
        }
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress,
            boolean fromUser) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        Log.d("SEEK", "value: " + seekBar.getProgress());
        seekBar.setProgress(50);
    }
}
A: 

You can use this... not exact what you looking for... but really easy to set up and customize...

KiranThorat
@Kiran Thorat: In Android not in iphone.
Macarse
HHmmm.... sorry
KiranThorat
+1  A: 

Just a try. You could put your custom progressBar inside a FrameLayout then, inside this layout, you have to add three TextViews with fill_parent as width. Then you can align the three texts in this way: left, center and right. Your texts shouldn't overwrite and you can adjust a little their position using margins.

MaxFish
@MaxFish: The number of `TextViews` is not fixed. The pic has 3, but it can have 2, 4, 5, 6, etc
Macarse
Ok. So you can use wrap_content instead of fill_parent for the textviews and distribute them in the space available using margins. I know it's not a perfect solution, but otherwise you have to overwrite the drawmethod of the view and do it yourself.
MaxFish
@MaxFish: I will edit my answer with what I have so far.
Macarse
A: 

You can set the TextViews to have:

android:layout_width="0dp"
android:layout_weight="1"

This will let the android framework take care of the placing of the TextViews and save you from manually calculating the padding of each one.

danh32
@danh32: I can only use that inside a `LinearLayout` :(
Macarse
I have been able to use this for EditTexts, TextViews, and other widgets by specifying the layout in XML. Not sure about doing it programmatically as you are here, though.
danh32
Why not just add a `LinearLayout` to your `FrameLayout` to contain your `TextViews` so you can use weighting?
jqpubliq
+4  A: 

You're already overriding onDraw, why not just draw the text strings yourself? Rather than go through the overhead of adding TextViews and messing with the padding, just use canvas.drawText to physically draw the text strings in the right place.

You can specify the style and color of the text using a Paint object:

Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(r.getColor(R.color.text_color));
textPaint.setFakeBoldText(true);
textPaint.setSubpixelText(true);
textPaint.setTextAlign(Align.LEFT);

And get the exact positioning by using the measureText method on that Paint object to find what width a particular string would be when drawn on a canvas:

textWidth = (int)textPaint.measureText(mTexts[i]);

Then you can iterate over your array of text strings and draw each string in the right place.

@Override 
protected void onDraw(Canvas canvas) {
  int myWidth = getMeasuredWidth()-LEFT_PADDING-RIGHT_PADDING;
  int separation = myWidth / (mSize-1);

  for (int i = 0; i++; i < mSize) {
    int textWidth = (int)textPaint.measureText(mTexts[i]);
    canvas.drawText(mTexts[i], 
                    LEFT_PADDING+(i*separation)-(int)(textWidth/2), 
                    TOP_PADDING, 
                    textPaint);
  }
}

You'll probably want to do the measurements in onMeasure instead of onDraw, and you should probably only measure the width of each string when you change the text or the paint, but I've put it all in one place to (hopefully) make it easier to follow.

Reto Meier
@Reto Meier: Cool, I will use that for the text. The only thing I missing is how to animate the thumb when moving it. in iPhone I had something like `moveToPosition(int position, boolean animate)` but there is nothing similar in Android. Any thought on that?
Macarse
There's no simple way to achieve what you're describing (and your code your onStopTrackingTouch implementation is redundant - the Seekbar progress value will already have been set correctly by the touch being tracked). I assume you want to animate the location of the thumb in the case where the user clicks to the left or right of the current position? You'd need to override the onTouch handler, listen for ACTION_UP and implement a loop that changes the slider position at a suitable interval to simulate animation.
Reto Meier