数码相框项目笔记
页面模块
这个是整个项目中的主体部分,该模块实现页面设计、显示、侦听输入事件、相应输入事件的功能。它同样提供了管理模块,当要增加页面时,向其注册即可。
1、主页面
主页面比较简单,提供三个按钮:浏览模式、连播模式、设置
点击浏览模式---->显示浏览页面
点击连播模式---->显示连播页面
点击设置---->显示设置界面
点击其他区域---->不作回应
大致设计效果如图:
在开发板上实际运行效果:
主页面上要显示图标,先要计算出每个图标的坐标值,如上图所示,然后调用解析和渲染模块,将RGB数据写入显存,在屏幕上显示页面。
如何确定用户点击了图标呢?直接通过输入模块读取触点的坐标,判断坐标位置是否位于图标区域即可。
主页面函数的核心在于run函数(MainPageRun),此函数主要做两件事:一是显示页面,二是调用输入模块获得数据,根据输入执行不同的动作。
/* 主页面运行函数:显示页面、处理输入事件 */
static void MainPageRun(PT_PageParams ptParentPageParams)
{
int iIndex;
T_InputEvent tInputEvent;
int bPressed = 0;
int iIndexPressed = -1;
T_PageParams tPageParams;
tPageParams.iPageID = ID("main");
/* 1. 显示页面 */
ShowMainPage(&g_tMainPageLayout);
/* 2. 调用GetInputEvent获得输入事件,进而处理 */
while (1)
{
iIndex = MainPageGetInputEvent(&g_tMainPageLayout, &tInputEvent);
if (tInputEvent.iPressure == 0)
{
/* 如果是松开 */
if (bPressed)
{
/* 曾经有按钮被按下 */
ReleaseButton(&g_atMainPageIconsLayout[iIndexPressed]);
bPressed = 0;
if (iIndexPressed == iIndex) /* 按下和松开都是同一个按钮 */
{
switch (iIndexPressed)
{
case 0: /* 浏览按钮 */
{
Page("browse")->Run(&tPageParams);
/* 从设置页面返回后显示当首的主页面 */
ShowMainPage(&g_tMainPageLayout);
break;
}
case 1: /* 连播按钮 */
{
Page("auto")->Run(&tPageParams);
/* 从设置页面返回后显示当首的主页面 */
ShowMainPage(&g_tMainPageLayout);
break;
}
case 2: /* 设置按钮 */
{
Page("setting")->Run(&tPageParams);
/* 从设置页面返回后显示当首的主页面 */
ShowMainPage(&g_tMainPageLayout);
break;
}
default:
{
break;
}
}
}
iIndexPressed = -1;
}
}
else
{
/* 按下状态 */
if (iIndex != -1)
{
if (!bPressed)
{
/* 未曾按下按钮 */
bPressed = 1;
iIndexPressed = iIndex;
PressButton(&g_atMainPageIconsLayout[iIndexPressed]);
}
}
}
}
}
2、浏览页面
浏览页面里把显示区域分为两部分:“菜单”、“目录和文件”
- "菜单"就是"返回、选择、上一页、下一页"四个可操作的图标
- "目录和文件"是浏览的内容
设计大致如下:
因为有两个显示部分,所以需要分别计算菜单显示区域和目录及文件的显示区域。在目录及文件区域,我们需要确定一行显示几个目录或文件,它们之间的间隔要适当(大于10个像素)。
开发板运行画面:
当用户点击向上按钮:先判断是否是顶层目录“\”,如果是顶层目录则直接返回;如果不是,则释放当前目录内容,获取上一层目录内容并显示。
选择按钮:(如果不是用于"选择目录", 该按钮无用处)当点击选择按钮,看按钮之前是否被选中(标记),如果被选中则释放图标,如果不是则选中。
上一页:如果上一页还有数据,就显示
下一页:如果下一页有数据,就显示
当点击目录和文件区域:如果是目录就释放当前页面内容,进入该目录并显示其内容。如果是图片就调用图片显示页面,显示图片。
图片显示页面
当我们选中了图片文件时,就会进入图片显示页面。在此页面中可以对图片进行放大、缩小、移动等操作。
具体的页面设计如下
当点击菜单区域图标:
返回按钮:释放图片内容,获取当前目录内容并显示
放大:调用图片操作模块,实现图片的放大功能(通过缩放因子控制放大缩小程度)
缩小:调用图片操作模块,实现图片的缩小功能
上一张:如果有则释放当前图片内容,获得上一张图片内容并显示
下一张:如果有则释放当前图片内容,获得下一张图片内容并显示
连播:调用连播模块,连续播放当前目录中的图片
如果点击的是图片显示区域,则滑动图片:
滑动图片中,是一直触摸的,我们可以记录触点的移动距离(第一次触点和当前触点)来确定图片移动的位置。
run函数:
#define ZOOM_RATIO 0.9
#define SLIP_MIN_DISTANCE (2*2)
...
static void ManualPageRun(PT_PageParams ptParentPageParams)
{
T_InputEvent tInputEvent;
T_InputEvent tPreInputEvent;
int bButtonPressed = 0;
int bPicSlipping = 0;
int iIndexPressed = -1;
int iIndex;
T_PageParams tPageParams;
int iError;
char strDirName[256];
char strFileName[256];
char strFullPathName[256];
PT_DirContent *aptDirContents;
int iDirContentsNumber;
int iPicFileIndex;
char *pcTmp;
PT_VideoMem ptDevVideoMem;
int iZoomedWidth;
int iZoomedHeight;
PT_PixelDatas ptZoomedPicPixelDatas = &g_tZoomedPicPixelDatas;
/* 这两句只是避免编译警告 */
tPreInputEvent.iX = 0;
tPreInputEvent.iY = 0;
tPageParams.iPageID = ID("manual");
ptDevVideoMem = GetDevVideoMem();
strcpy(strFullPathName, ptParentPageParams->strCurPictureFile);
/* 显示菜单和图片文件 */
ShowManualPage(&g_tManualPageMenuIconsLayout, strFullPathName);
/* 取出目录名 */
strcpy(strDirName, ptParentPageParams->strCurPictureFile);
pcTmp = strrchr(strDirName, '/');
*pcTmp = '\0';
/* 取出文件名 */
strcpy(strFileName, pcTmp+1);
/* 获得当前目录下所有目录和文件的名字 */
iError = GetDirContents(strDirName, &aptDirContents, &iDirContentsNumber);
/* 确定当前显示的是哪一个文件 */
for (iPicFileIndex = 0; iPicFileIndex < iDirContentsNumber; iPicFileIndex++)
{
if (0 == strcmp(strFileName, aptDirContents[iPicFileIndex]->strName))
{
break;
}
}
while (1)
{
/* 先确定是否触摸了菜单图标 */
iIndex = ManualPageGetInputEvent(&g_tManualPageMenuIconsLayout, &tInputEvent);
/* 如果是松开 */
if (tInputEvent.iPressure == 0)
{
bPicSlipping = 0;
if (bButtonPressed)
{
/* 曾经有按钮被按下 */
ReleaseButton(&g_atManualMenuIconsLayout[iIndexPressed]);
bButtonPressed = 0;
if (iIndexPressed == iIndex) /* 按下和松开都是同一个按钮 */
{
switch (iIndexPressed)
{
case 0: /* 返回按钮 */
{
return;
break;
}
case 1: /* 缩小按钮 */
{
/* 获得缩小后的数据 */
iZoomedWidth = (float)g_tZoomedPicPixelDatas.iWidth * ZOOM_RATIO;
iZoomedHeight = (float)g_tZoomedPicPixelDatas.iHeight * ZOOM_RATIO;
ptZoomedPicPixelDatas = GetZoomedPicPixelDatas(&g_tOriginPicPixelDatas, iZoomedWidth, iZoomedHeight);
/* 重新计算中心点 */
g_iXofZoomedPicShowInCenter = (float)g_iXofZoomedPicShowInCenter * ZOOM_RATIO;
g_iYofZoomedPicShowInCenter = (float)g_iYofZoomedPicShowInCenter * ZOOM_RATIO;
/* 显示新数据 */
ShowZoomedPictureInLayout(ptZoomedPicPixelDatas, ptDevVideoMem);
break;
}
case 2: /* 放大按钮 */
{
/* 获得放大后的数据 */
iZoomedWidth = (float)g_tZoomedPicPixelDatas.iWidth / ZOOM_RATIO;
iZoomedHeight = (float)g_tZoomedPicPixelDatas.iHeight / ZOOM_RATIO;
ptZoomedPicPixelDatas = GetZoomedPicPixelDatas(&g_tOriginPicPixelDatas, iZoomedWidth, iZoomedHeight);
/* 重新计算中心点 */
g_iXofZoomedPicShowInCenter = (float)g_iXofZoomedPicShowInCenter / ZOOM_RATIO;
g_iYofZoomedPicShowInCenter = (float)g_iYofZoomedPicShowInCenter / ZOOM_RATIO;
/* 显示新数据 */
ShowZoomedPictureInLayout(ptZoomedPicPixelDatas, ptDevVideoMem);
break;
}
case 3: /* "上一张"按钮 */
{
while (iPicFileIndex > 0)
{
iPicFileIndex--;
snprintf(strFullPathName, 256, "%s/%s", strDirName, aptDirContents[iPicFileIndex]->strName);
strFullPathName[255] = '\0';
if (isPictureFileSupported(strFullPathName))
{
ShowPictureInManualPage(ptDevVideoMem, strFullPathName);
break;
}
}
break;
}
case 4: /* "下一张"按钮 */
{
while (iPicFileIndex < iDirContentsNumber - 1)
{
iPicFileIndex++;
snprintf(strFullPathName, 256, "%s/%s", strDirName, aptDirContents[iPicFileIndex]->strName);
strFullPathName[255] = '\0';
if (isPictureFileSupported(strFullPathName))
{
ShowPictureInManualPage(ptDevVideoMem, strFullPathName);
break;
}
}
break;
}
case 5: /* "连播"按钮 */
{
/* Manual页面的触发有两个方法: 在主页面按"浏览模式"进入"浏览页面"->"选中某个文件", 在"连播页面"里点击正在显示的图片
* 如果是后者, 直接return就可以了:因为return后是返回到"连播页面"的, 它会继续"连播"
*/
if (ptParentPageParams->iPageID == ID("browse")) /* 触发自"浏览页面" */
{
strcpy(tPageParams.strCurPictureFile, strFullPathName);
Page("auto")->Run(&tPageParams);
ShowManualPage(&g_tManualPageMenuIconsLayout, tPageParams.strCurPictureFile);
}
else /* 当前manual页面的父页面是auto页面, 直接返回即可 */
{
return;
}
break;
}
default:
{
break;
}
}
}
iIndexPressed = -1;
}
}
else /* 按下状态 */
{
/* 点击的是菜单按钮 */
if (iIndex != -1)
{
if (!bButtonPressed)
{
/* 未曾按下按钮 */
bButtonPressed = 1;
iIndexPressed = iIndex;
PressButton(&g_atManualMenuIconsLayout[iIndexPressed]);
}
}
else /* 点击的是图片显示区域, 滑动图片 */
{
/* 如果没有按钮被按下 */
if (!bButtonPressed && !bPicSlipping)
{
bPicSlipping = 1;
tPreInputEvent = tInputEvent;
}
if (bPicSlipping)
{
/* 如果触点滑动距离大于规定值, 则挪动图片 */
if (DistanceBetweenTwoPoint(&tInputEvent, &tPreInputEvent) > SLIP_MIN_DISTANCE)
{
/* 重新计算中心点 */
g_iXofZoomedPicShowInCenter -= (tInputEvent.iX - tPreInputEvent.iX);
g_iYofZoomedPicShowInCenter -= (tInputEvent.iY - tPreInputEvent.iY);
/* 显示新数据 */
ShowZoomedPictureInLayout(ptZoomedPicPixelDatas, ptDevVideoMem);
/* 记录滑动点 */
tPreInputEvent = tInputEvent;
}
}
}
}
}
}
开发板实际画面: