65.9K
CodeProject 正在变化。 阅读更多。
Home

Windows Mobile 上的 iPhone UI

2008 年 12 月 11 日

CPOL

7分钟阅读

viewsIcon

356899

downloadIcon

4983

这是一个支持透明效果的界面。作为示例,我使用了一个类似于 iPhone 的界面。在本教程中,我将解释在 Windows Mobile 上处理透明效果的简单方法。

目录

什么是 iPhoneUI?

这是一个支持透明效果的界面。作为示例,我使用了一个类似于 iPhone 的界面。
在本教程中,我将解释在 Windows Mobile 上处理透明效果和动画的简单方法。

引言

在整篇文章中,我一直在提及 Alpha 混合。Alpha 混合是两种颜色的一种凸组合,可以实现透明效果。
为了引起读者对此文章的更多兴趣,我添加了一个动画(状态栏)、事件通知(电池、GPS 信号、定时器)、手指触摸对象移动(解锁按钮)以及关于按钮视图及其与移动原生程序的相应交互的第二部分。
作为一个项目基础,我使用了 Alex Yakhnin 的一个示例,该示例当然涉及在 C# 中使用 API AlphaBlend。

IPhoneUI/Info.png 信息:如果您对以下内容感兴趣:支持分辨率和方向感应、动态图形文本大小调整,我向您推荐我的新文章:iPod touch UI[^]。

背景

正如前面提到的,这个程序展示了如何在 C# 中显示具有透明或半透明像素的位图。

Using the Code

解决方案

我选择了 C#/.NET 来简化操作,并将使用 Visual Studio 2005 作为 IDE。
如果您对 C# 或 C++(Windows GDI)有一定了解,阅读本文将更快、更有用。

要调试该项目,您需要Windows Mobile 6 Professional and Standard Software Development Kits Refresh[^]。项目包含:

  • 两个窗体 Home.csMainMenu.cs
  • 用于管理 P/Invoke 平台 API 的类
  • 用于图像的类 ImageButton
  • 用于移动图像的类 SlideButton
  • 用于拦截触摸按钮的类 InterceptButtonPressed
  • 用于执行程序的类 programsProcessExecute
  • 以及大量的 BMP 文件

最初,我只发布了一个窗体,但随后我将其拆分为两个以使内容更清晰,并从 MainMenu.cs 中删除了顶栏。
这是一个包含 4 种不同壁纸的示例,所有壁纸都包含在解决方案中。

窗体全屏显示(WindowState = Maximize),以获得最佳图像效果,我重写了 OnPaint()

Home

为了绘制背景,您需要先清除屏幕,然后(按正确的顺序)放置背景,最后放置透明对象。最后,您可以添加需要交互的元素。

离屏绘制

为了更快地绘制,我提前在屏幕外(内存中)绘制了所有控件,最后将它们显示到屏幕上。

protected override void OnPaint(PaintEventArgs e)
{
    gxBuffer = Graphics.FromImage(offBitmap);
    //Graphics gxBuffer = e.Graphics;
    gxBuffer.Clear(this.BackColor);
    ...
    ...
    this.Invalidate();
    e.Graphics.DrawImage(offBitmap, 0, 0);
}

绘制透明效果

为了绘制 Alpha,我使用了 DrawAlpha,它通过 P/Invoke 调用 AlphaBlend

public class PlatformAPIs
{
    [DllImport("coredll.dll")]
    extern public static Int32 AlphaBlend(
           IntPtr hdcDest, Int32 xDest, Int32yDest, Int32 cxDest, Int32 cyDest,
           IntPtr hdcSrc, Int32 xSrc, Int32 ySrc, Int32 cxSrc, Int32 cySrc, 
           BlendFunction blendFunction);                 
} 
private void DrawAlpha(Graphics gx, Bitmap image, byte transp, int x, int y)
 {
     using (Graphics gxSrc = Graphics.FromImage(image))
    {
        IntPtr hdcDst = gx.GetHdc();
        IntPtr hdcSrc = gxSrc.GetHdc();
        BlendFunction blendFunction = new BlendFunction();
        blendFunction.BlendOp = (byte)BlendOperation.AC_SRC_OVER;   
        blendFunction.BlendFlags = (byte)BlendFlags.Zero;           
        blendFunction.SourceConstantAlpha = transp;
        blendFunction.AlphaFormat = (byte)0;                        
        PlatformAPIs.AlphaBlend(hdcDst, x, y, image.Width, image.Height, hdcSrc,
                                0, 0, image.Width, image.Height, blendFunction);
        gx.ReleaseHdc(hdcDst);    
        gxSrc.ReleaseHdc(hdcSrc); 
    }
}

初始化图像

我在类构造函数中的 OnPaint() 中加载了窗体的所有图像。首先,我定位了不需要交互的图像,最后添加了需要交互的按钮。
构造函数示例

    path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
    backImage = new Bitmap(path + @"\BMP\wallpaper.bmp");
    topBar = new Bitmap(path + @"\BMP\topbar.bmp");
    topLock = new Bitmap(path + @"\BMP\toplock.bmp");
    ...

