屏幕自适应修改方案
 
    源:终端开发
关 键 词:分辨率  屏幕 布局
 
现象、问题描述
  为了适应不同分辨率屏幕上iRead的正常显示,在不替换图片的前提下,对画面进行缩放和位置调整是一种可以满足所有屏幕变化的方案。iRead原先的绘图方案是用Image::Draw方法来绘制png图片,这个方法是可以对图片进行拉伸,那么剩下的问题就是如何缩放和调整位置了。那么什么时候该缩放,什么时候该调整位置,这个怎么决定呢?下面对这些问题做几点描述。
 
关键过程、根本原因分析
 
1. 图片的缩放和位置的调整      
屏幕自适应是为了我们的程序画面能够满足不同的分辨率,如480x640,240x320,480x800等等。如果程序一开始是在480x640下写的程序,换到240x320屏幕,我们需要把图片长宽都缩小一半就可以了,胆识换到480x800下面就不同了,如果按x和y方向缩放比例拉伸的话,那么图片就会被拉长变形,画面就会失真。所以我们不能这么处理。因为我们的程序是在480x640开发的,那么就以这个为基准,来对其他分辨率的图片进行处理。在对图片进行缩放之前,先得出X方向和Y方向的两个缩放因子,dx=x/480,dy=y/640,并且取小的缩放因子,这样图片就能显示完整,画面不再失真。
图片缩放大小好了,还要考虑图片的位置,原先的对齐方式都是以左上角的点为原点,然后给出相对于这个点的位置坐标。需要考虑图片的对齐方式。
a. 置顶靠左对齐: 如果原先显示图片的区域是可以滚动的,那么就不需要更改,图片还是以左上角为原点,各个图片是从上往下,从左往右的顺序,间距也不用调整
b. 居中对齐: 如果原先的图片是要放在中间的,那么区域扩大后,中点也跟着要改变,在480x800下就是Y方向向下偏移1.25倍距离,比如PopDlg对话框。
c. 靠底对齐和靠右对齐:这种情况程序中很少出现,只有在我的设置里面的几个按钮才需要靠右对齐,处理的话和第一种情况一样。
下面给出调整图片位置和大小方案的代码
CRect CDataProcessCtrl::GetRect( const string& strWinName, int iRectId, int iAnchorType)
{
    CRect rcTemp;
    (void)GetRect( strWinName, iRectId, rcTemp);
    int cx = GetSystemMetrics(SM_CXSCREEN);
    int cy = GetSystemMetrics(SM_CYSCREEN);
    double iMaxCx = (double)cx/(double)STD_SCREEN_CX;
    double iMaxCy = (double)cy/(double)STD_SCREEN_CY; 
    double dScale = iMaxCy - iMaxCx;
    double iTemp = 0;
 
    int iSrcWidth = rcTemp.Width();
    int iSrcHeight = rcTemp.Height();
 
    if( cx > cy )
    {
       iTemp = (double)rcTemp.left * iMaxCx;
       rcTemp.left =(int)iTemp;
       iTemp = (double)rcTemp.right * iMaxCx;
       rcTemp.right =(int)iTemp;
       iTemp = (double)rcTemp.top * iMaxCy;
       rcTemp.top =(int)iTemp;
       iTemp = (double)rcTemp.bottom * iMaxCy;
       rcTemp.bottom =(int)iTemp;
 
       return rcTemp;
    }
 
    //比例相等
    if( dScale == 0 )
    {
       iTemp = (double)rcTemp.left * iMaxCx;
       rcTemp.left =(int)iTemp;
       iTemp = (double)rcTemp.right * iMaxCx;
       rcTemp.right =(int)iTemp;
       iTemp = (double)rcTemp.top * iMaxCy;
       rcTemp.top =(int)iTemp;
       iTemp = (double)rcTemp.bottom * iMaxCy;
       rcTemp.bottom =(int)iTemp;
    }
    //CY比例大于 CX,竖屏情况,以CX为基准
    else if( dScale > 0 )
    {
       // 居中对齐, Y坐标按Y方向做不等比缩放
       if( iAnchorType & ANCHOR_VCENTER )        {
           iTemp = (double)rcTemp.top * iMaxCy;
           rcTemp.top =(int)iTemp;
       }
       // 靠顶对齐, Y坐标按X方向做等比缩放
       else if( iAnchorType & ANCHOR_TOP )   
       {
           iTemp = (double)rcTemp.top * iMaxCx;
           rcTemp.top =(int)iTemp;
       }
       //X坐标做等比缩放
       iTemp = (double)rcTemp.left * iMaxCx;
       rcTemp.left =(int)iTemp;
       // 长宽做等比拉伸
       rcTemp.right = (int)(rcTemp.left + (double)(iSrcWidth * iMaxCx));
       rcTemp.bottom = (int)(rcTemp.top + (double)(iSrcHeight * iMaxCx));
    }
    //CY比例小于 CX,横屏情况,已CY为基准
    else
    {
       // 居中对齐, X坐标按X方向做不等比缩放
       if( iAnchorType & ANCHOR_HCENTER )        {
           iTemp = (double)rcTemp.left * iMaxCx;
           rcTemp.left =(int)iTemp;
       }
// 靠顶对齐, Y坐标按X方向做等比缩放
       else if( iAnchorType & ANCHOR_LEFT )  
       {
           iTemp = (double)rcTemp.left * iMaxCy;
           rcTemp.left =(int)iTemp;
       }
       // Y坐标只做等比拉伸
       iTemp = (double)rcTemp.top * iMaxCy;
       rcTemp.top =(int)iTemp;
       // 长宽做等比拉伸
       rcTemp.right = (int)(rcTemp.left + (double)(iSrcWidth * iMaxCy));
       rcTemp.bottom = (int)(rcTemp.top + (double)(iSrcHeight * iMaxCy));
    }
   
    return rcTemp;
}
 
