3

所以我现在面临的问题如下,我想创建一个鱼眼视图(所以一个包含项目的视图,而中间的项目比你在 MAC OS 项目栏或其他地方看到的其他项目更大)。

到目前为止,我已经扩展了 Horizo​​ntalScrollView 来实现这一点,经过一些测试,一切似乎都很好,所以在移动滚动视图时,项目会根据它们的位置正确更新。

但是,如果滚动视图“反弹”其边界,则会出现一个问题。因此,如果 ScrollView 快速移动,“getScrollX()”会给我一个小于 0 或大于最大界限的值。在那之后,项目不再调整大小,这很奇怪。

我检查了我的代码并调用了我的项目的调整大小方法,但我不知道为什么项目不再更新。

ScrollView 类如下所示

public class HorizontalFishEyeView extends HorizontalScrollView
{
    //****************************************************************************************************
    //                                  enums
    //****************************************************************************************************  
    public enum ONTOUCHEND
    {
        //-----undefined value
        NONE,
        //-----meaning to keep scrolling after the touch event ended
        SCROLL_ON_END,
        //-----meaning to continue to the next base after the touch event ended
        CONTINUE_ON_END,
        //-----meaning to switch the element after the on touch event ended
        CHANGE_ELEMENT_ON_END
    }


    public enum MODE
    {
        //-----none mode meaning the view is not scrolling or doing the finish animation, thus being idle
        NONE,
        //-----scroll meaning the view is scrolling after an accelleration
        SCROLL,
        //-----finish meaning the view is doing the finish animation to move to the actual element
        FINISH,
    }

    //****************************************************************************************************
    //                                  variables
    //****************************************************************************************************
    //-----determines if the view will continue when the finish animation is played
    private boolean m_bContinueOnClick = false;

    //-----time for the scroll animation
    private long m_nScrollAnimationTime = 0;

    //------the multiplier to be used for the velocity on initaial start
    private float m_fVelocityMultiplier = 1.0f;

    //-----the direction of the velocity however the reverse value to use it in conjunction with the decrement
    private int m_nVelocityDirectionReverse = 0;

    //------the velocity provided when the event has ended
    private float m_fVelocity = 0.0f;

    //-----determines hwo much the velocity decreases per millisecond
    private float m_fVelocityDecrement = 0.001f; 

    //-----time when the touch event was started
    private long m_nStartTime = 0;

    //-----the x position of the touch event
    private float m_nXPosition = -1.0f;

    //-----determines when the animation for moving shall be canceled
    private final float m_fVelocityThreshold = 0.25f; 

    //-----determines the time, e.g the start time of the animation and stores the time each time the draw method is called
    //-----while the finish animation is in progress
    private long m_nFinishAnimationTime = 0;

    //-----determines how much pixel the layout will be moved for each millisecond passed,
    //-----while the finish animation is playing
    private double m_dFinishAnimationIncrements = 0.0;

    //-----the actually duration of the finish animation, this value is dependent of the difference distance
    //-----which the view has to be moved, so at max this will bei 0.5 times m_nFinishAnimationTime
    private int m_nFinishAnimationDuration = 0;

    //-----determines the distance which the view has to be moved in order to set the selected element into focus
    private int m_nFinishRemainingDiff = 0;

    //-----the position which the view will have as its left margin, 
    //-----this value us determined when the user lets go of the view
    private int m_nFinishTargetPosition = 0;

    //-----the animation time the finish animation when the user lets go of the view
    private int m_nAnimationTime = 0;

    //-----the position of the element which is closest to the selector, thus it actually is the selected element
    private FishEyeItem m_nClosestElement = null;






    //-----scalefactor used to calculate the min item size, thus m_nItemSizeMin = nItemSize * m_fItemSizeMaxScale
    private float m_fItemSizeMinScale = -1;

    //-----the size of the image of the item when not selected
    private int m_nItemSizeMin = -1;

    //-----scalefactor used to calculate the max item size, thus m_nItemSizeMax = nItemSize * m_fItemSizeMaxScale
    private float m_fItemSizeMaxScale = -1;

    //-----the size of the image of the item when selected
    private int m_nItemSizeMax = -1;

    //-----the difference in item size between the max and the min value
    private int m_nItemSizeDiff = -1;

    //-----determines at which distance the item size will always be min
    private int m_nMaxDiff = 0;

    //-----the middel point of the view, used to determine the distance of an item and thus its size
    private int m_nReferenceX = 0;



    //-----event listener attached to this view
    protected AnimationEventListener m_oEventListener;

    //-----this factor is multiplied by the velocity up on the UP event to determine the remaining scroll
    private float m_fVelocityScaleFactor = 0.25f;

    //-----the mode in which the fisheyeview currently is
    private MODE m_eMode = MODE.NONE;

