自定义进度条小部件

7

我想在Android中实现类似这个的功能。

alt text

在Android中,我可以扩展ProgressBar,但我怀疑如何添加顶部的TextViews。在iPhone上很容易,因为我可以使用绝对位置,但在这里不行。

有什么想法吗?

编辑: 我决定使用SeekBar而不是ProgressBar来添加拇指图形。我在下面发表了评论。需要注意以下几点:

  • 我正在使用硬编码值,实际上有三个,但可能更多或更少。
  • 当移动拇指时,它会移动到50,但它应该移动到不同的选项。
  • 我正在使用像素而不是dpi。我应该解决这个问题。
  • 我需要解决拇指移动时缺乏动画的问题。

我的进展如下:

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

你是否已经创建了与iPhone上的相同进度条? - βhargavḯ
@NewToiOS:是的,很久以前 :) - Macarse
4个回答

8

您已经重写了onDraw方法,为什么不直接自己绘制文本字符串呢?与添加TextView和调整padding的开销相比,只需使用canvas.drawText在正确的位置物理绘制文本字符串即可。

您可以使用Paint对象指定文本的样式和颜色:

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

使用Paint对象的measureText方法可以精确地确定在画布上绘制特定字符串时其宽度:

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

然后,您可以遍历文本字符串数组并在正确的位置绘制每个字符串。
@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);
  }
}

你可能希望在 onMeasure 中进行测量,而不是在 onDraw 中进行。当你更改文本或画笔时,你应该只测量每个字符串的宽度,但我已经将所有内容放在了一个地方,以便更容易理解。


很好,我会用这个文本。唯一遗漏的是如何在移动拇指时进行动画处理。在 iPhone 上,我有类似于 moveToPosition(int position, boolean animate) 的东西,但在 Android 上没有类似的东西。你有什么想法吗? - Macarse
没有简单的方法可以实现您所描述的内容(而且您的onStopTrackingTouch实现中的代码是多余的——SeekBar进度值已经通过跟踪触摸来正确设置)。我猜您想在用户点击当前位置左侧或右侧时动画拇指的位置?您需要重写onTouch处理程序,监听ACTION_UP并实现一个循环,在适当的间隔内更改滑块位置以模拟动画。 - Reto Meier

1

只是一次尝试。您可以将自定义进度条放置在FrameLayout中,然后在此布局内添加三个TextViews,宽度为fill_parent。 然后,您可以按以下方式对齐三个文本:左、中、右。您的文本不应重叠,并且您可以使用边距稍微调整它们的位置。


TextViews的数量是不固定的。图片有3个,但它可以有2个、4个、5个、6个等等。 - Macarse
好的。所以你可以使用wrap_content而不是fill_parent来设置textviews,并使用边距在可用空间中分配它们。我知道这不是完美的解决方案,但否则你必须重写视图的drawmethod并自己完成它。 - MaxFish

0

您可以设置TextView具有:

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

这将让Android框架负责放置TextView并使您免于手动计算每个TextView的填充。


我只能在 LinearLayout 中使用它 :( - Macarse
我已经能够通过在XML中指定布局来使用它,包括EditTexts、TextViews和其他小部件。不过,我不确定你在这里是否要以编程的方式实现它。 - danh32
为什么不在你的FrameLayout中添加一个LinearLayout来容纳你的TextViews,这样你就可以使用权重了呢? - jqpubliq

0

你可以使用this... 不完全是你所寻找的... 但非常容易设置和自定义...


Thorat:在Android中,而不是iPhone。 - Macarse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接