最近项目主要是做一个类似wps文档阅历的功能,以列表的形式显示文档,并且需要实现缩放平移。而网上关于此类功能的实现主要是通过自定义的listview实现的,类名为ZoomListView。

  网上的zoomlistview模板大多是一套,主要核心代码就是以下:

 @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        if (mScaleFactor == 1.0f) {
            mPosX = 0.0f;
            mPosY = 0.0f;
        }
        canvas.translate(mPosX, mPosY);
        canvas.scale(mScaleFactor, mScaleFactor);
        super.dispatchDraw(canvas);
        canvas.restore();
        invalidate();
    }

  该函数主要实现了对整体列表的缩放和平移。但是mPosY的值影响了判断当前显示的第一个item,而且当你惯性滑动时,永远滑不到底。所以如何解决这两个问题呢。

  解决第一个问题很简单,canvas.translate(mPosX,0),就可以了。而此时由于没有了纵向的平移,列表肯定会显示不全了。

  如何解决列表显示不全的问题呢。究其原因是因为滚动条滚不到底的原因。由于此时网上也没有这方面的资料,写ZoomListView的大佬也找不到本人,后来的人估计也是copy然后写文章的,问他们其实也都不知道。于是我决定自己看源码解决问题。

  根据代码跟踪发现了滚动的时候会调用trackMotionScroll函数,其中有这么一串代码:

        final boolean cannotScrollDown = (firstPosition == 0 &&
                firstTop >= listPadding.top && incrementalDeltaY >= 0);
        final boolean cannotScrollUp = (firstPosition + childCount == mItemCount &&
                lastBottom <= getHeight() - listPadding.bottom && incrementalDeltaY <= 0);

        if (cannotScrollDown || cannotScrollUp) {
            return incrementalDeltaY != 0;
        }

   其中cannotScrollUp就是不能向下滚动。看这个式子有没有发现什么问题,判断不能向下滚动的条件是lastBottom <= getHeight() - listPadding.botoom。于是我突发奇想设置paddingbottom的值就好了。设置paddingbottom为对应值后,发现真的可以滚动了,太棒了,问题基本上解决了。

  后来发现又有bug了,在paddingbottom部分居然不能滚动!不能滚动!不能滚动!!!哎,详细也正常,padingbottom本来就是不能滚动的,这时候怎么办呢。然后调试进入ontouchevent函数中,发现触摸该区域其实还是可以会调touchevent事件的。于是我有想到了一个奇妙的方法去解决问题。自己在touchevent中先对ev的y值进行位移,这样该区域就可以滚动了。

  终于解决问题了,经过这一次我明白了一个道理,当你遇到问题的时候,一定要解析问题所在,然后跟踪深入源码,只有知道底层代码才能更好的解决问题。

  想要源码的可以评论,在这里就不贴了。

  这几天又来了一个需求,如何保证中心缩放。这个就有点难了,如何在这个类上修改呢。第一步就是需要优化在双指缩放的时候不能平移画布。这个问题横向的时候好解决,只要判断是否是单指就行了。横向的时候就需要禁用滚动来判断,函数setenable。

  第二步就是找推导公式,进行算出新的平移。以下是纵向调整平移的代码。

 

            //求y0的推导公式,view是对应中心点item的view。
            /*
            (view.top + y0)*mOldScaleFactor = cy;
            (view.top(新)+y0)*mScaleFactor = cy;
            先求出view.top(新),然后计算出view.top(新)与view.top之间的差。
            然后求得firstitem的view.top(新的值),最后通过setSelectionFromTop滚动到对应区域
             */
            int iFirstItem = getFirstVisiblePosition();
            int iFirstViewTop = 0;
            for(int iCnt=0;iCnt<getChildCount();++iCnt)
            {
                View view = getChildAt(iCnt);
                if(0==iCnt)
                {
                    iFirstViewTop = view.getTop();
                }
                int iViewTop = (int)(view.getTop()*mOldScaleFactor);
                int iViewBottom = (int)(view.getBottom()*mOldScaleFactor);
                if(iViewTop<=cy && cy<=iViewBottom)
                {
                    int iNewViewTop = (int)(cy/mScaleFactor - (cy/mOldScaleFactor-iViewTop));
                    int iNewFirstViewTop = iFirstViewTop + (iNewViewTop-iViewTop);
                    if(0==iFirstItem && iNewFirstViewTop>=0)
                    {
                        iNewFirstViewTop = 0;
                    }
                    setSelectionFromTop(iFirstItem,iNewFirstViewTop);
                    break;
                }
            }

主要要算出当前点会平移多少,然后通过setSelectionFromTop来校准。终于经过千辛万苦,把缩放的listview完成的总算可以了!!!