    //-----the reference to the one and only child in the scrollview, as it should be
    private LinearLayout m_oChild = null; 

    //-----number of items whose bitmap will still be available even if they are not visible
    private int m_nItemBuffer = 2;

    //-----activity to use
    private Activity m_oActivity = null;

    //-----scalefactor to use
    private float m_fScaleFactor = 1.0f;

    //-----determines if the itemsize is stable thus each item is the same size, used to prevent unnecessary calculations 
    private boolean m_bItemSizeStable = false;

    //****************************************************************************************************
    //                                  constructor
    //****************************************************************************************************
    public HorizontalFishEyeView(Context context) 
    {
        super(context);
    }

    public HorizontalFishEyeView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }


    //****************************************************************************************************
    //                                  public
    //****************************************************************************************************
    /**
     * this method will set up the view before it is used, thus it needs to be called before
     * @param oActivity
     * @param fItemSizeMinScale
     * @param fItemSizeMaxScale
     * @param fScaleFactor
     * @param bItemSizeStable
     */
    public void Initialize(Activity oActivity, float fItemSizeMinScale, float fItemSizeMaxScale, float fScaleFactor, boolean bItemSizeStable)
    {
        try
        {
            m_oActivity = oActivity;
            m_nReferenceX = (int)(getWidth()*0.5f);
            m_fItemSizeMaxScale = Math.min(1.0f, Math.max(0.0f, fItemSizeMaxScale));
            m_fItemSizeMinScale = Math.min(1.0f, Math.max(0.0f, fItemSizeMinScale));
            m_bItemSizeStable = bItemSizeStable;
            m_fScaleFactor = fScaleFactor;
        }
        catch(Exception e)
        {
            Log.d("Initialize", e.toString());
        }
    }


    public void Clear()
    {
        try
        {
            if(m_oChild!=null)
            {
                for(int i=0;i<m_oChild.getChildCount();i++)
                {
                    View oChild = m_oChild.getChildAt(i);
                    if(oChild instanceof FishEyeItem)
                    {
                        NetcoMethods.RecycleImageView(((FishEyeItem)oChild).GetImageView());
                    }
                }
                m_oChild.removeAllViews();
            }
        }
        catch(Exception e)
        {
            Log.d("Clear", e.toString());
        }
    }


    public void AddItem(FishEyeItem oItem, LinearLayout.LayoutParams oParams)
    {
        try
        {
            if(m_oChild!=null)
            {
                m_oChild.addView(oItem, oParams);
            }
        }
        catch(Exception e)
        {
            Log.d("AddItem", e.toString());
        }
    }


    public MODE GetMode()
    {
        return m_eMode;
    }


    public void Reinitialize()
    {

    }


    public void Deinitialize()
    {

    }

    /**
     * adds an animation listener to the list
     * @param listener
     */
    public void SetAnimationEventListener(AnimationEventListener listener) 
    {
        m_oEventListener = listener;
    }


    public void ScrollTo()
    {
        try
        {

        }
        catch(Exception e)
        {
            Log.d("ScrollTo", e.toString());
        }
    }


    public LinearLayout GetChild()
    {
        return m_oChild;
    }

    //****************************************************************************************************
    //                                  private
    //****************************************************************************************************
    /**called when the size was calculated*/
    private void SizeCalculated(Object o) 
    {
        try
        {
            if(m_oEventListener!=null)
            {
                m_oEventListener.AnimationEvent(o);
            }
        }
        catch(Exception e)
        {
            Log.d("AnimationEndEvent", e.toString());
        }   
    }

    /**
     * calculates the sizes for an item, if m_bItemSizeStable is set to true this will only be done once
     * @param nItemSize, the size of the item which will be used
     */
    private void CalulateItemSize(int nItemSize)
    {
        try
        {
            if(!m_bItemSizeStable)
            {
                m_nItemSizeMax = (int)(nItemSize * m_fItemSizeMaxScale);
                m_nItemSizeMin = (int)(nItemSize * m_fItemSizeMinScale);
                m_nItemSizeDiff = m_nItemSizeMax - m_nItemSizeMin;
                m_nMaxDiff = nItemSize*2;
            }
            else if(m_nItemSizeMax==-1)
            {
                m_nItemSizeMax = (int)(nItemSize * m_fItemSizeMaxScale);
                m_nItemSizeMin = (int)(nItemSize * m_fItemSizeMinScale);
                m_nItemSizeDiff = m_nItemSizeMax - m_nItemSizeMin;
                m_nMaxDiff = nItemSize*2;
            }
        }
        catch(Exception e)
        {
            Log.d("CalculateItemSize", e.toString());
        }
    }

    /**
     * this method will Resize and item in the view depending on its position
     * @param oItem the item which shall be resized
     * @param nDiff the distance of the item from the middle of he view
     * @param nCurrentClosestDiff the currently know closest distance, if the item is closer the given nDiff will be used
     */
    private void DeterminenSize(FishEyeItem oItem, int nDiff, int nCurrentClosestDiff)
    {
        try
        {
            if(oItem!=null)
            {
                CalulateItemSize(oItem.getWidth());
                //-----check if the item can be resized
                if(oItem.GetCanBeResized())
                {
                    //System.out.println("Item is "+ oItem.GetImagePath());
                    //System.out.println("Item Diff is "+ nDiff);
                    //-----items is in range
                    if(nDiff<m_nMaxDiff)
                    {
                        //-----determine whether this element is closer to the selector then the previously known
                        if(nCurrentClosestDiff==-1)
                        {
                            nCurrentClosestDiff = nDiff;
                            m_nClosestElement = oItem;
                            SizeCalculated(m_nClosestElement);
                        }
                        else
                        {
                            if(nDiff<nCurrentClosestDiff)
                            {
                                nCurrentClosestDiff = nDiff;
                                m_nClosestElement = oItem;
                                SizeCalculated(m_nClosestElement);
                            }
                        }

                        //-----get the new size
                        float fRelative = 1.0f - (float)nDiff/(float)m_nMaxDiff;
                        int nNewItemSize = m_nItemSizeMin + (int)(fRelative * m_nItemSizeDiff);

                        //-----set the new size
                        oItem.Resize(nNewItemSize, nNewItemSize);
                        oItem.SetIsInRange(true);
                    }
                    else
                    {
                        //----if the item is now out of range set it to the minimum size
                        if(oItem.GetIsInRange())
                        {
                            //-----set the minimum size
                            oItem.Resize(m_nItemSizeMin, m_nItemSizeMin);
                            oItem.SetIsInRange(false);
                        }
                    }
                }   
            }
        }
        catch(Exception e)
        {
            Log.d("DeterminenSize", e.toString());
        }
    }


    //****************************************************************************************************
    //                                  overrides
    //****************************************************************************************************
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) 
    {
        super.onScrollChanged(l, t, oldl, oldt);
        try
        {       
            if(m_eMode == MODE.FINISH)
            {

            }
            else
            {
                //------get the top element which must be a linear layout
                if(m_oChild!=null)
                {
                    m_oChild.setWillNotDraw(false);

                    FishEyeItem oFishEyeItem = null;
                    View oChildView = null;
                    ImageView oImage = null;
                    String cFilename = null;
                    int nPositionStart = 0;
                    int nPositionEnd = 0;
                    int nItemSize = 0;
                    int nScroll = getScrollX();
                    int nBoundEnd = getWidth();
                    int nItemPosition = 0;
                    int nCurrentClosestDiff = -1;


                    System.out.println(nScroll);

                    for(int i=0;i<m_oChild.getChildCount();i++)
                    {
                        oChildView = m_oChild.getChildAt(i);
                        //-----check if the child is of a certain type
                        if(oChildView instanceof FishEyeItem)
                        {
                            oFishEyeItem = (FishEyeItem)oChildView;
                            nItemSize = oFishEyeItem.getWidth();
                            nPositionStart = i * nItemSize;
                            nPositionEnd = nPositionStart + nItemSize;
                            oImage = oFishEyeItem.GetImageView();
                            cFilename = oFishEyeItem.GetImagePath();    
                            //-----check if the item is in visible area
                            if(oImage!=null)
                            {
                                //-----image is in visible area
                                if(nPositionEnd>=nScroll - (m_nItemBuffer * nItemSize) && nPositionStart - (m_nItemBuffer * nScroll)<=nBoundEnd)
                                {
                                    //-----check if image needs to be loaded
                                    if(!oFishEyeItem.GetIsImageLoaded())
                                    {
                                        oFishEyeItem.SetIsImageLoaded(true);
                                        new DownloadTaskImage(m_oActivity, 
                                                oImage, 
                                                cFilename, 
                                                nItemSize, 
                                                nItemSize, 
                                                m_fScaleFactor, 
                                                POWERROUNDMODES.ROUND).execute((Void)null);
                                    }

                                    //-----get the item position in the fisheyeview
                                    nItemPosition = nPositionStart - nScroll + (int)(nItemSize*0.5f);
                                    DeterminenSize(oFishEyeItem, Math.abs(m_nReferenceX - nItemPosition), nCurrentClosestDiff);
                                }
                                else
                                {
                                    //-----check if an image can be recycle
                                    if(oFishEyeItem.GetIsImageLoaded())
                                    {
                                        oFishEyeItem.SetIsImageLoaded(false);
                                        new RecycleTaskImage(oImage).execute((Void)null);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        catch(Exception e)
        {
            Log.d("onScrollChanged", e.toString());
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent oEvent) 
    {
        super.onTouchEvent(oEvent);
        try
        {
            switch(oEvent.getAction())
            {
                case MotionEvent.ACTION_DOWN:
                    break;

                case MotionEvent.ACTION_UP:
                    break;

                case MotionEvent.ACTION_MOVE:
                    break;

                default:
                    break;
            }
        }
        catch(Exception e)
        {
            Log.d("onTouchEvent", e.toString());
        }
        return true;
    }

    protected void onFinishInflate()
    {
        super.onFinishInflate();
        try
        {
            m_oChild = (LinearLayout)getChildAt(0);
        }
        catch(Exception e)
        {
            Log.d("onFinishInflate", e.toString());
        }
    }
}

不要介意一些未使用的变量,因为一旦视图本身完成滚动,它们将在以后用于实现自动滚动功能(这样当前关闭的项目将始终在放开滚动视图的中间) .

视图需要实际填充“FishEyeItem”,然后用于加载图像和调整内容大小。在我获得需要显示的项目列表后,这些项目会在运行时加载。

FishEyeItem 代码如下。

public class FishEyeItem extends RelativeLayout
{
    //****************************************************************************************************
    //                                  variables
    //****************************************************************************************************
    //-----determines if this item can be resized
    private boolean m_bCanBeResized = false;

    //-----path to the image of this fisheye items image
    private String m_cImagePath = null;

    //-----determines if this item is in range for the fisheye calculation
    private boolean m_bIsInRange = true;

    //-----determines if the image is loaded already, thus occupying memory
    private boolean m_bIsImageLoaded = false;

    //-----id of the image4view holding the image
    private int m_nImageViewID = -1;

    //-----the id of the view in this view which is responsible for resizing 
    private int m_nResizeViewID = -1;


    //****************************************************************************************************
    //                                  constructor
    //****************************************************************************************************
    public FishEyeItem(Context context) 
    {
        super(context);
    }

    public FishEyeItem(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }


    //****************************************************************************************************
    //                                  setter
    //****************************************************************************************************
    public void SetCanBeResized(boolean bValue)
    {
        m_bCanBeResized = bValue;
    }

    public void SetImagePath(String cValue)
    {
        m_cImagePath = cValue;
    }

    public void SetIsInRange(boolean bValue)
    {
        m_bIsInRange = bValue;
    }

    public void SetIsImageLoaded(boolean bValue)
    {
        m_bIsImageLoaded = bValue;
    }

    public void SetImageViewID(int nValue)
    {
        m_nImageViewID = nValue;
    }

    public void SetResizeViewID(int nValue)
    {
        m_nResizeViewID = nValue;
    }


    //****************************************************************************************************
    //                                  getter
    //****************************************************************************************************
    public boolean GetCanBeResized()
    {
        return m_bCanBeResized;
    }

    public String GetImagePath()
    {
        return m_cImagePath;
    }

    public boolean GetIsInRange()
    {
        return m_bIsInRange;
    }

    public boolean GetIsImageLoaded()
    {
        return m_bIsImageLoaded;
    }

    public int GetImageViewID()
    {
        return m_nImageViewID;
    }

    public int GetResizeViewID()
    {
        return m_nResizeViewID;
    }

    public ImageView GetImageView()
    {
        ImageView oView = null;
        try
        {
            oView = (ImageView)findViewById(m_nImageViewID);
        }
        catch(Exception e)
        {
            Log.d("GetImageView", e.toString());
        }
        return oView;
    }

    //****************************************************************************************************
    //                                  getter
    //****************************************************************************************************
    public void Resize(int nWidth, int nHeight)
    {
        try
        {
            View oView = findViewById(m_nResizeViewID);
            if(oView!=null)
            {
                System.out.println("Resizing Item" + m_cImagePath);

                //-----set the minimum size
                RelativeLayout.LayoutParams oParams = (RelativeLayout.LayoutParams)oView.getLayoutParams();
                oParams.width = nWidth;
                oParams.height = nHeight;
                oView.setLayoutParams(oParams);
            }
        }
        catch(Exception e)
        {
            Log.d("Resize", e.toString());
        }
    }
}

因此,基本上每次调用 onScrollChanged() 时,都会加载或回收项目的图像(两者都在异步任务中运行,因此它们不会阻塞滚动和 GUI)。如果项目距离滚动视图的中间有一定距离,也将确定项目的大小。

就像我说的那样,总是调用 Resize() 方法(这就是 system.out 存在的原因),但是当“反弹”到边界时,项目不再调整大小。

所以我猜测问题出在Horizo​​ntalScrollView 类本身的某个地方,例如,在“弹跳”边界时设置了某个标志。

编辑:

好吧,我可以通过简单地检查 onscrollchanged() 中的 getScrollX() 并返回该值是否为 <= 0 或该值是否 >= 最大边界来防止项目无法再更新。然而,这仍然不能解释项目在“反弹”边界时不再更新的事实。

4

0 回答 0