数码相框实现三:浏览页面显示及功能实现

一、本文内容概述
在前文时间间隔设置页面显示基础上,继续完善,实现点击设置页面的“选择目录”图标按钮,
显示如下“浏览”页面。然后接着实现浏览页面的四个菜单功能。

四个菜单的功能介绍:
1.向上:返回上一目录,如果在顶层目录,则返回到“设置页面”
2.选择:选中某一目录作为主页面里“连播模式”的播放对象
3.上一页:返回上层目录,如果在顶层目录,并不返回
4. 下一页:进入下一层目录,如果下一层目录不存在,点击没有反应。

补充:显示的目录和文件在根文件系统上。
下面,分为两部分讲解: 浏览页面显示 + 菜单功能实现
友情提示:本文长达1.2w+,可按前面目录按需选择 : )

显示浏览页面

二、浏览页面的显示
浏览页面显示分两三走:1)计算菜单图标坐标  2)计算目录、文件图标位置  3)获取根文件系统目录和文件名
2. 1 计算菜单图标坐标
	我用的是4.3寸(480x272)LCD, 宽大于高,故菜单图标从上到下排列在LCD最左侧合适。
	(当然,如果使用其它尺寸,可将菜单图标放在最上边,这里只讲解最左侧的坐标位置计算)

4个菜单坐标位置计算

来看下代码实现,在browse_page.c 文件中
/* 计算4个菜单图标坐标 */
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;
 	/*  iYres/4
   	*   -------------------------------------------
   	*    up       |
   	*             |
   	*    select   |
   	*             |     目录和文件
   	*    pre_page |
   	*             |
   	*   next_page |
   	*             |
   	*   -------------------------------------------
   	*/
 	iHeight = iYres / 4;
 	iWidth  = iHeight;
 	
 	/* up图标 */
 	atLayout[0].iLeftTopY  = 0;
 	atLayout[0].iRightBottomY = atLayout[0].iLeftTopY + iHeight - 1;
 	atLayout[0].iLeftTopX  = 0;
 	atLayout[0].iRightBottomX = atLayout[0].iLeftTopX + iWidth - 1;
 
 	/* select图标 */
 	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;
 	
 	/* pre_page图标 */
 	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;
 	
 	/* next_page图标 */
 	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;
 	
 	/* 待完善:可用 for 循环 计算上面图标的位置*/
    	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)计算图标位置,如下图

目录、文件图标位置计算

代码实现如下,也在browse_page.c文件中
/* 事先定义框的大小,每个框 60 x 60 = 图标 40x40 + 文字 60x20 */
#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)
{
    /* 1. 申请一页能显示的目录和文件图标内存空间 */
    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;
    
    /* 2. 计算每个目录和文件图标在LCD的显示位置 */
    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)
{
    /* 1.给目录和文件图标分配空间并设置(提取图标数据) */
    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)
    {
        // 1.1 分配图标空间
        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;
        }
        
        // 1.2 提取图标数据
        /* 1.2.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);
        
        /* 1.2.2 提取“文件图标”*/
        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);
    
    /* 2.将目录和文件图标写入缓冲块 */
     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;
 	
 	/* 扫描目录,结果按名字排序,存在aptNameList[0],aptNameList[1],... */
 	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;
  		}
 	}
 	
 	/* 先把目录挑出来存入aptDirContents */
 	for (i = 0, j = 0; i < iNumber; i++)
 	{
  		/* 忽略".",".."这两个目录 */
  		if ((0 == strcmp(aptNameList[i]->d_name, ".")) || (0 == strcmp(aptNameList[i]->d_name, "..")))
   			continue;
        	/* 并不是所有的文件系统都支持d_type, 所以不能直接判断d_type */
  		/* if (aptNameList[i]->d_type == DT_DIR) */
        	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++;
  		}
 	}
 	
 	/* 再把常规文件挑出来存入aptDirContents */
 	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;
        	/* 并不是所有的文件系统都支持d_type, 所以不能直接判断d_type */
  		/* if (aptNameList[i]->d_type == DT_REG) */
        	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++;
  		}
 	}
 	/* 释放aptDirContents中未使用的项 */
 	for (i = j; i < iNumber - 2; i++)
 	{
  		free(aptDirContents[i]);
 	}
 	/* 释放scandir函数分配的内存 */
 	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;
}
那点击目录图标时,如何显示子目录内容呢?
限于篇幅,这里提一下编程思路,详细实现还请参考源码。

显示子目录内容的逻辑图

ok,1.2 w+的内容,篇幅太长了。就此打住。
如果我还有没讲清除或讲错的地方,欢迎评论交流噢 : )
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值