Custom ProgressBar widget

2019-03-10 08:22发布

问题:

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

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);
    }
}

回答1:

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.



回答2:

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.



回答3:

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



回答4:

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.