Windows mobile 开发入门—开发绚丽滑动效果
在这里制作一个9宫格的小程序,如果超过9个,那么就自动翻页。翻页采用划动实现,并且有惯性作用。
原理
mobile手机里滑动效果主要是原理是支持屏幕的触摸,当我们按下、松开时系统分别可以捕捉到相应的坐标位置,然后动态的改变布局,从而达到划动的效果。
图型button
由于mobile还没有支持图片button,所以我们做出一个辅佐类,当按下、弹开时分别使用钢笔绘制不同的图片到屏幕。
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
namespace iPhoneUI
{
public class ImageButton
{
internal Rectangle clientArea;
internal Form owner;
internal Bitmap image;
internal Bitmap imageDown;
internal bool pushed = false;
internal Point location;
internal Point start;
internal int h=30,w=30;
internal bool Enable = false;
private double pressTime = Environment.TickCount;
internal bool pushedOneTime = false;
public string Name {get;set;}
public bool IsPressedOneTime
{
get { return pushedOneTime; }
set { pushedOneTime = value; }
}
public event System.EventHandler Click;
public event System.EventHandler DoubleClick;
public ImageButton(Form owner,int w,int h)
{
this.w = w;
this.h = h;
this.owner = owner;
Attach(owner);
}
public ImageButton(Form owner,Point location)
{
this.owner = owner;
Attach(owner);
this.location = location;
this.clientArea = new Rectangle(location.X, location.Y, h, w);
}
public Point Location
{
set
{
this.location = value;
this.clientArea = new Rectangle(location.X, location.Y, w, h);
}
get { return location; }
}
public Bitmap Image
{
get { return image;}
set
{
image = value;
if (image != null)
{
clientArea = new Rectangle(location.X, location.Y, image.Width, image.Height);
}
}
}
public Bitmap ImageDown
{
get { return imageDown;}
set { imageDown = value; }
}
public bool HitTest(int x, int y)
{
return clientArea.Contains(x, y);
}
private void Attach(Form owner)
{
owner.MouseDown += new MouseEventHandler(owner_MouseDown);
owner.MouseUp += new MouseEventHandler(owner_MouseUp);
owner.DoubleClick += new EventHandler(owner_DoubleClick);
}
void owner_DoubleClick(object sender, EventArgs e)
{
}
public virtual void owner_MouseUp(object sender, MouseEventArgs e)
{
if (!Enable)
return;
if (pushed)
{
using (Graphics gx = owner.CreateGraphics())
{
this.pushed = false;
this.Paint(gx);
owner.Invalidate();
}
pushed = false;
if ((Environment.TickCount - pressTime) < 100)
{
if (Click != null)
Click(this, e);
}
}
// SendMessage(ButtonCode, "Button Pressed");
}
public virtual void owner_MouseDown(object sender, MouseEventArgs e)
{
if (!Enable)
return;
if (this.HitTest(e.X, e.Y))
{
using (Graphics gx = owner.CreateGraphics())
{
this.pushed = true;
IsPressedOneTime = true;
this.Paint(gx);
if ((Environment.TickCount - pressTime) < 300)
{
if (DoubleClick != null)
DoubleClick(this, e);
}
pressTime = Environment.TickCount;
}
start = new Point(e.X, e.Y);
}
}
public void Paint(Graphics gx)
{
if (!Enable)
return;
//gx.Clear(Color.White);
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);
}
Brush b = new SolidBrush(Color.Black);
int txtX=clientArea.Location.X;
if(Name.Length<5)//右移5个PIX
txtX+=5;
gx.DrawString(this.Name, owner.Font, b, txtX, clientArea.Bottom);
}
internal Color GetTransparentColor(Bitmap image)
{
return image.GetPixel(0, 0);
}
}
}
当mouse按下时,判断是否在图像所在的区域内,就执行按下时绘制图像。并且判断两次按下时间小于300毫秒,就认为是双击,发出注册双击事件。当mouse弹起时,恢复按前的图像,并且如果按下时间间隔不超过100毫秒,认为是单击。
初使化按扭
1<?xml version="1.0" standalone="yes"?>
2<DocumentElement>
3 <menu>
4 <Name>测试1</Name>
5
6 <path>Chat 46x46.bmp</path>
7 <pressPath>Chat 46x46.bmp</pressPath>
8 </menu>
9 <menu>
10 <Name>测试1a</Name>
11
12 <path>Chat 46x46.bmp</path>
13 <pressPath>Chat 46x46.bmp</pressPath>
14 </menu>
15 <menu>
16 <Name>测试1b</Name>
17
18 <path>lock 46x46.bmp</path>
19 <pressPath>lock 46x46_pressed.bmp</pressPath>
20 </menu>
21 <menu>
22 <Name>测试1c</Name>
23
24 <path>Internet Explorer 46x46.bmp</path>
25 <pressPath>Internet Explorer 46x46_pressed.bmp</pressPath>
26 </menu>
27 <menu>
28 <Name>测试1d</Name>
29
30 <path>Close 46x46.bmp</path>
31 <pressPath>Close 46x46_pressed.bmp</pressPath>
32 </menu>
33 <menu>
34 <Name>测试1e</Name>
35
36 <path>Chat 46x46.bmp</path>
37 <pressPath>Chat 46x46_pressed.bmp</pressPath>
38 </menu>
39 <menu>
40 <Name>测试1f</Name>
41
42 <path>Camera 46x46.bmp</path>
43 <pressPath>Camera 46x46_pressed.bmp</pressPath>
44 </menu>
45 <menu>
46 <Name>测试1g</Name>
47
48 <path>Camera 46x46.bmp</path>
49 <pressPath>Camera 46x46_pressed.bmp</pressPath>
50 </menu>
51 <menu>
52 <Name>
53 测试1h
54 </Name>
55
56 <path>Camera 46x46.bmp</path>
57 <pressPath>Camera 46x46_pressed.bmp</pressPath>
58 </menu>
59 <menu>
60 <Name>
61 测试1i
62 </Name>
63 >
64 <path>Camera 46x46.bmp</path>
65 <pressPath>Camera 46x46_pressed.bmp</pressPath>
66 </menu>
67 <menu>
68 <Name>
69 测试1j
70 </Name>
71
72 <path>Camera 46x46.bmp</path>
73 <pressPath>Camera 46x46_pressed.bmp</pressPath>
74 </menu>
75 <menu>
76 <Name>测试1k</Name>
77
78 <path>Camera 46x46.bmp</path>
79 <pressPath>Camera 46x46_pressed.bmp</pressPath>
80 </menu>
81 <menu>
82 <Name>测试1l</Name>
83
84 <path>Camera 46x46.bmp</path>
85 <pressPath>Camera 46x46_pressed.bmp</pressPath>
86 </menu>
87 <menu>
88 <Name>测试1m</Name>
89
90 <path>Camera 46x46.bmp</path>
91 <pressPath>Camera 46x46_pressed.bmp</pressPath>
92 </menu>
93 <menu>
94 <Name>测试1n</Name>
95
96 <path>Camera 46x46.bmp</path>
97 <pressPath>Camera 46x46_pressed.bmp</pressPath>
98 </menu>
99 <menu>
100 <Name>测试1o</Name>
101
102 <path>Camera 46x46.bmp</path>
103 <pressPath>Camera 46x46_pressed.bmp</pressPath>
104 </menu>
105 <menu>
106 <Name>测试1p</Name>
107 <path>Camera 46x46.bmp</path>
108 <pressPath>Camera 46x46_pressed.bmp</pressPath>
109 </menu>
110
111</DocumentElement>
程序中的button,放置在XML中,以方便动态的加载。pat是正常的路径,presspath是按下时显示的图片。
1 private void initMenu()
2 {
3 string path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
4 DataTable dt = new DataTable();
5 DataSet ds = new DataSet();
6
7 ds.ReadXml(path + "\\menu.xml");
8 dt = ds.Tables[0];
9
10 foreach (DataRow dr in dt.Rows)
11 {
12
13 Bitmap b = new Bitmap(path + @"\Images\" + dr["path"].ToString());
14 ImageButton buttonCalendar = new ImageButton(this, b.Width, b.Height);
15 buttonCalendar.Image = b;
16 buttonCalendar.ImageDown = new Bitmap(path + @"\Images\" + dr["pressPath"].ToString().Trim());
17 buttonCalendar.Name = dr["Name"].ToString().Trim() ;
18 buttonCalendar.Click += new EventHandler(buttonCalendar_Click);
19 menuList.Add(buttonCalendar);
20
21 }
22
23 point1 = new Bitmap(path + @"\Images\point.png");
24 point2 = new Bitmap(path + @"\Images\point2.png");
25
26
27 }
在mobile中获取文件的路径与windows下不同, string path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
这样才能找到当前动行的程序路径.
在对XML文件中的初使化,并赋值给图像按钮,并且注册事件
Point是分页时在下方的位置产生一个小圆点,代表页数和当前页。
绘制按扭
1 protected override void OnPaint(PaintEventArgs e)
2 {
3 int startX = 0;
4
5 int h = 8;
6 List<ImageButton> temList = new List<ImageButton>();
7
8 int i = 0;
9
10 foreach (ImageButton item in menuList)
11 {
12 int w = (Width / 3 - item.w) / 2 + startX;
13
14 if (i % 3 == 0)
15 {
16 h = 8;
17 item.Location = new Point(w, h);
18 }
19 if (i % 3 == 1)
20 {
21 h += 85;
22 item.Location = new Point(w, h);
23 }
24 if (i % 3 == 2)
25 {
26 h += 85;
27 item.Location = new Point(w , h);
28
29 startX += Width / 3;
30 }
31
32 item.Enable = true;
33
34 Point p = item.Location;
35 p.X += moveX;
36 // p.Y += moveY;
37 p.X -= this.Width * currentPage;
38 item.Location = p;
39
40 temList.Add(item);
41
42 i++;
43 }
44 gxBuffer = Graphics.FromImage(offBitmap);
45
46 //Graphics gxBuffer = e.Graphics;
47 gxBuffer.Clear(this.BackColor);
48
49
50 foreach (ImageButton item in temList)
51 {
52 item.Paint(gxBuffer);
53 }
54
55 if (menuList.Count > 9)
56 {
57 int pageCount = menuList.Count % 9==0?0:1;
58 pageCount += menuList.Count / 9;
59
60 startX=(this.Width-pageCount*10)/2;
61 int startY=this.Height-10;
62
63 for (int j = 0; j < pageCount; j++)
64 {
65 if(currentPage==j)
66 gxBuffer.DrawImage(point1, startX,startY);
67 else
68 gxBuffer.DrawImage(point2, startX, startY);
69
70 startX += 10;
71 }
72
73 }
74 e.Graphics.DrawImage(offBitmap, 0, 0);
75
76 }
OnPaint 是窗体的方法,它是在调用Invalidate后自动执行。在这里控制每行显示3个,每页显示9个,如果有分页画划分页小圆点。在系统初使化,mouse 按下,弹出时都调用Invalidate方法,放系统重画UI。为了防卡需要override OnPaintBackground,这点我也不是很明白,如果有高手请多指教。
实现划动效果
void Form1_MouseDown(object sender, MouseEventArgs e)
{
x=e.X;
y = e.Y;
}
void Form1_MouseMove(object sender, MouseEventArgs e)
{
moveX = e.X - x;
moveY = e.Y - y;
if ((Math.Abs(moveX) + Math.Abs(moveY)) > 5)
{
this.Invalidate();
}
}
mouse按下时,记录坐标,弹起时,计算偏移量,并且重绘UI.这对手机的性能有一定的要求,可能会有点卡,但是我个人觉得还是可以接受.不知各位有没有更好的方法.
实现惯性
当划动到边的松开时候,如果有下一页,则翻到下一页,如果没有,则慢慢的返回到原来的位置。
实现这个功能主要是使用一个timer控件。用TIMER来计算位置,具体实现请看代码。