Thursday, May 27, 2010

Wheel UI contol: Backgrounds

The NumericWheel widget has a simple design. It contains only text data (described in previous topic) and gradient backgrounds that are described in this post.

Backgrounds
There are used next backgrounds:
  • Main widget background
  • Center (current item) background
  • Top and bottom shadows

Main Background
Main background consists of two parts: the gradient bevel and the gradient background.
It's easy to create that, just find necessary colors. Not that the best way it to combine them by using layers that are described here.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <gradient
                android:startColor="#333"
                android:centerColor="#DDD"
                android:endColor="#333"
                android:angle="90" />
                
                <stroke android:width="1dp" android:color="#FF333333" />
        </shape>
    </item>
    <item android:left="4dp" android:right="4dp" android:top="1dp" android:bottom="1dp">
        <shape android:shape="rectangle">
            <gradient
                android:startColor="#AAA"
                android:centerColor="#FFF"
                android:endColor="#AAA"
                android:angle="90" />
        </shape>
    </item>
</layer-list>

Just set it as a widget background by calling setBackgroundResource().

Center background
Center background is used to mark the current item value and widget label. It is also defined in XML and is a little transparent.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
        android:startColor="#70222222"
        android:centerColor="#70222222"
        android:endColor="#70EEEEEE"
        android:angle="90" />

    <stroke android:width="1dp" android:color="#70333333" /> 
</shape>

Just draw it on center of our widget:
/**
 * Draws rect for current value
 * @param canvas the canvas for drawing
 */
private void drawCenterRect(Canvas canvas) {
    int center = getHeight() / 2;
    int offset = getHeight() / visibleItems / 2;
    centerDrawable.setBounds(0, center - offset, getWidth(), center + offset);
    centerDrawable.draw(canvas);
}


Top and bottom shadows
The main background (see above) does not make widget to look so nice (like the original iPhone widget) because it is not enough dark on top and bottom. So, I decided to draw additional gradient drawable to get shadow effect:
/** Top and bottom shadows colors */
private static final int[] SHADOWS_COLORS = new int[] { 0xFF111111,
        0x00AAAAAA, 0x00AAAAAA };

/**
 * Draws shadows on top and bottom of control
 * @param canvas the canvas for drawing
 */
private void drawShadows(Canvas canvas) {
    if (topShadow == null) {
        topShadow = new GradientDrawable(Orientation.TOP_BOTTOM, SHADOWS_COLORS);
    }
    topShadow.setBounds(0, 0, getWidth(), getHeight() / visibleItems);
    topShadow.draw(canvas);

    if (bottomShadow == null) {
        bottomShadow = new GradientDrawable(Orientation.BOTTOM_TOP, SHADOWS_COLORS);
    }
    bottomShadow.setBounds(0, getHeight() - getHeight() / visibleItems,
                getWidth(), getHeight());
    bottomShadow.draw(canvas);
}


onDraw()
So, all drawable are defined, and we can draw the Wheel control. We need to draw text layouts and all defined above drawables excluding the widget background.
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    if (itemsLayout == null) {
        calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY);
    }

    drawCenterRect(canvas);

    canvas.save();
    // Skip padding space and hide a part of top and bottom items
    canvas.translate(PADDING, -ITEM_OFFSET);
    drawItems(canvas);
    drawValue(canvas);
    canvas.restore();

    drawShadows(canvas);
}

/**
 * Draws value and label layout
 * @param canvas the canvas for drawing
 */
private void drawValue(Canvas canvas) {
    valuePaint.setColor(VALUE_TEXT_COLOR);
    valuePaint.drawableState = getDrawableState();

    Rect bounds = new Rect();
    itemsLayout.getLineBounds(visibleItems / 2, bounds);

    // draw label
    if (labelLayout != null) {
        canvas.save();
        canvas.translate(itemsLayout.getWidth() + LABEL_OFFSET, bounds.top);
        labelLayout.draw(canvas);
        canvas.restore();
    }

    // draw current value
    canvas.save();
    canvas.translate(0, bounds.top);
    valueLayout.draw(canvas);
    canvas.restore();
}

/**
 * Draws items
 * @param canvas the canvas for drawing
 */
