TouchUI - Animated Navigation (part 12)

by Chris 25. December 2009 10:02

nontouchanim Now we come to one of the nicest additions that the TouchUI bring to the table - the animated navigation. This is, as so many other things in modern mobile user experience design, inspired by the way things work on the iPhone.

When the user select an item in a list (in a dialog), the following code is run in the main form:

private void navigateForward()
{
    Common.Instance.PaintCopyGraphics.DrawImage
        (Common.Instance.PaintBitmap, 0, 0);

   
if(dialogStack.Last() is MainDialog)
    {
       
MainDialog mainDialog = (MainDialog)dialogStack.Last();
       
DetailDialog detailDialog = new DetailDialog(Common.Instance.ScreenFactor,
            Common.Instance.ClientRectangle, mainDialog.SelectedLine + 1);
        dialogStack.Add(detailDialog);
    }
    animationOffset =
Common.Instance.ClientRectangle.Width / 4;
    animationLeft =
Common.Instance.ClientRectangle.Width;
}

As you may remember from the painting part of this series, the PaintBitmap (in the app's singleton class Common.Instance) is used to do all the painting in the app. Then, it is used in the OnPaint method to do the actual painting to the screen. The first thing that happen in the code above, is that a copy of the current screen is made (into the PaintCopyBitmap that is pointed to by PaintCopyGraphics). The navigation is done (the details form is added to the dialog stack), and then the animations horizontal start position (animationLeft) is set along with the offset to move the new screen into place for each frame (animationOffset, in this case it's in four steps making the animation take about a third of a second). The navigation backward (up the dialog stack) is done in a similar fashion, but then the animationLeft and animationOffset are both negative. Btw, the beautiful strawberry graphics is from a photo by Tina Phillips / FreeDigitalPhotos.net.

With this in place, the actual painting is done in the OnPaint method, and it looks like this:

protected override void OnPaint(PaintEventArgs e)
{
   
base.OnPaint(e);

    dialogStack.Last().Paint(
Common.Instance.PaintGraphics);
   
Rectangle r = Common.Instance.ClientRectangle;
   
if(animationLeft != 0)
    {
        e.Graphics.DrawImage(
Common.Instance.PaintCopyBitmap,
           
new Rectangle(animationLeft - r.Width * animationLeft.CompareTo(0),
            0, r.Width, r.Height), r, GraphicsUnit.Pixel);
        e.Graphics.DrawImage(
Common.Instance.PaintBitmap,
            new Rectangle(animationLeft, 0, r.Width, r.Height), r,
            GraphicsUnit.Pixel);
        animationLeft -= animationOffset;
       
if(Math.Abs(animationLeft) < Math.Abs(animationOffset))
            animationLeft = 0;
    }
   
else
        e.Graphics.DrawImage(Common.Instance.PaintBitmap, r, r,
            GraphicsUnit.Pixel);
}

First, the dialog is painted (updating the PaintBitmap with the new content), and then both the old and new bitmaps are painted according to the step in the animation (controlled by animationLeft). When the animation is complete (animationLeft is zero), only the new dialog is painted.

A similar approach is used for the switching of images in the detail dialog in the sample, so be sure to check that out as an example of responding to sweep gestures.

Currently rated 5.0 by 4 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | User Interface | Windows Mobile

TouchUI - ScrollPanel (part 11)

by Chris 18. December 2009 10:01

Another controls that inherit from the ScrollControl (described in a previous part of this series) is the ScrollPanel control. As the name implies, this is a panel that implement modern features like soft (kinetics) scrolling, and the design and functionality was inspired by the scroll view control on iPhone.

The ScrollPanel itself does not implement much logic, it mainly functions as a way for the app to implement a scrolling panel. In the TouchUI sample app, that is done in the DetailScrollPanel that inherits from ScrollPanel (and in turn from ScrollControl). The painting of the panel is done like this:

public override void Paint(Graphics g)
{
   
base.Paint(g);

    imageHeight = Rectangle.Width * image.Size.Height / image.Size.Width;
    g.DrawImage(imagenew Rectangle(Rectangle.Left,
        Rectangle.Top + ScrollTop, Rectangle.Width, imageHeight),
        new Rectangle(0, 0, image.Width, image.Height),
        GraphicsUnit.Pixel);

   
string s = "Lorem ipsum dolor sit amet, consectetur adipisicing...";

    g.DrawString(s,
Common.Instance.Font, Common.Instance.FontBrush,
        new RectangleF(Rectangle.Left + 8 * ScreenFactor,
        Rectangle.Top + ScrollTop + imageHeight + 8,
        Rectangle.Width - 16 * ScreenFactor,
        ScrollHeight - imageHeight - 16 * ScreenFactor),
       
new StringFormat());
}

It draws the image at the top, and some Latin text as the bottom, and note that the use of the control's placement and size (Rectangle) will make both the image and the test resize with the screen. Dynamically that useful when switching between portrait and landscape, but it also means that different resolutions on different devices are handled.

This simple setup provides for full controls while still allowing for any use of the panel area. It could be of any height, and it could include any number of items. Although not included in the sample, it could also include traditional WinForms controls to allow input (text and combo boxes).

Currently rated 5.0 by 3 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | User Interface | Windows Mobile

TouchUI - ScrollList (part 10)

by Chris 11. December 2009 10:00

One of the controls that inherit from the ScrollControl (described in the previous part of this series) is the ScrollList control. As the name implies, this is a list that implement modern features like soft (kinetics) scrolling, and the design and functionality was inspired by the table view control on iPhone.

