2013-08-26 85 views
2

我有SeekBar的自定義樣式,我想添加標籤。該SeekBar有進步繪製看起來像:自定義搜索欄上的Android標籤

<?xml version="1.0" encoding="utf-8"?> 
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > 
    <item 
     android:id="@android:id/background" 
     android:drawable="@drawable/seekbar_background"/> 
</layer-list> 

seekbar_background 9補丁看起來是這樣的: enter image description here

我想有過杆的兩端各有一個標籤。優選地,標籤將文本在每側的燈泡上居中。看起來我應該能夠在SeekBar的端點上集中TextView,但是我找不到一種方法將一個視圖的中心與另一個視圖的邊緣對齊。

有沒有辦法,或者使用SeekBar API,或者通過巧妙地使用佈局對齊來實現這種情況?

+0

你試圖延長SeekBar獲得需要的效果? – sandrstar

+0

你會建議如何擴展@sandrstar?我不知道如何才能讓標籤正確排列。 – karl

+0

只需在onDraw中添加文字的圖形(您將擁有所有位置信息)。你想在兩端顯示兩個標籤(例如「開始」和「結束」)嗎? – sandrstar

回答

5

我認爲自定義View/ViewGroup的創建可能會訣竅,下面是我相信它可以完成的一些例子。

的main.xml:

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:custom="http://schemas.android.com/apk/res/com.example.TestApp" 
    android:layout_height="match_parent" 
    android:layout_width="match_parent" 
    android:id="@+id/root"> 

    <com.example.TestApp.LabelledSeekBar 
     android:layout_width="300dp" 
     android:layout_height="wrap_content" 
     android:layout_centerInParent="true" 
     custom:labelLeft="@string/left_label" 
     custom:labelRight="@string/right_label" 
     custom:labelPaddingBottom="@dimen/label_padding_bottom" 
     custom:labelCenterSidePadding="@dimen/label_padding_side" 
     android:textSize="@dimen/label_text_size" 
     android:textColor="@android:color/white" 
     android:textStyle="italic" 
     android:progressDrawable="@drawable/seek_progress" /> 

</RelativeLayout> 

attrs.xml帶有自定義屬性:

<resources> 
    <declare-styleable name="LabelledSeekBar"> 
     <attr name="labelLeft" format="string"/> 
     <attr name="labelRight" format="string"/> 
     <attr name="labelPaddingBottom" format="dimension"/> 
     <!-- This is bulb center padding --> 
     <attr name="labelCenterSidePadding" format="dimension"/> 
    </declare-styleable> 
</resources> 

自定義視圖/ ViewGroup中的核心思想是隻提供文本的正確擺放,並做正確的測量風景。自定義的ViewGroup本身(很多代碼如下):

public class LabelledSeekBar extends ViewGroup { 

    /** SeekBar itself */ 
    private final SeekBar mSeekBar; 
    /** Label for left end */ 
    private String mLeftLabel = null; 
    /** Label for right end */ 
    private String mRightLabel = null; 
    /** Bottom paddings for labels */ 
    private static final int DEFAULT_LABEL_PADDING_BOTTOM = 10; // px 
    private int mLabelPaddingBottom = DEFAULT_LABEL_PADDING_BOTTOM; 
    /** Center of 'bulbs' to draw labels above centered */ 
    private static final int DEFAULT_LABEL_PADDING_SIDE = 10; // px 
    private int mLabelCenterPadding = DEFAULT_LABEL_PADDING_SIDE; 
    /** Here goes labels attributes, they are similar to TextViews ones */ 
    private static final int DEFAULT_TEXT_SIZE = 10; // px 
    private static final int DEFAULT_TEXT_COLOR = Color.BLACK; // px 
    private static final Typeface DEFAULT_TEXT_STYLE = Typeface.DEFAULT; // px 
    private int mTextSize = DEFAULT_TEXT_SIZE; 
    private int mTextColor = DEFAULT_TEXT_COLOR; 
    private Typeface mTextStyle = DEFAULT_TEXT_STYLE; 
    /** Bounds for labels rects */ 
    private Rect mLeftTextBound = null; 
    private Rect mRightTextBound = null; 
    /** Rect for SeekBar */ 
    private Rect mSeekBarRect = null; 
    /** Default height for SeekBar */ 
    private int mDefaultSeekBarHeight = 0; 
    /** Paint for text */ 
    private Paint mTextPaint = null; 
    /** Flag to draw or not the labels */ 
    private boolean mDrawLabels = false; 