private void drawItems(Canvas canvas) {
    itemsPaint.setColor(ITEMS_TEXT_COLOR);
    itemsPaint.drawableState = getDrawableState();
    itemsLayout.draw(canvas);
}
Note that to draw StaticLayout it needs to "move" it to necessary position by using Canvas.translate() method.

Notes
The first part of Wheel UI control implementation has been done.
What is not implemented yet? Of course, rotation - the main behavior of this widget. I'll describe it in next post.

See also
    Introduction to the Wheel control
    The Wheel control layouts
    Scrolling the wheel control
    Ideas for the future

19 comments:

  1. Why hardcoding in onDraw() method? I'm talking about "Center (current item) background".

    ReplyDelete
  2. Don't understand which part of code you mean.
    In any case it's just a draft :)

    ReplyDelete
    Replies
    1. Hi kankan,
      How can I set the caption for the customized wheelview.I didn't find any method to set caption for the wheelview released library.

      Delete
  3. How do I go about using it in my own project in eclipse?

    ReplyDelete
  4. Hi!
    First of all: awesome work!
    The wheel looks really good!

    But I also have problems to implement it into my existing eclipse project :-/

    A short tutorial would be really nice...

    ReplyDelete
  5. For more control over the center rectangle, I modified drawCenterRect to draw 3 shapes instead of 1. This gave me the ability to get that iPhone look with a dark gradient on the bottom a light one on the top half. The sharp separation of the dark and light halves in the middle is a nice look. To get the outline to draw around it, I just created a third shape with a transparent color.


    private void drawCenterRect(Canvas canvas) {
    int center = getHeight() / 2;
    int offset = (int) (getItemHeight() / 2 * 1.2);

    centerDrawableTop.setBounds(0, center - offset, getWidth(), center);
    centerDrawableTop.draw(canvas);
    centerDrawableBottom.setBounds(0, center, getWidth(), center + offset);
    centerDrawableBottom.draw(canvas);
    centerDrawableOutline.setBounds(0, center - offset, getWidth(), center + offset);
    centerDrawableOutline.draw(canvas);
    }

    ReplyDelete
  6. Hello, first of all. Great job.

    Does anyone knows how can I modify the wheel component to create horizontal wheel? I mean, I want to change the direction of the scroll to horizontal and "Center (current item) background" to vertical.

    thanks.

    ReplyDelete
  7. The easy way: use the vertical wheel, but rotate the canvas before drawing. In this case it needs also to change the onTouch and swap x and y touch coordinates for scrolling.

    ReplyDelete
  8. Maidul said...

    Hi kankan How can i increase the size of month value.Suppose You set month view 15 dip.I want increase 25 dip.Can you please help me

    ReplyDelete
  9. Hi Kankan,

    How can we draw the kankanWheel widget such that it starts showing its adapter elements from top rather than center?
    From what i understood, in drawItems of kankanWheel widget, canvas is being translated to shift to center of the view.
    I wanted to use it as listview and hence the requirement.

    Any pointers will be really helpful.

    Thankyou.

    ReplyDelete
  10. I'm having trouble getting the widget to work. Do you have a usage example?

    ReplyDelete
  11. Is it possible to set the style on the wheel items? Specifically i want to be able to set the font size. My items are being truncated and need to be smaller.

    ReplyDelete
  12. Hi
    Is it possible to set the style on the wheel items? Specifically i want to be able to set the font size. My items are being truncated and need to be smaller.
    I want 12sp size of font in wheel view...

    ReplyDelete
  13. Hi
    I want wheel items run forever no time. How to custom it. Please help me.

    ReplyDelete
  14. How to modify to select only color from an array of color selections?

    ReplyDelete
  15. Nice article. I think it is useful and unique article. I love this kind of article and this kind of blog. I have enjoyed it very much. Thanks for your website.
    TLR Carbon wheels

    ReplyDelete
  16. Craig Willows-Keetley shares the story of his journey to compete in the half Ironman length Challenge event in Phuket. Craig shares his training program, details of the race and lessons he learnt from the event. Hear his story about the Challenge event that’s a Challenge and a half!.
    Carbon bike wheels

    ReplyDelete
  17. Is there a source address? i find the android-wheel project on github . but i don't find drawValue() emthod.

    ReplyDelete
  18. Hi Kankan,

    I am using it as a date picker and on sat and sun i want to change main back ground color. I tried every possibilities but could not get any clue. Can you please help me on this?

    ReplyDelete