场景:

  需要做出来的效果差不多如标题所述,即如果屏幕宽度为720,而图片的宽度只有150,现在需要从中间开始,往两边移动该图片,

我们现在以往右边移动为例,如果往右移动了150,此时绘制了一整个完整图形,继续移动,此时不仅要从中间绘制出新的图形,已经绘制的完整图形也得向右移动。进而达到动画的效果。

 

分析:

  为了达到上面的效果,最开始想到的是使用动画,但是发现:

  1)如果选择平移动画,只能移动图片,不会保留之前图片走过的路径,这样的话背景图就不会出现连续的情况,而只是有单张图,并不是这里想要的。

  2)选择多张图片循环播放,向gif那样,可如此一来使用到的图片资源过多,也不予考虑。

 

解决方案:

  最后决定自己实现一个view,通过在onDraw中不断的从中间向两边绘制一个图片,并平移已绘制的图形,进而达到想要的效果。

  这个看起来有些像游戏中的实现,即一个人物在屏幕中走,其实它一直都是在屏幕中央,只是背景图在不断的往左边绘制。

  

  如图所示。

  目前来看思路还是挺明确的,但是在实现的过程中,还是有一些情况要考虑清楚,这里做个简单的分析。

  1)图片从中间一点点绘制出来,这里需要一个偏移量xOffset,同时移动还有速度,所以得有个变量speed

  2)图片在屏幕中的显示存在两种情况:显示完全,显示不完全。

    i)针对显示不完全的情况,我们需要根据Bitmap的createBitmap来创建新的bitmap并将其绘制出来。

      那么显示不完全的图形的宽度是多少呢?明显的,中间新绘制出来的图形宽度为xOffset,最右边不完整图形的宽度为sourceWidth-超出屏幕的宽度。

      既然要以xOffset为createBitmap的参数来构建新的bitmap,那么xOffset得有个最大值xOffsetLimit,这里得对xOffset的值做个限定【1,xOffsetLimit】,

      每当xOffset+speed>xOffsetLimit时,都得重置xOffset为1.

      此时xOffsetLimit=sourceWidth。

      但是!!

      如果sourceWidth>parentWidth时,xOffset只需限定到parentWidth,即xOffsetLimit=parentWidth。所以xOffsetLimit得取图片和区域宽度中较小的那个。

    ii)显示完全的情况,我们要考虑,整个区域(我们这里以半个屏幕为例),最多能绘制几个完整图形?

      此时很自然的,我们会想到,最多能绘制多少,那就parentWidth(区域宽度)/ sourceWidth(完整图形宽度)= maxCompleteNum(最大完整图形个数)

      但是!!

      同时我们也得考虑到,由于屏幕是在不断移动中的,可能在某一刻,绘制的完整图形个数是maxCompleteNum(如上图,为2),

      但是继续右移时,最右边的完整图形超出了屏幕,而中间新绘制出来的图形却还没有达到完整,此时屏幕中实际存在的完整图形只有1个!!

      所以,这里我们还需要一个变量,记录此时此刻在屏幕中所能绘制的完整图形个数completeNumBeforeOut。

   3)通过以上分析,我们知道屏幕中最多出现maxCompleteNum个完整图形,某个时刻绘制的完整图形个数应为completeNumBeforeOut。但是屏幕一直在绘制,我们怎么知道completeNumBeforeOut的值?

      此时,我们得先有个变量drawnNum,即从开始到某个时刻,已经绘制的完整图形的个数。每当xOffset加完speed,都会去判断是否超过了xOffsetLimit,如果超过了,在将xOffset置回1的同时,也会让drawnNum加1.

      但由于最多只能绘制maxCompleteNum个完整图形,所以当drawnNum加完1之后,还得将其限制在【0,maxCompleteNum】。

      由于drawnNum是从开始到现在所绘制的完整图形个数,所以它是一直递加直到最大值,那么当前这个时候,到底有几个完整图形显示在屏幕中?

      此时得用循环来判断了。如果xOffset+ i*sourceWidth <= screenWidth(屏幕宽度),表示还没有超过屏幕,即i对应的这个图形还是完整的,所以此时completeNumBeforeOut可加1.其中i的范围是【1,drawnNum】

   4)此外,我们还得记录当前这个时刻,绘制的图形是否已经超出了屏幕,即该有个变量hasExist。

      hasExist = xOffset+drawnNum*sourceWidth > screenWidth。这个应该不难理解了,需要注意的是,这里表达式及上一个表达式,都是假定xOffset是相对屏幕中间而言的,事实上计算式得加上screenWidth/2。

 

   5)目前位置,变量差不多都齐全了,我们开始进行绘制。

      先判断当前屏幕中是否有完整图形存在,即completeNumBeforeOut,如果大于0,则在相应位置绘制出相应的个数。

      接着将从中间新移动出的图形绘制出来,其宽度为xOffset。

      最后,得用到上面的hasExist进行判断。即如果hasExist为false,那么还没有超出屏幕,则不进行操作。

        如果hasExist为true,即已经超过了屏幕了,那么绘制最右边的图形。

  6)以上流程下来,看起来挺不错的,事实上还存在一个挺大的问题。

    由于xOffset每次都是加speed,所以当xOffset达到最大值的时候,往往不会等于xOffsetLimit,而是

      maxOffsetCount(xOffset超过limit前可添加speed的最大次数) = (xOffsetLimit - 1) / 2;

      maxOffset(xOffset的最大值) = 1+ maxOffsetCount * speed;

      在以上判断是否超出屏幕的判断总,我们是假定xOffset + drawnNum * sourceWidth > screenWidth一定会满足条件。

      但事实上,由于drawnNum存在最大值maxCompleteNum,而xOffset也存在最大值maxOffset,存在这样一种情况,即maxOffset + maxCompleteNum * sourceWidth任然还差一些,不能超过screenWidth。

      (以sourceWidth=147,parentWidth=720为例,此时最多可绘制1个完整图形,最大偏移量为136,而要超出屏幕xoffset必须要达到139.5 。因为136在加上一个speed(15)就又还原了,所以此时不可能会超出屏幕)

      为了避免以上情况,当判断maxOffset + maxCompleteNum * sourceWidth < screenWidth时,得使maxCompleteNum加上1!!

 

之后附上源码

 

posted on 2015-12-02 13:51  Ivan Aldrich  阅读(485)  评论(0编辑  收藏  举报