The control holds a generic list of scroll list items (suitably name ScrollListItem), and during the paining of the control, it instructs all the list items to paint themselves. It also takes care of the painting of the background of each item, to enable every other line to have a different background color, and also to highlight the selected item. The painting is efficient in the way that only the items that are (at least partly) visible is painted, and the code looks like this:

public override void Paint(Graphics g)
{
    DoAutomaticMotion();

   
int firstListItem = Math.Max(-(ScrollTop / ListItemHeight), 0);
   
int lastListItem = Math.Min(firstListItem +
        (Rectangle.Height / ListItemHeight) + 2, ListItems.Count);
   
for(int i = firstListItem; i < lastListItem; i++)
    {
       
if(i == SelectedLine)
        {
            ListItems[i].Selected =
true;
            g.FillRectangle(HighBrush,
new Rectangle(0,
                Rectangle.Top + ListItemHeight * i + ScrollTop,
                Rectangle.Width, ListItemHeight));
        }
       
else
        {
            ListItems[i].Selected =
false;
           
if(i % 2 == 1)
                g.FillRectangle(GrayBrush,
new Rectangle(0,
                    Rectangle.Top + ListItemHeight * i + ScrollTop,
                    Rectangle.Width, ListItemHeight));
        }

        ListItems[i].Rectangle =
new Rectangle(0,
            Rectangle.Top + ListItemHeight * i + ScrollTop,
            Rectangle.Width, ListItemHeight);
        ListItems[i].Paint(g);
    }
}

The control also takes care of translating a generic selection in the base class ScrollControl into an selection of an item in the list. The owner of this control, normally a dialog, is notified of each selection through an event (Selected). This is handled for both the finger (mouse) and if a hardware (physical) key on the phone is pressed. This hardware key support means that the control is also functional on a non-touch device.

The implementation of each list item is done by inheriting from ScrollListItem and mainly implement the Paint method. See the sample code for more details.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | User Interface | Windows Mobile

TouchUI - ScrollControls (part 9)

by Chris 4. December 2009 13:26

Now we have come to the heart of this series, as this is where the most important part of the TouchUI framework is implemented. It's the base control class for the controls that allow scrolling, and the implementation was derived in some parts from the excellent SmoothListBox by Fredrik Bornander.

As you will see when you look at the implementation, there are a lot of settings that you can use to modify the exact behavior, and here are some examples:

  • snapBackFactor: how fast should the control snap back when passed the top and bottom borders
  • dragDistanceFactor: factor to increase the actual speed of dragging
  • maxVelocity: maximum scrolling velocity
  • deaccelerationFactor: how fast should the scrolling decelerate when we let go
  • borderDeaccelerationFactor: same as before, but on the top and bottom borders

The action starts when the finger (mouse) is pushed down...

public override void MouseDown(Point p)
{
    mouseDownPoint = p;
    previousPoint = mouseDownPoint;
}

...and position is simply saved. This becomes useful as the finger is dragged...

public override void MouseMove(Point p)
{
    int delta = p.Y - previousPoint.Y;
    draggedDistance = delta;
    scroll(delta);
    previousPoint = p;
}

...the distance dragged is recorded for each movement, and also the control is scrolled accordingly:

private void scroll(int offset)
{
    ScrollTop += offset;
}

When the finger is finally released...

public override void MouseUp(Point p)
{
    velocity = Math.Min(Math.Max(dragDistanceFactor * draggedDistance,
        -maxVelocity), maxVelocity);
    draggedDistance = 0;
    DoAutomaticMotion();
}

...the speed is recorded and the main scrolling logic is called...

public virtual void DoAutomaticMotion()
{
    velocity *= deaccelerationFactor;
   
if(ScrollTop > 0 || ScrollTop + ScrollHeight < Rectangle.Height)
        velocity *= borderDeaccelerationFactor;
   
float deltaDistance = elapsedTime * velocity;
   
if(Math.Abs(deltaDistance) >= 1.0f)
        scroll((
int)deltaDistance);
   
else
    {
       
if(ScrollTop != 0)
        {
           
if(ScrollTop > 0)
                scroll(-
Math.Max(1, (int)(snapBackFactor * (float)(ScrollTop))));
           
else
            {
               
if(ScrollHeight > Rectangle.Height)
                {
                   
int bottomPosition = ScrollTop + ScrollHeight + Rectangle.Top;
                   
if(bottomPosition < Rectangle.Height)
                        scroll(
Math.Max(1, (int)(snapBackFactor *
                            (float)(Rectangle.Height - bottomPosition))));
                }
               
else
                    scroll(Math.Max(1, -((int)(snapBackFactor *
                        (float)ScrollTop))));
            }
        }
    }
}

...and it handles the automatic motion of the control when the finger is released. It also takes care of the handling of the borders, and the automatic negative scroll that occurs then the borders are passed.

Another important thing that happens in the MouseUp method is...

PointSelected = (Math.Abs(p.Y - mouseDownPoint.Y) < 5);
if(Math.Abs(mouseDownPoint.X - p.X) > 50)
    HorizontalGesture = mouseDownPoint.X - p.X;
else
    HorizontalGesture = 0;

...that indicate if something was selected in the control (difference between pressed and released vertical position is less that five pixels), and whether sweep gesture was made (difference between pressed and released horizontal position greater than fifty pixels).

Currently rated 5.0 by 4 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Chris | Compact Framework | User Interface | Windows Mobile

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen