3

So the problem I am facing right now is the following, I want to create a fisheye view (so a view which contains items while the item in the middle is bigger then the other ones like you will see in the MAC OS itembar or somewhere else).

So far I have extended the HorizontalScrollView to achieve that and after some testing everything seemed to work fine, so when moving the scrollview the items are updated properly depending on their position.

However there is one problem that occurs if the scrollview "bounces" against its borders. So if the ScrollView moves to fast the "getScrollX()" will give me a value that is smaller 0 or bigger then the max bounds. After that happens the items are no longer resized, which is quite strange.

I checked my code and the resize methods of my items are called, yet i don´t know why the items are no longer updated.

The ScrollView class looks like the following

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());
        }
    }
}

Don´t mind some of the unused variables as they are intended to be used later to realize an autoscroll feature once the view itself has finished scrolling (so that the currently closed item will always be in the middle up on letting go of the scrollview).

The View requires to actually be filled with "FishEyeItem"s, which are then used to load images and resize the content. Those Items are loaded during runtime after I have gained the list of items that i need to display.

The FishEyeItem code is the following.

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());
        }
    }
}

So essentially everytime the onScrollChanged() is called, the image of an item will be loaded or recycled (both are in running in a async task so they dont block the scrolling and GUI). Also the size of an item will be determined if it is a certain distance away from the middle of the scrollview.

Like I said the Resize() method always gets called (thats why the system.out is there) but when "bouncing" against the borders the items are no longer resized.

So I am guessing the problem is somewhere in the HorizontalScrollView class itself, e.g. a certain flag gets set when "bouncing" against the borders.

EDIT:

So okay I was able to prevent that the items were not able to be updated anymore by simply checking the getScrollX() in the onscrollchanged() and returning if that value is <= 0 or if the value is >= the max bounds. However this still does not explain the fact that the items were not updated anymore when "bouncing" against the borders.

Goo
  • 1,318
  • 1
  • 13
  • 31
DokutoMekki
  • 491
  • 4
  • 17

0 Answers0