2. 图片的嵌套关系和相对位置
因为程序中有许多图片都是有嵌套关系的,比如PopDlg是居中对齐的,而里面的元素却是置顶对齐,所以我们需要给出元素所在窗口的相对位置。原先的程序中很多都是以屏幕的右上点为原点给出的位置,那么就要改成使用相对父窗口为原点的位置。
 
3 浮点数计算的优化和横竖屏变换
在计算坐标时,需要使用到float类型数据运算,如果在OnPaint中执行的话,会耗去很多时间,而且是重复的过程,所以将区域提取出来作为成员变量,在OnSize中计算出些值,再在OnPaint中使用,同时OnSize也可以满足窗口发生变化的情况,如果横竖屏的切换。
另外进行float计算的过程可以用两个 long型来代替,通过一个函数来执行iLength*zoomX的运算 ZoomX(iLength),ZoomY(iWidth)。在屏幕缩放中使用到得值目前最大只是800,再多考虑点最大的值设为10,000也就够了,因为目前还没有这么大像素的显示屏,那么两个最大值相乘也就是100,000,000,long型也就足够了。
 
4. 字体的缩放
程序的字体中使用的是系统的,不同的屏幕的系统都各自设置了合适的字体,所以一半不需要考虑字体的变化,除非是一些特殊的情况,我们可以更改字体的高度来满足这种变化,当然字体最好要满足和屏幕的缩放比例一致,太小的话也会影响效果。
 
5. 计算的误区
       在显示文字的时候,经常要将文字放置在区域中间,而文字区域也需要根据文字的数量来确定,那么通过中心点来调整文字区域就很必要
    rcTip.left      = rc.CenterPoint().x - sizeTip.cx / 2;
    rcTip.top       = rc.CenterPoint().y - sizeTip.cy / 2;
    rcTip.right     = rc.CenterPoint().x + sizeTip.cx / 2;
    rcTip.bottom    = rc.CenterPoint().y + sizeTip.cy / 2;
 
上面的方法咋一看起来没什么问题,可要是sizeTip.cx和sizeTip.cy是个奇数就有问题了,算出来的区域会比原先会少了1个像素,在显示换行文字的时候就会有问题。而下面的方法就可以避免:       
    rcTip.left      = rc.CenterPoint().x - sizeTip.cx / 2;
    rcTip.top       = rc.CenterPoint().y - sizeTip.cy / 2;
    rcTip.right     = rcTip.left + sizeTip.cx;
    rcTip.bottom    = rcTip.top + sizeTip.cy;
 
  
结论、解决方案及效果
以上就是iRead中屏幕自适应修改方案的几点描述,在使用了上面的方案后,对原先的代码只需要更改部分基本上就可以满足所有屏幕分辨率的需求。如果您还有更好的方案,请不吝赐教!