绘制所有图像

绘制示例(OnPaint

    DrawAlpha(gxBuffer, signal, 200, 0, 0);
    DrawAlpha(gxBuffer, topLock, 200, signal.Width, 0);
    DrawAlpha(gxBuffer, GetBatteryImage(), 200, topLock.Width + signal.Width, 0);
    ...

电池电量

屏幕不是static的。当操作系统更改此注册表项时,电池图像会发生变化。为此,我使用了通知代理并将图像拆分成多个部分,并添加了一个更全面的步骤。

我还添加了一个方法,用于决定显示哪个图像(当然,这在 OnPaint() 中使用)。

private Bitmap GetBatteryImage()
{            
    switch (SystemState.PowerBatteryStrength)
    {
        case BatteryLevel.VeryHigh:
            return batteryLevelVeryHigh;                    
        case BatteryLevel.High:
            return batteryLevelHigh;                    
        case BatteryLevel.Medium:
            return batteryLevelMedium;
        case BatteryLevel.Low:
            return batteryLevelLow;
        case BatteryLevel.VeryLow:
            _HideBattery = !_HideBattery;
            if (_HideBattery)
                return batteryLevelVeryLow1;
            else
                return batteryLevelVeryLow2;                    
                    
    }
    return null;     
}

电池动画示例

GSM信号强度

GPS 信号也一样。

private Bitmap GetGPSSignalImage()
{
    int SignalStrength = SystemState.PhoneSignalStrength;

    if (SignalStrength > 80)
        return signalLevelVeryHigh;

    if (SignalStrength > 60)
        return signalLevelHigh;

    if (SignalStrength > 50)
        return signalLevelMedium;

    if (SignalStrength > 20)
        return signalLevelLow;

    //else
    {
        _HideSignal = !_HideSignal;
            if (_HideSignal)
                return signalLevelVerylow1;
            else
                return signalLevelVerylow2;
    }
    return null;
}

信号动画示例

信息:WM 6 模拟器必须通过Cellular Emulator连接到Fake Network。下面我添加了一些可能有用的步骤。

运行蜂窝网络模拟器

  1. 启动蜂窝网络模拟器(开始/所有程序 / Windows Mobile 6 SDK / 工具 / Cellular Emulator)和设备模拟器。
  2. 从蜂窝网络模拟器主窗口的状态栏读取 COM 端口配置。
  3. 在设备模拟器中,转到文件 / 配置,然后选择外围设备选项卡。
  4. 将设备模拟器的串行端口 0 映射到从步骤 2 获得的特定 COM 号。
  5. 对设备模拟器进行软重置(文件 / 重置 / )。

绘制动画滑块

在窗体的末尾,我添加了另一个动画,但图像不会随系统情况而改变。
由于这是一个动画,我选择将所有内容加载到一个图像中,并添加了显示它所需的全部帧。

为了创建动画,我移动了查看区域。换句话说,只显示当前所需的帧。

绘制时间

我添加了一个计时器,它会定期检查时间并在显示器上更新。

private void DrawDateAndTime(string time, Graphics gx, int y)
{
    SizeF sizeTime = gx.MeasureString(time, timeFont);
    int xTime = this.Width / 2 - (int)sizeTime.Width / 2;
    gx.DrawString(time, timeFont, new SolidBrush(Color.White), xTime, y);

    SizeF sizeDate = gxBuffer.MeasureString(date, dateFont);
    int xDate = this.Width / 2 - (int)sizeDate.Width / 2;

    gxBuffer.DrawString(date, dateFont, whiteBrush, xDate, 70);
}

这是带有 Windows 时钟的视图。

绘制按钮

我的所有按钮都不需要透明效果,我为此创建了 ImageButton.cs 类。
ImageButton 有两个位图 imageimageDown,它重写了 MouseDownMouseUp,并在 Paint() 中自行绘制。

public void Paint(Graphics gx)
{
    ImageAttributes attrib = new ImageAttributes();
    Color color = GetTransparentColor(image);
    attrib.SetColorKey(color, color);
    if (!pushed || imageDown == null)
        gx.DrawImage(image, clientArea, 0, 0, clientArea.Width, clientArea.Height, 
                     GraphicsUnit.Pixel, attrib);                
    else
        gx.DrawImage(imageDown, clientArea, 0, 0, clientArea.Width, clientArea.Height, 
                     GraphicsUnit.Pixel, attrib);
}

透明颜色基于图像的第一个像素设置。

private Color GetTransparentColor(Bitmap image)
{
    return image.GetPixel(0, 0);
}

这是按钮图像的示例。

移动按钮

为了移动箭头按钮,我使用了 SlideButton,它继承了前面的按钮并添加了移动功能。一旦释放鼠标,owner_MouseUp 就会启动计时器,并使用 timeAnimation_Tick。您可以在下方看到代码。

private void timeAnimation_Tick(object sender, EventArgs e)
{
    int x = (start.X - 20);
    Move(x);
}
void Move(int x)
{
    int shift = x - start.X;
    int newX = (this.clientArea.X + shift);// -mousePos;
    if (newX <= leftLimit)
    {
        newX = leftLimit;
        timeAnimation.Enabled = false;
    }
    if (newX + this.clientArea.Width >= rightLimit)
    {
        newX = rightLimit - this.clientArea.Width;
        unLock = true;
    }

    this.clientArea = new Rectangle
                      (newX, clientArea.Y, this.clientArea.Width, this.clientArea.Height);
    //owner.Invalidate(new Rectangle(0, 258, 240, 70));
    start.X = x;
}

MainMenu

主菜单在箭头按钮到达终点时显示。
当按下钥匙按钮时,它会隐藏。<anim2>

绘制按钮

我的所有按钮都不需要透明效果。我为此创建了 ImageButton.cs 类。
ImageButton 有两个位图图像和 imageDown,它重写了 MouseDownMouseUp,并在 Paint 中绘制。
在构造函数中,我加载了所有位图并设置了 Button GridPositionOnPaint 绘制了所有 Button
一个计时器监视哪个按钮被按下,并在必要时自行关闭。

private void timerCheckButtonPressed_Tick(object sender, EventArgs e)
{
    try
    {
         CheckButtonPress();

         IntPtr activeWindowHandle = GetForegroundWindow();
         IntPtr thisWindowHandle = this.Handle;
         if (activeWindowHandle != thisWindowHandle)
         {
             if (DialogResult != DialogResult.OK)
                 NeedRepaint = true;
         }
         else
         {
             if (NeedRepaint)
             {
                 this.Invalidate();
                 NeedRepaint = false;
             }

         }
     }
     catch (Exception ex)
     {
         Close();
     }
 }

CheckButtonPress 检查按钮是否被按下并执行其命令。

private bool CheckButtonPress()
{
    if (buttonCalendar.IsPressedOneTime)
    {
        buttonCalendar.IsPressedOneTime = false;
        ProcessExecute.Calendar();
        return true;
    }
    if (buttonPhoto.IsPressedOneTime)
    {
        buttonPhoto.IsPressedOneTime = false;
        ProcessExecute.Picture();
        return true;
    }  
    if (buttonMail.IsPressedOneTime)
    {
        buttonMail.IsPressedOneTime = false;
        ProcessExecute.Notes();
        return true;
    }
    if (buttonPhone.IsPressedOneTime)
    {
        buttonPhone.IsPressedOneTime = false;
	...
}	

执行程序或链接

为了执行程序,我创建了 ProcessExecute,它直接执行我需要的程序或链接。
这是一个示例

public static void Phone()
{
    keybd_event(VK_TTALK, 0, KEYEVENTF_KEYDOWN, 0);
    //StartProcess(@"\windows\cprog.exe");
} 
public static void MediaPlayer()
{
     StartProcess(@"\windows\WMPlayer.exe");
}

internal static void TaskManager()
{
    StartProcess(@"\Windows\TaskMgr.exe");
} 
public static void Picture()
{
    StartLink(@"\Windows\Start Menu\Programs\Pictures & Videos.lnk"); 
}   
public static void Camera()
{
    //Namespace: Microsoft.WindowsMobile.Forms
    //Assembly: Microsoft.WindowsMobile.Forms (in microsoft.windowsmobile.forms.dll)

    CameraCaptureDialog cameraCapture = new CameraCaptureDialog();
    cameraCapture.ShowDialog();
} 

这是 StartProcess

public static void StartProcess(string FileName)
{
    ...
    System.Diagnostics.Process myProcess = new System.Diagnostics.Process();
    myProcess.StartInfo.FileName = FileName;
    myProcess.Start();
    ...	
}

关于任务管理器信息TaskMgr.exe 仅在 WM 6.1 或更高版本中可用。但是我搜索了它,如果不存在,则不显示该按钮。如下图所示。

IPhoneUI/TaskMgr.gif

链接

关注点

我想提醒您,这个程序不是一个完整的界面。它实际上并不像 iPhone 那样锁定手机,按下日历时也不会显示 iPhone 日历,也不会修改电话主屏幕。
我相信我已经证明了如何简单地使用单个图像或多个图像进行透明度和动画绘制。
我也相信,一个熟练的开发者应该很容易使其适应屏幕,并为其应用程序的窗体之间添加菜单、按钮和动画。

历史

  • 2008 年 12 月 11 日 - 首次发布
  • 2008 年 12 月 13 日 - 解决了关闭外部应用程序后的错误
  • 2009 年 3 月 24 日 - 新的次要版本 v1.1
    • 添加了 CameraCaptureDialog
    • 添加了 TaskManager(仅适用于 WM 6.1 或更高版本)
    • 添加了退出按钮
Windows Mobile 上的 iPhone UI - CodeProject - 代码之家
© . All rights reserved.