一、本文内容概述
在前文时间间隔设置页面显示基础上,继续完善,实现点击设置页面的“选择目录”图标按钮,
显示如下“浏览”页面。然后接着实现浏览页面的四个菜单功能。
四个菜单的功能介绍:
1.向上:返回上一目录,如果在顶层目录,则返回到“设置页面”
2.选择:选中某一目录作为主页面里“连播模式”的播放对象
3.上一页:返回上层目录,如果在顶层目录,并不返回
4. 下一页:进入下一层目录,如果下一层目录不存在,点击没有反应。
补充:显示的目录和文件在根文件系统上。
下面,分为两部分讲解: 浏览页面显示 + 菜单功能实现
友情提示:本文长达1.2w+,可按前面目录按需选择 : )
![显示浏览页面](https://img-blog.csdnimg.cn/20200524211152589.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyODAwMDc1,size_16,color_FFFFFF,t_70)
二、浏览页面的显示
浏览页面显示分两三走:1)计算菜单图标坐标 2)计算目录、文件图标位置 3)获取根文件系统目录和文件名
2. 1 计算菜单图标坐标
我用的是4.3寸(480x272)LCD, 宽大于高,故菜单图标从上到下排列在LCD最左侧合适。
(当然,如果使用其它尺寸,可将菜单图标放在最上边,这里只讲解最左侧的坐标位置计算)
![4个菜单坐标位置计算](https://img-blog.csdnimg.cn/20200524214606818.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyODAwMDc1,size_16,color_FFFFFF,t_70)
来看下代码实现,在browse_page.c 文件中
static void BrowsePageCalcMenuLayout(PT_PageLayout ptPageLayout)
{
int i;
int iWidth;
int iHeight;
int iXres, iYres, iBpp;
int iTmpTotalBytes;
PT_Layout atLayout;
atLayout = ptPageLayout->atLayout;
GetLCDParams(&iXres, &iYres, &iBpp);
ptPageLayout->iBpp = iBpp;
iHeight = iYres / 4;
iWidth = iHeight;
atLayout[0].iLeftTopY = 0;
atLayout[0].iRightBottomY = atLayout[0].iLeftTopY + iHeight - 1;
atLayout[0].iLeftTopX = 0;
atLayout[0].iRightBottomX = atLayout[0].iLeftTopX + iWidth - 1;
atLayout[1].iLeftTopY = atLayout[0].iRightBottomY + 1;
atLayout[1].iRightBottomY = atLayout[1].iLeftTopY + iHeight - 1;
atLayout[1].iLeftTopX = 0;
atLayout[1].iRightBottomX = atLayout[1].iLeftTopX + iWidth - 1;
atLayout[2].iLeftTopY = atLayout[1].iRightBottomY + 1;
atLayout[2].iRightBottomY = atLayout[2].iLeftTopY + iHeight - 1;
atLayout[2].iLeftTopX = 0;
atLayout[2].iRightBottomX = atLayout[2].iLeftTopX + iWidth - 1;
atLayout[3].iLeftTopY = atLayout[2].iRightBottomY + 1;
atLayout[3].iRightBottomY = atLayout[3].iLeftTopY + iHeight - 1;
atLayout[3].iLeftTopX = 0;
atLayout[3].iRightBottomX = atLayout[3].iLeftTopX + iWidth - 1;
i = 0;
while (atLayout[i].pcIconName)
{
iTmpTotalBytes = (atLayout[i].iRightBottomX - atLayout[i].iLeftTopX + 1) *
(atLayout[i].iRightBottomY - atLayout[i].iLeftTopY + 1) * iBpp / 8;
if (ptPageLayout->iMaxTotalBytes < iTmpTotalBytes)
{
ptPageLayout->iMaxTotalBytes = iTmpTotalBytes;
}
i++;
}
}
2. 2 计算目录、文件图标位置
相对于菜单图标坐标位置计算,目录、文件图标位置计算难一些,但都是对称的,可用for循环计算。
分两步走:2.1)计算图标位置 2.2) 分配、提取图标,并写入缓冲块再刷到LCD显存
2.1)计算图标位置,如下图
![目录、文件图标位置计算](https://img-blog.csdnimg.cn/20200524221054953.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyODAwMDc1,size_16,color_FFFFFF,t_70)
代码实现如下,也在browse_page.c文件中
#define DIR_FILE_ICON_WIDTH 40
#define DIR_FILE_ICON_HEIGHT DIR_FILE_ICON_WIDTH
#define DIR_FILE_NAME_HEIGHT 20
#define DIR_FILE_NAME_WIDTH (DIR_FILE_ICON_HEIGHT + DIR_FILE_NAME_HEIGHT)
#define DIR_FILE_ALL_WIDTH DIR_FILE_NAME_WIDTH
#define DIR_FILE_ALL_HEIGHT DIR_FILE_ALL_WIDTH
static PT_Layout g_ptDirAndFileLayout;
static int BrowsePageCalcDirAndFileLayout(void)
{
unsigned int iNumPerRow = 0;
unsigned int iNumPerCol = 0;
int iRectLeftTopX;
int iRectLeftTopY;
int iRectRightBottomX;
int iRectRightBottomY;
int iDeltaX, iDeltaY;
int iIconWidth, iIconHeight;
int iXres, iYres, iBp;
int iTopLeftXBak;
int iRow, iCol, iStep = 0;
GetLCDParams(&iXres, &iYres, &iBpp);
iRectLeftTopX = g_atBrowsePageIconLayout[0].iRightBottomX + 1;
iRectLeftTopY = 0;
iRectRightBottomX = iXres -1;
iRectRightBottomY = iYres - 1;
iIconWidth = DIR_FILE_ALL_WIDTH;
iIconHeight = iIconWidth;
iNumPerRow = (iRectRightBottomX - iRectLeftTopX) / iIconWidth;
while (1)
{
iDeltaX = (iRectRightBottomX - iRebLeftTopX + 1) - iIconWidth * iNumPerRow;
if ((iDeltaX / (iNumPerRow + 1)) < 10)
iNumPerRow--;
else
break;
}
iNumPerCol = (iRectRightBottomY - iRectLeftTopY) / iIconHeight;
while (1)
{
iDeltaY = (iRectRightBottomY - iRectLeftTopY + 1) - iIconHeight * iNumPerCol;
if ((iDeltaY / (iNumPerCol + 1)) < 10)
iNumPerCol--;
else
break;
}
iDeltaX = iDeltaX / (iNumPerRow + 1);
iDeltaY = iDeltaY / (iNumPerCol + 1);
g_iDirFileNumPerRow = iNumPerRow;
g_iDirFileNumPerCol = iNumPerCol;
g_ptDirAndFileLayout = malloc(sizeof(T_Layout) * (2 * iNumPerRow * iNumPerCol + 1));
if(NULL == g_ptDirAndFileLayout)
{
DBG_PRINTF("malloc g_ptDirAndFileLayout fail!\n");
return -1;
}
g_tBrowsePageDirAndFileLayout.iTopLeftX = iRectLeftTopX;
g_tBrowsePageDirAndFilbeLayout.iBotRightX = iRectLeftTopY;
g_tBrowsePageDirAndFileLayout.iTopLeftY = iRectRightBottomX;
g_tBrowsePageDirAndFileLayout.iBotRightY = iRectRightBottomY;
g_tBrowsePageDirAndFileLayout.iBpp = iBpp;
g_tBrowsePageDirAndFileLayout.atLayout = g_ptDirAndFileLayout;
g_tBrowsePageDirAndFileLayout.iMaxTotalBytes = DIR_FILE_ALL_WIDTH * DIR_FILE_ALL_HEIGHT * iBpp / 8;
iRectLeftTopX += iDeltaX;
iRectLeftTopY += iDeltaY;
iTopLeftXBak = iRectLeftTopX;
for(iCol = 0; iCol < iNumPerCol; ++iCol)
{
for(iRow = 0; iRow < iNumPerRow; ++iRow)
{
g_ptDirAndFileLayout[iStep].iLeftTopX = iRectLeftTopX + (DIR_FILE_NAME_WIDTH - DIR_FILE_ICON_WIDTH) / 2;
g_ptDirAndFileLayout[iStep].iLeftTopY = iRectLeftTopY;
g_ptDirAndFileLayout[iStep].iRightBottomX = g_ptDirAndFileLayout[iStep].iLeftTopX + DIR_FILE_ICON_WIDTH -1;
g_ptDirAndFileLayout[iStep].iRightBottomY = iRectLeftTopY + DIR_FILE_ICON_HEIGHT - 1;
g_ptDirAndFileLayout[iStep+1].iLeftTopX = iRectLeftTopX ;
g_ptDirAndFileLayout[iStep+1].iLeftTopY = g_ptDirAndFileLayout[iStep].iRightBottomY + 1;
g_ptDirAndFileLayout[iStep+1].iRightBottomX = iRectLeftTopX + DIR_FILE_NAME_WIDTH - 1;
g_ptDirAndFileLayout[iStep+1].iRightBottomY = g_ptDirAndFileLayout[iStep+1].iLeftTopY
+ DIR_FILE_NAME_HEIGHT - 1;
iRectLeftTopX += DIR_FILE_ALL_WIDTH + iDeltaX;
iStep += 2;
}
iRectLeftTopX = iTopLeftXBak;
iRectLeftTopY += DIR_FILE_ALL_HEIGHT + iDeltaY;
}
g_ptDirAndFileLayout[iStep].iLeftTopX = 0;
g_ptDirAndFileLayout[iStep].iLeftTopY = 0;
g_ptDirAndFileLayout[iStep].iRightBottomX = 0;
g_ptDirAndFileLayout[iStep].iRightBottomY = 0;
g_ptDirAndFileLayout[iStep].pcIconName = NULL;
return 0;
}
2.2)分配、提取图标,并写入缓冲块再刷到LCD显存
这部分看代码即可,不难理解。(下面的代码也在browse_page.c文件)
static int
GenerateBrowsePageDirAndFile(int iStartIndex, int iDirContentsNumber,
PT_DirContent *aptDirContents, PT_VideoMem ptVideoMem)
{
static int bGetDirFileBmp = 0;
static T_PixelDatas tDirIconPixelDatas;
static T_PixelDatas tFileIconPixelDatas;
PT_PageLayout ptPageLayout = &g_tBrowsePageDirAndFileLayout;
PT_Layout atLayout = ptPageLayout->atLayout;
T_PixelDatas tOriginIconPixelDatas;
int iDirContentIndex = iStartIndex;
int iError;
int i, j, k = 0;
if(!bGetDirFileBmp)
{
tDirIconPixelDatas.iBpp = ptVideoMem->tPixelDatas.iBpp;
tDirIconPixelDatas.aucPixelDatas = malloc(ptPageLayout->iMaxTotalBytes);
if (tDirIconPixelDatas.aucPixelDatas == NULL)
{
return -1;
}
tFileIconPixelDatas.iBpp = ptVideoMem->tPixelDatas.iBpp;
tFileIconPixelDatas.aucPixelDatas = malloc(ptPageLayout->iMaxTotalBytes);
if (tFileIconPixelDatas.aucPixelDatas == NULL)
{
return -1;
}
iError = GetPixelDatasForIcon(g_strDirIconName, &tOriginIconPixelDatas);
if (iError)
{
DBG_PRINTF("GetPixelDatasForIcon %s error!\n", g_strDirIconName);
return -1;
}
tDirIconPixelDatas.iHeight = atLayout[0].iRightBottomY - atLayout[0].iLeftTopY + 1;
tDirIconPixelDatas.iWidth = atLayout[0].iRightBottomX - atLayout[0].iLeftTopX + 1;
tDirIconPixelDatas.iLineBytes = tDirIconPixelDatas.iWidth * tDirIconPixelDatas.iBpp / 8;
tDirIconPixelDatas.iTotalBytes = tDirIconPixelDatas.iLineBytes * tDirIconPixelDatas.iHeight;
PicZoom(&tOriginIconPixelDatas, &tDirIconPixelDatas);
FreePixelDatasForIcon(&tOriginIconPixelDatas);
iError = GetPixelDatasForIcon(g_strFileIconName, &tOriginIconPixelDatas);
if (iError)
{
DBG_PRINTF("GetPixelDatasForIcon %s error!\n", g_strFileIconName);
return -1;
}
tFileIconPixelDatas.iHeight = atLayout[0].iRightBottomY - atLayout[0].iLeftTopY + 1;
tFileIconPixelDatas.iWidth = atLayout[0].iRightBottomX - atLayout[0].iLeftTopX+ 1;
tFileIconPixelDatas.iLineBytes = tDirIconPixelDatas.iWidth * tDirIconPixelDatas.iBpp / 8;
tFileIconPixelDatas.iTotalBytes = tFileIconPixelDatas.iLineBytes * tFileIconPixelDatas.iHeight;
PicZoom(&tOriginIconPixelDatas, &tFileIconPixelDatas);
FreePixelDatasForIcon(&tOriginIconPixelDatas);
bGetDirFileBmp = 1;
}
ClearRectangleInVideoMem(ptPageLayout->iTopLeftX, ptPageLayout->iTopLeftY,
ptPageLayout->iBotRightX, ptPageLayout->iBotRightY, ptVideoMem, COLOR_BACKGROUND);
SetFontSize(atLayout[1].iRightBottomY - atLayout[1].iLeftTopY - 5);
for (i = 0; i < g_iDirFileNumPerCol; i++)
{
for (j = 0; j < g_iDirFileNumPerRow; j++)
{
if (iDirContentIndex < iDirContentsNumber)
{
if (aptDirContents[iDirContentIndex]->eFileType == FILETYPE_DIR)
{
PicMerge(atLayout[k].iLeftTopX, atLayout[k].iLeftTopY,
&tDirIconPixelDatas, &ptVideoMem->tPixelDatas);
}
else
{
PicMerge(atLayout[k].iLeftTopX, atLayout[k].iLeftTopY,
&tFileIconPixelDatas, &ptVideoMem->tPixelDatas);
}
k++;
iError = MergerStringToCenterOfRectangleInVideoMem(atLayout[k].iLeftTopX,
atLayout[k].iLeftTopY, atLayout[k].iRightBottomX, atLayout[k].iRightBottomY,
(unsigned char *)aptDirContents[iDirContentIndex]->strName, ptVideoMem);
k++;
iDirContentIndex++;
}
else
{
break;
}
}
if (iDirContentIndex >= iDirContentsNumber)
{
break;
}
}
return 0;
}
2. 3 获取根文件系统目录和文件名
策略:把这些目录名、文件名放在一个数组里,显示时指定下标即可确定每页内容。
这部分下面代码解决的很清除了,看源码
int GetDirContents(char *strDirName, PT_DirContent **pptDirContents, int *piNumber)
{
PT_DirContent *aptDirContents;
struct dirent **aptNameList;
int iNumber;
int i;
int j;
iNumber = scandir(strDirName, &aptNameList, 0, alphasort);
if (iNumber < 0)
{
DBG_PRINTF("scandir error : %s!\n", strDirName);
return -1;
}
aptDirContents = malloc(sizeof(PT_DirContent) * (iNumber - 2));
if (NULL == aptDirContents)
{
DBG_PRINTF("malloc error!\n");
return -1;
}
*pptDirContents = aptDirContents;
for (i = 0; i < iNumber - 2; i++)
{
aptDirContents[i] = malloc(sizeof(T_DirContent));
if (NULL == aptDirContents)
{
DBG_PRINTF("malloc error!\n");
return -1;
}
}
for (i = 0, j = 0; i < iNumber; i++)
{
if ((0 == strcmp(aptNameList[i]->d_name, ".")) || (0 == strcmp(aptNameList[i]->d_name, "..")))
continue;
if (isDir(strDirName, aptNameList[i]->d_name))
{
strncpy(aptDirContents[j]->strName, aptNameList[i]->d_name, 256);
aptDirContents[j]->strName[255] = '\0';
aptDirContents[j]->eFileType = FILETYPE_DIR;
free(aptNameList[i]);
aptNameList[i] = NULL;
j++;
}
}
for (i = 0; i < iNumber; i++)
{
if (aptNameList[i] == NULL)
continue;
if ((0 == strcmp(aptNameList[i]->d_name, ".")) || (0 == strcmp(aptNameList[i]->d_name, "..")))
continue;
if (isRegFile(strDirName, aptNameList[i]->d_name))
{
strncpy(aptDirContents[j]->strName, aptNameList[i]->d_name, 256);
aptDirContents[j]->strName[255] = '\0';
aptDirContents[j]->eFileType = FILETYPE_FILE;
free(aptNameList[i]);
aptNameList[i] = NULL;
j++;
}
}
for (i = j; i < iNumber - 2; i++)
{
free(aptDirContents[i]);
}
for (i = 0; i < iNumber; i++)
{
if (aptNameList[i])
{
free(aptNameList[i]);
}
}
free(aptNameList);
*piNumber = j;
return 0;
}
三、菜单功能的实现
1.向上:返回上一目录,如果在顶层目录,则返回到“设置页面”
2. 选择:选中某一目录作为主页面里“连播模式”的播放对象(这个留到连播实现再讲解)
3.上一页:返回上层目录,如果在顶层目录,并不返回
4. 一页:进入下一层目录,如果下一层目录不存在,点击没有反应。
先实现翻页操作。
前面说过又数组存储当前页的目录和文件名,因此,有了前面获得一页内容功能后,对于翻页操作,
只需改变数组下标,确定当前页显示哪些目录或文件即可。
下面代码只展示逻辑部分,完整代码可从我的“代码集合”获取 :)
case 0:
{
return;
break;
}
case 1:
{
break;
}
case 2:
{
g_iStartIndex -= g_iDirFileNumPerRow * g_iDirFileNumPerCol;
if(g_iStartIndex >= 0)
{
iError = GenerateBrowsePageDirAndFile(g_iStartIndex,
g_iDirContentsNumber, g_aptDirContents, ptDevVideoMem);
}
else
{
g_iStartIndex += g_iDirFileNumPerRow * g_iDirFileNumPerCol;
}
break;
}
case 3:
{
g_iStartIndex += g_iDirFileNumPerRow * g_iDirFileNumPerCol;
if(g_iStartIndex < g_iDirContentsNumber)
{
iError = GenerateBrowsePageDirAndFile(g_iStartIndex,
g_iDirContentsNumber, g_aptDirContents, ptDevVideoMem);
}
else
{
g_iStartIndex -= g_iDirFileNumPerRow * g_iDirFileNumPerCol;
}
break;
}
那点击目录图标时,如何显示子目录内容呢?
限于篇幅,这里提一下编程思路,详细实现还请参考源码。
![显示子目录内容的逻辑图](https://img-blog.csdnimg.cn/20200524231059329.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyODAwMDc1,size_16,color_FFFFFF,t_70)
ok,1.2 w+的内容,篇幅太长了。就此打住。
如果我还有没讲清除或讲错的地方,欢迎评论交流噢 : )