    /** 
    * Constructor 
    */ 
    public LabelledSeekBar(final Context context) { 
     super(context); 
     mSeekBar = new SeekBar(context); 
     init(null); 
    } 

    /** 
    * Constructor 
    */ 
    public LabelledSeekBar(final Context context, final AttributeSet attrs) { 
     super(context, attrs); 
     mSeekBar = new SeekBar(context, attrs); 
     init(attrs); 
    } 

    /** 
    * Constructor 
    */ 
    public LabelledSeekBar(final Context context, final AttributeSet attrs, final int defStyle) { 
     super(context, attrs, defStyle); 
     mSeekBar = new SeekBar(context, attrs, defStyle); 
     init(attrs); 
    } 

    @Override 
    protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) { 
     mSeekBar.layout(mSeekBarRect.left, mSeekBarRect.top, mSeekBarRect.right, mSeekBarRect.bottom); 
    } 

    /** 
    * 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.LabelledSeekBar, 0, 0); 

     mDefaultSeekBarHeight = getResources().getDimensionPixelSize(R.dimen.default_seekbar_height); 

     mLeftLabel = attrsArray.getString(R.styleable.LabelledSeekBar_labelLeft); 
     mRightLabel = attrsArray.getString(R.styleable.LabelledSeekBar_labelRight); 
     mLabelPaddingBottom = attrsArray.getDimensionPixelOffset(R.styleable.LabelledSeekBar_labelPaddingBottom, DEFAULT_LABEL_PADDING_BOTTOM); 
     mLabelCenterPadding = attrsArray.getDimensionPixelOffset(R.styleable.LabelledSeekBar_labelCenterSidePadding, DEFAULT_LABEL_PADDING_SIDE); 

     // Now get needed Text attributes 
     final int textSizeResource = attributeSet.getAttributeResourceValue("http://schemas.android.com/apk/res/android", "textSize", 0); 

     if (0 != textSizeResource) { 
      mTextSize = getResources().getDimensionPixelSize(textSizeResource); 
     } 

     final int textColorResource = attributeSet.getAttributeResourceValue("http://schemas.android.com/apk/res/android", "textColor", 0); 

     if (0 != textColorResource) { 
      mTextColor = getResources().getColor(textColorResource); 
     } 

     final int typeface = attributeSet.getAttributeIntValue("http://schemas.android.com/apk/res/android", "textStyle", 0); 

     switch (typeface) { 
      // normale 
      case 0: 
       mTextStyle = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL); 
       break; 
      // bold 
      case 1: 
       mTextStyle = Typeface.create(Typeface.DEFAULT, Typeface.BOLD); 
       break; 
      // italic 
      case 2: 
       mTextStyle = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC); 
       break; 
      // bold | italic 
      case 3: 
       mTextStyle = Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC); 
       break; 
     } 

     mTextPaint = new TextPaint(); 
     mTextPaint.setColor(mTextColor); 
     mTextPaint.setTextSize(mTextSize); 
     mTextPaint.setTypeface(mTextStyle); 
     mTextPaint.setTextAlign(Paint.Align.LEFT); 
     mTextPaint.setStyle(Paint.Style.FILL); 

     addView(mSeekBar); 
    } 

    /** 
    * Setters for labels 
    * 
    * @param leftLabel {@link String} 
    * @param rightLabel {@link String} 
    */ 
    public void setLabels(final String leftLabel, final String rightLabel) { 
     mLeftLabel = leftLabel; 
     mRightLabel = rightLabel; 

     requestLayout(); 
    } 

    @Override 
    protected synchronized void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 
     // measure labels height - this logic is not very strict and can be changed 
     mLeftTextBound = new Rect(); 
     mTextPaint.getTextBounds(mLeftLabel, 0, mLeftLabel.length(), mLeftTextBound); 
     mRightTextBound = new Rect(); 
     mTextPaint.getTextBounds(mRightLabel, 0, mRightLabel.length(), mRightTextBound); 

     final int labelHeight = Math.max(mLeftTextBound.height(), mRightTextBound.height()); 
     final int desiredMinHeight = labelHeight + mLabelPaddingBottom; 
     final int desiredMinWidth = mLeftTextBound.width() + mRightTextBound.width(); 
     final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 
     final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
     final int heightSize = MeasureSpec.getSize(heightMeasureSpec); 
     final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
     int measuredWidth = 0; 
     int measuredHeight = 0; 

     mSeekBarRect = new Rect(); 

     // Calculate width 
     switch (widthMode) { 
      case MeasureSpec.EXACTLY: 
      case MeasureSpec.AT_MOST: 
       if (widthSize < desiredMinWidth) { 
        mDrawLabels = false; 
       } else { 
        mDrawLabels = true; 
        mSeekBarRect.set(mLeftTextBound.width()/2 - mLabelCenterPadding, desiredMinHeight, 
          widthSize - mRightTextBound.width()/2 + mLabelCenterPadding, heightSize); 
       } 
       measuredWidth = widthSize; 
       break; 

      case MeasureSpec.UNSPECIFIED: 
       mDrawLabels = true; 
       measuredWidth = desiredMinWidth + mLabelCenterPadding * 4; 
       mSeekBarRect.set(mLeftTextBound.width()/2 - mLabelCenterPadding, desiredMinHeight, 
         widthSize - mRightTextBound.width()/2 + mLabelCenterPadding, heightSize); 
       break; 
     } 

     if (mDrawLabels) { 
      // Calculate height 
      switch (heightMode) { 
       case MeasureSpec.EXACTLY: 
       case MeasureSpec.AT_MOST: 
        if (heightSize < desiredMinHeight) { 
         mDrawLabels = false; 
        } else { 
         mDrawLabels = true; 
         mSeekBarRect.top = desiredMinHeight; 
         mSeekBarRect.bottom = heightSize > mDefaultSeekBarHeight ? (desiredMinHeight + mDefaultSeekBarHeight) : heightSize; 
        } 
        measuredHeight = (heightSize > (desiredMinHeight + mDefaultSeekBarHeight)) ? (desiredMinHeight + mDefaultSeekBarHeight) : heightSize; 
        break; 

       case MeasureSpec.UNSPECIFIED: 
        mDrawLabels = true; 
        measuredHeight = desiredMinHeight + mDefaultSeekBarHeight; 
        mSeekBarRect.top = desiredMinHeight; 
        mSeekBarRect.bottom = measuredHeight; 
        break; 
      } 
     } else { 
      switch (heightMode) { 
       case MeasureSpec.EXACTLY: 
       case MeasureSpec.AT_MOST: 
        measuredHeight = heightSize; 
        break; 

       case MeasureSpec.UNSPECIFIED: 
        measuredHeight = mDefaultSeekBarHeight; 
        break; 
      } 
     } 

     if (!mDrawLabels) { 
      // define SeekBar rect 
      mSeekBarRect.set(0, 0, measuredWidth, measuredHeight); 
     } 

     mSeekBar.measure(MeasureSpec.makeMeasureSpec(mSeekBarRect.width(), MeasureSpec.EXACTLY), 
       MeasureSpec.makeMeasureSpec(mSeekBarRect.height(), MeasureSpec.EXACTLY)); 

     setMeasuredDimension(measuredWidth, measuredHeight); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    protected void dispatchDraw(final Canvas canvas) { 
     if (mDrawLabels) { 
      final int height = Math.max(mLeftTextBound.height(), mRightTextBound.height()); 

      canvas.drawText(mLeftLabel, 0, height, mTextPaint); 
      canvas.drawText(mRightLabel, getMeasuredWidth() - mRightTextBound.width(), height, mTextPaint); 
     } 

     super.dispatchDraw(canvas); 
    } 

    /** 
    * Any layout manager that doesn't scroll will want this. 
    */ 
    @Override 
    public boolean shouldDelayChildPressedState() { 
     return false; 
    } 
} 

結果如下所示(請注意不同的文本,但兩者都幾乎上方燈泡居中):

example of view

+0

非常感謝您爲此付出的時間。我最終爲我的標籤添加了少量的填充,因爲它們都很短,而且出來的距離很近。我一定會放棄這一點,看看它是如何工作的。 – karl