Search Unity

Optimized ScrollView Adapter + Playmaker support

Discussion in 'Assets and Asset Store' started by xucian, Apr 1, 2016.

  1. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    No problem, I'll provide more and ask a few questions in the following weeks.


    I am using Unity 2019.1.12f1
    As for the OSA version, I am using the latest one on the AssetStore, 'Tools -> OSA -> About' indicate version 5.0.


    No, it's not an edge case, it is very easily reproducible, I'd say about 95-98% of the time.
    About 'BUG 3', if elasticity is disabled then the scrollbar behaves normally.


    No problem, there's no hurry, and I prefer things well done than things done fast.
     
  2. anil_unity385

    anil_unity385

    Joined:
    Feb 7, 2019
    Posts:
    18
    @thefallengamesstudio

    Hi there, I am using the OSA and it's really good. I have a question though. I have a horizontal scroll view adapter with a Snapper8 and Scrolbar fixer. I have an issue where when I swipe the bar past the extremeites on either end that the SnappingEndedOrCancelled action event is not fired. So it means my data can go out of sync.

    What is the best way to know when the scroll view has finished it's update and has settled?
     
    Last edited: Aug 20, 2019
  3. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    I think "SnappingEndedOrCancelled" event will be removed or at least will have its documentation updated, as it's not very useful, mostly because the majority of users will use elastic movement and when they pull the content outside its bounds the "release from pull" effect is actually working there, while the snapper isn't involved at all - they serve slightly different purposes.
    What are you trying to achieve, more specifically? Maybe I can think of a simpler solution.
    If you want to execute some code when the scroll view is not moving, there are multiple properties you should check for, in your case:
    OSA.Velocity,
    OSA.IsDragging (pointer on content),
    ScrollbarFixer8.IsDragging (pointer on scrollbar),
    Snapper8.SnappingInProgress.
    If all of these are zero/false, you're guaranteed the scroll view isn't moving the current frame.

    But again, I might give you a simpler solution if I'd know what you need to achieve exactly.

    -- Lucian
     
  4. anil_unity385

    anil_unity385

    Joined:
    Feb 7, 2019
    Posts:
    18
    Hi Lucian. Specifically, I want to know when the scroll bar has stopped scrolling and know the ID of the prefab that is currently in the central position of the scroll bar.

    Below is an image of my UI. To the right are a number of other elements. When i scroll the view to see the others, when the bar settles i can get the ID of the selected entry. But if I swipe fully to the left past the extents, i cant seem to get the ID.

    So all I want is a way to poll the id of the element currently in view once the scroll view stops, and ideally through a callback rather than an update method, though i could use an update method also.

    UI.JPG
     
    Last edited: Aug 20, 2019
  5. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hm, usually I see users want to continuously update the appearance of the "middle" item, like I see in your image, but it's independent of when the snapping ends.

    You'll have a FocusedIndex = -1, which you reset to -1 each time items change. Override ChangeItemsCount() and do it there.
    Make Snapper8.GetMiddleVH() method public.
    In Update, you'll get the middle item from Snapper (it already calculates it correctly based on the pivots you set in inspector. It wraps the complexities of calling OSA.GetViewsHolderClosestToViewportLongitudinalNormalizedAbstractPoint for you).
    If the middle item's ItemIndex is different than FocusedIndex, call a method ChangeFocusedItem(vh.ItemIndex) where you execute your logic.

    Here's the code (tested):
    Code (CSharp):
    1.  
    2.         Snapper8 _Snapper; // retrieve this on Start/Awake
    3.         int _FocusedIndex = -1;
    4.  
    5.         protected override void Update()
    6.         {
    7.             base.Update();
    8.  
    9.             float _;
    10.             var vh = _Snapper.GetMiddleVH(out _);
    11.             int newIndex;
    12.             if (vh == null)
    13.                 newIndex = -1;
    14.             else
    15.                 newIndex = vh.ItemIndex;
    16.  
    17.             if (_FocusedIndex != newIndex)
    18.                 ChangeFocusedItem(newIndex);
    19.         }
    20.  
    21.         public override void ChangeItemsCount(ItemCountChangeMode changeMode, int cellsCount, int indexIfAppendingOrRemoving = -1, bool contentPanelEndEdgeStationary = false, bool keepVelocity = false)
    22.         {
    23.             base.ChangeItemsCount(changeMode, cellsCount, indexIfAppendingOrRemoving, contentPanelEndEdgeStationary, keepVelocity);
    24.  
    25.             if (_FocusedIndex != -1)
    26.                 ChangeFocusedItem(-1);
    27.         }
    28.  
    29.         void ChangeFocusedItem(int newFocusedItemIndex)
    30.         {
    31.             _FocusedIndex = newFocusedItemIndex;
    32.             for (int i = 0; i < VisibleItemsCount; i++)
    33.             {
    34.                 var vh = GetItemViewsHolder(i);
    35.                 if (vh.ItemIndex == _FocusedIndex)
    36.                 { /* code for newly focused item */ }
    37.                 else
    38.                 { /* code for newly surrounding items */ }
    39.             }
    40.  
    41.             // GetItemViewsHolder(int) takes the "index in the visible items array", i.e. only the items you actually see, whose count is given by VisibleItemsCount.
    42.             // You can use GetItemViewsHolderIfVisible(int) to get only the focused item alone, by ItemIndex, if you don't want to execute any code on surrounding items and thus, don't need to iterate through them
    43.         }
    Let me know how it works out.

    -- Lucian
     
  6. anil_unity385

    anil_unity385

    Joined:
    Feb 7, 2019
    Posts:
    18
    Where should the code go? GridAdapter.cs?

    I have made the Snapper8.GetMiddleVH() public.

    EDIT: Ah yeah I think I have it working now. Thank you Lucian! Keep up the good work and appreciate the fast response.
     
    Last edited: Aug 21, 2019
  7. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    In your class that implements OSA.

    You're extending GridAdapter (for grids) or OSA directly (for lists)?
    Because from what I see you have a horizontal List of items.
    I'm asking just to make sure you're using the right kind of base class for your needs.
    For lists, you'd create an implementation similar to SimpleExample.cs, not GridExample.cs.

    -- Lucian
     
    anil_unity385 likes this.
  8. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    Have you had a chance to check on the problems I've reported last week?


    Also, I told you I would have some questions, here is the first one:
    I have noticed that when you remove all the elements of the ScrollView, it will destroy all the views (somewhere, lost in the very middle of 'ChangeItemsCountInternal()' there is a check on 'newCount' and everything is destroyed when it's 0).
    I agree that in some situation we would like to destroy all the views, but there are other situations where it will only generate garbage endlessly.
    I am in one of these situations, I have a ScrollView which is constantly filled/emptied, and I hoped to reduce the garbage generated by using OSA.

    Is there a way to clear the ScrollView without actually destroying the views in order to avoid garbage collection?
     
  9. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    Didn't have the chance yet, but it's the first thing on the list. I'll let you know when I'll have an update for it. Should be around these days.

    As for preserving the cached items even when emptying the list, I think the only maintainable solution is to have a bool parameter in BaseParams.Optimization which decides whether that happens or not.
    I'll do that now.

    So here's what I'll add in the next minor release:
    Additions to BaseParams.Optimization class:
    Code (CSharp):
    1.  
    2.             [SerializeField]
    3.             [Tooltip("When the list becomes empty, should the currently cached item views be kept (True) or destroyed in order to start with a fresh state (False)? False by default")]
    4.             bool _KeepItemsPoolOnEmptyList = false;
    5.             /// <summary>When the list becomes empty, should the currently cached item views be kept (True) or destroyed in order to start with a fresh state (False)? False by default</summary>
    6.             public bool KeepItemsPoolOnEmptyList { get { return _KeepItemsPoolOnEmptyList; } set { _KeepItemsPoolOnEmptyList = value; } }
    7.  
    8.             [SerializeField]
    9.             [Tooltip("When the ScrollView is rebuilt (as when its size changes), should the currently cached item views be kept (True) or destroyed in order to start with a fresh state (False)? False by default")]
    10.             bool _KeepItemsPoolOnLayoutRebuild = false;
    11.             /// <summary>When the ScrollView is rebuilt (as when its size changes), should the currently cached item views be kept (True) or destroyed in order to start with a fresh state (False)? False by default</summary>
    12.             public bool KeepItemsPoolOnLayoutRebuild { get { return _KeepItemsPoolOnLayoutRebuild; } set { _KeepItemsPoolOnLayoutRebuild = value; } }
    OSAInternal.cs::ChangeItemCountInternal(..) method change:
    This:
    Code (CSharp):
    1.             // If the itemsCount is 0, then it makes sense to destroy all the views, instead of marking them as recyclable. Maybe the ChangeItemCountTo(0) was called in order to clear the current contents
    2.             if (newCount == 0)
    3.             {
    4.                 ClearVisibleItems();
    5.                 ClearCachedRecyclableItems();
    6.             }
    7.             else if (recycleAllViewsHolders)
    8.                 RecycleAllVisibleViewsHolders();
    9.  
    Becomes:
    Code (CSharp):
    1.             if (newCount == 0)
    2.             {
    3.                 recycleAllViewsHolders = _Params.optimization.KeepItemsPoolOnEmptyList;
    4.                 if (!recycleAllViewsHolders)
    5.                 {
    6.                     // If the itemsCount is 0, then in most cases it makes sense to destroy all the views, instead of marking them as recyclable.
    7.                     // Maybe the ChangeItemCountTo(0) was called in order to clear the current contents
    8.                     ClearVisibleItems();
    9.                     ClearCachedRecyclableItems();
    10.                 }
    11.             }
    12.  
    13.             if (recycleAllViewsHolders)
    14.                 RecycleAllVisibleViewsHolders();

    OSA.cs::RebuildLayoutDueToScrollViewSizeChange() method change:
    This:
    Code (CSharp):
    1.             ClearCachedRecyclableItems();
    Becomes:
    Code (CSharp):
    1.             if (!_Params.optimization.KeepItemsPoolOnLayoutRebuild)
    2.                 ClearCachedRecyclableItems();
    You'll want to set both of those new properties to True in inspector (or code).
    I figured you'll also want to preserve the cached items if the ScrollView is rebuilt when it's resized, so KeepItemsPoolOnLayoutRebuild came to life.

    -- Lucian
     
  10. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    Thank you, it works pretty well, it will help a lot for optimization.
    And nice touch about 'KeepItemsPoolOnLayoutRebuild' that was going to be my next question.
     
  11. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    Hello again,


    Another question:
    I have a browsable list, when I use the up/down keys that changes the currently selected item.
    When I reach the top of the ScrollView I need to scroll one item in order to display the next item.
    Currently I use 'GetItemViewsHolderIfVisible(BrowseIndex)' to know if an item is visible or not, and then I use 'ScrollTo(BrowseIndex, Direction, Direction)' so that the item is displayed ('Direction' being either 0 or 1 depending if I'm going up or down).

    All that works pretty fine, except in some situations where the newly selected item is not completely visible.
    There are even some situation where only a pixel of the item is visible.
    In these situations, 'GetItemViewsHolderIfVisible(BrowseIndex)' will not return 'null' (as it should, because the item is partially visible).

    I could scroll whether the item is visible or not, but I do not want to do it when the item is already fully visible because I do not want the ScrollView to scroll in these situations.


    I haven't found any 'GetItemViewsHolderIfFullyVisible()' method.
    Is there an easy way to know how visible a ViewHolder is?
    I think the best thing would be to have a way to ask a view how visible it is, with a float ]0 ; 1] where 1 is fully visible.



    Unrelated: I've found a few copy/paste errors in the examples:
    - AnimatedInsertRemoveSceneEntry.OnAddItemRequested()
    - MainSceneEntry.OnAddItemRequested()
    - MultiplePrefabsSceneEntry.OnAddItemRequested()
    They all call 'base.OnRemoveItemRequested()' instead of 'base.OnAddItemRequested()'
     
  12. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    About the visibility of the items, are the views in '_VisibleItems' always ordered?
    Because if they are, checking if the item we want to display is the first one (or before) then we can scroll up and if it's the last one (or after) we can scroll down, and if it is any of the others then no need to scroll.
    It solves my problem because I don't mind to scroll if the item is already visible as long as it won't move the scrollview.
     
  13. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    Thanks for pointing those typos! Base implementations do nothing ATM, so thankfully they had no effect.

    OSA.SmoothBringToView() would be more suited for your case.
    And for knowing the normalized visibility amount of an item, I created a new method, which is now also used by SmoothBringToView.

    So here's the changes that the next version will have (1 method changed, 1 method added):
    Code (CSharp):
    1. /// <summary>
    2.         /// Similar to <see cref="SmoothScrollTo(int, float, float, float, Func{float, bool}, Action, bool)"/> (see it for more info about the other params),
    3.         /// but scrolls the content only by the minimum needed amount to make the item fully visible and
    4.         /// optionally adding some spacing as specified by <paramref name="spacingFromViewportEdge"/>
    5.         /// </summary>
    6.         /// <param name="spacingFromViewportEdge">
    7.         /// if not set, <see cref="BaseParams.ContentSpacing"/> will be used. Set to 0 to align the item's edge to the viewport's edge exactly, with no spacing
    8.         /// </param>
    9.         /// <returns>True, if the animation started. For details, see also the return value of <see cref="SmoothScrollTo(int, float, float, float, Func{float, bool}, Action, bool)"/></returns>
    10.         public virtual bool SmoothBringToView(int itemIndex, float duration, float? spacingFromViewportEdge = null, Func<float, bool> onProgress = null, Action onDone = null, bool overrideCurrentScrollingAnimation = false)
    11.         {
    12.             double signedVisibility = GetItemSignedVisibility(itemIndex, spacingFromViewportEdge);
    13.  
    14.             double spacingToUse = spacingFromViewportEdge ?? _Params.ContentSpacing;
    15.             float spacing01RelativeToViewportSize = (float)(spacingToUse / _InternalState.vpSize);
    16.  
    17.             // Has its start before the viewport's start => align the start with viewport's start
    18.             if (signedVisibility < 0d)
    19.                 return SmoothScrollTo(itemIndex, duration, 0f + spacing01RelativeToViewportSize, 0f, onProgress, onDone, overrideCurrentScrollingAnimation);
    20.  
    21.             // Same for viewport's end
    22.             if (signedVisibility > 0d)
    23.                 return SmoothScrollTo(itemIndex, duration, 1f - spacing01RelativeToViewportSize, 1f, onProgress, onDone, overrideCurrentScrollingAnimation);
    24.  
    25.  
    26.             // Fully visible
    27.             return false;
    28.         }
    29.  
    30.  
    31.         /// <summary>
    32.         /// <para>How much of the item is visible. </para>
    33.         /// <para>Returns a value in range [-1, 1], where negative values mean the item is partially/completely before the viewport, and positive values mean the opposite. </para>
    34.         /// <para>0 means the item is fully inside the viewport. </para>
    35.         /// <para>
    36.         /// An item's signed visibility also depends on what you pass as <paramref name="spacingFromViewportEdge"/>.
    37.         /// For example, using a positive value can return -1 ("item fully before viewport") even if you can see the item
    38.         /// </para>
    39.         /// </summary>
    40.         /// <param name="itemIndex"></param>
    41.         /// <param name="spacingFromViewportEdge">Same as in see <see cref="SmoothBringToView(int, float, float?, Func{float, bool}, Action, bool)"/></param>
    42.         /// <returns>
    43.         /// </returns>
    44.         public virtual double GetItemSignedVisibility(int itemIndex, float? spacingFromViewportEdge = null)
    45.         {
    46.             double signedVisibility = 0d;
    47.  
    48.             double spacingToUse = spacingFromViewportEdge ?? _Params.ContentSpacing;
    49.             var vhIfVisible = GetItemViewsHolderIfVisible(itemIndex);
    50.             bool visible = vhIfVisible != null;
    51.             int indexInView = _ItemsDesc.GetItemViewIndexFromRealIndexChecked(itemIndex);
    52.             double itemSize = _ItemsDesc[itemIndex];
    53.             double itemSizePlusSpacing = itemSize + spacingToUse;
    54.  
    55.             double startInset = visible ? GetItemRealInsetFromParentStart(vhIfVisible.root) : _InternalState.GetItemInferredRealInsetFromParentStart(indexInView);
    56.             double startInsetAdjusted = startInset - spacingToUse; // calculating visibility relative to vp's start+spacing
    57.             double outsideAmount;
    58.             if (startInsetAdjusted < 0d)
    59.             {
    60.                 outsideAmount = -startInsetAdjusted;
    61.                 if (outsideAmount > itemSizePlusSpacing)
    62.                     outsideAmount = itemSizePlusSpacing;
    63.  
    64.                 // Before vp => negative
    65.                 signedVisibility = -(outsideAmount / itemSizePlusSpacing);
    66.             }
    67.             else
    68.             {
    69.                 double endInset = visible ? GetItemRealInsetFromParentEnd(vhIfVisible.root) : _InternalState.GetItemInferredRealInsetFromParentEnd(indexInView);
    70.                 double endInsetAdjusted = endInset - spacingToUse; // calculating visibility relative to vp's end+spacing
    71.                 if (endInsetAdjusted < 0d)
    72.                 {
    73.                     outsideAmount = -endInsetAdjusted;
    74.                     if (outsideAmount > itemSizePlusSpacing)
    75.                         outsideAmount = itemSizePlusSpacing;
    76.  
    77.                     // After vp => positive
    78.                     signedVisibility = outsideAmount / itemSizePlusSpacing;
    79.                 }
    80.             }
    81.  
    82.             return signedVisibility;
    83.         }
    -- Lucian
     
  14. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Generally, yes. Safest would be to only rely on this assumption in OSA.Update(), and maybe
    OSA.OnScrollPositionChanged().

    You mention "scrolling" without "moving the scroll view", but they're the same thing from how I see it. Maybe you meant something else?

    But anyway, the new method I've implemented gives you a finer control, as you can now decide to fully bring the item into view if, let's say, it's only 20% visible, but if it's already 50% visible, bring the next item.

    -- Lucian
     
  15. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    I just meant that I want to scroll only if necessary, and do not scroll if the item is already fully in view.
    ScrollTo() will place the item on top, even if it was already visible.
    For now I use '_VisibleItems' to find out the first and last items, and that works fine so far.

    Will look into 'SmoothBringToView()' if necessary, but I'd prefer instantly scroll for now.


    Good thing we'll have 'GetItemSignedVisibility()', it can be very handy in some cases.
    Just one thing, there's a very specific case where on item is larger than the view, so its middle is visible and not its start and end, you may want to test your code with that case to ensure it behaves correctly.
     
  16. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    I posted the code because you can already use it now as it is. And for SmoothBringToView(), you can use a very small number for the duration, like 0.01f. If you want instant movement, here's the non-animated version I'll also add in the next version:
    Code (CSharp):
    1.         /// <summary>
    2.         /// Same as <see cref="SmoothBringToView(int, float, float?, Func{float, bool}, Action, bool)"/>, but it executes instantly via <see cref="ScrollTo(int, float, float)"/>
    3.         /// </summary>
    4.         public virtual void BringToView(int itemIndex, float? spacingFromViewportEdge = null)
    5.         {
    6.             double signedVisibility = GetItemSignedVisibility(itemIndex, spacingFromViewportEdge);
    7.  
    8.             double spacingToUse = spacingFromViewportEdge ?? _Params.ContentSpacing;
    9.             float spacing01RelativeToViewportSize = (float)(spacingToUse / _InternalState.vpSize);
    10.  
    11.             // Has its start before the viewport's start => align the start with viewport's start
    12.             if (signedVisibility < 0d)
    13.             {
    14.                 ScrollTo(itemIndex, 0f + spacing01RelativeToViewportSize, 0f);
    15.                 return;
    16.             }
    17.  
    18.             // Same for viewport's end
    19.             if (signedVisibility > 0d)
    20.             {
    21.                 ScrollTo(itemIndex, 1f - spacing01RelativeToViewportSize, 1f);
    22.                 return;
    23.             }
    24.  
    25.             // Fully visible
    26.         }
    With this, you're referring to the implementation of GetItemSignedVisibility()?
    If yes, then visibility will be returned as expected, i.e. it'll never be 0 :)= "fully visible"). And when parts of the item are both before and after the viewport (item is bigger than viewport, as you say), it'll return a negative value by convention. Not a problem IMO.

    PS: Today I started investigating the previous bugs you've posted.

    -- Lucian
     
  17. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    I've just deprecated the ExpandCollapseOnClick component (it only gives a warning if you use it, not a compile-time error) in favor of the existing ExpandCollapseAnimationState class, which is currently only used in the "expand_to_item_variable_size" in the official pack. I'll migrate all the other 5 examples that use ExpandCollapseOnClick to use that instead. Let me know if you'll need those changes (email).

    Done. Request the changed code by email.

    ATM, it's required for the sizes cache to be reset when this happens, but you can easily restore every item that has a non-default size by overriding PostRebuildLayoutDueToScrollViewSizeChange() and manually setting back the sizes.

    First step:
    Use the new ExpandCollapseAnimationState class instead of ExpandCollapseOnClick. I've already changed the MainExample.cs to use that (email for the code).

    Second step:
    Case A: iterating through all the items is acceptable performance-wise (you have 10k items or less, I guess)
    A.Solution1: Use CollectItemsSizes to set specific sizes to the non-default-sized items (look ad IncrementalItemsFetchExample). Don't specify the size for the default-sized items, as it'll increase the cache unnecessarily.
    A.Solution2: override PostRebuildLayoutDueToScrollViewSizeChange and go through all the models and call RequestChangeItemSizeAndUpdateLayout(), only for the items with non-default sizes.

    Case B: (else)
    Solution: Same as A.Solution2, but you'll need to keep track of the items that have non-standard size yourself

    I've just added a new KeepItemsSizesOnLayoutRebuild property to BaseParams.Optimization, which controls whether OSA.CollectItemsSizes will execute some custom code for this or not. Email me for changes, if you need them.

    -- Lucian
     
    Last edited: Aug 27, 2019
  18. ibyte

    ibyte

    Joined:
    Aug 14, 2009
    Posts:
    1,047
    Hi, would it be possible in future releases to have the examples package store all the files under a root dir of something like "Examples" directory?
     
  19. ibyte

    ibyte

    Joined:
    Aug 14, 2009
    Posts:
    1,047
    I wanted to reuse the pageview example but the code seems to tightly linked to the Canvas-Overlay - when I disable that gameobject nothing works anymore.
     
  20. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Hi there! Do you mean i should move Views transform or may be it's child, or prefab root transform?
     
  21. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hi,

    Yes. Good point, I added it on the high-priority list.

    If you're just getting started with OSA, please check the manual, especially the demos manual, if you want to copy a demo.
    In your case, you're tying to copy it together with the "SceneEntry" script and other demo-related scripts/objects, which are used to test the example, not only to use it (which is what you want). You'll find more info about this in the manual and how to properly copy the examples in your own use-case.

    -- Lucian
     
  22. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,
    Only the item's root transform is managed by OSA. Any child of the items can transformed however you want because OSA doesn't "know" about them.
    In your case, I suggested having a single direct child of the item, named "Views" where you have any other objects, so you can move all of them by just moving the Views child.

    -- Lucian
     
    justtime likes this.
  23. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Lucian, thank a lot, another moment:
    If i turn off looping(before play), my first and last elements are not centered, how could i fix that?
    Middle one

    First of the list



    2)Also, does it possible scroll only by 1 step (independently from scroll power) ? I mean just to near left or right.
     
    Last edited: Sep 6, 2019
  24. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,
    1. Use settings of the Snapper script that are similar to the ones in the page_view scene.
    2. Turning on/off looping needs to be tested/adjusted differently in each particular case, because there are too many variables to take care of. If you're turning looping on/off before initializing OSA, that'd be the safest way I think.
    In case of non-looping, you can either set a constant start/end padding (but you need to test it on different screen sizes to see if the same value is OK) or use the OSAPercentageContentPaddingSetter script with its default settings (if just setting a fixed padding in pixels in inspector doesn't look consistent across different screen sizes/aspect-ratios).

    -- Lucian
     
  25. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Hi there! Does it possible to limit items count? I want to use all width viewport, but 3-5 items.
     
  26. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    BTW, when i updated from 2017.4 to 2018.4 i face with TMP issue. I had to manually add reference to the TMP assembly in OSA assembly. I think it could be helpful.
     
  27. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,
    The items' default size will be the same as the prefab's, if you use the built-in BaseParamsWithPrefab, so if you make sure the prefab always stays sized to 1/5 of the viewport, the items will naturally be sized so that exactly 5 of them will occupy the viewport, regardless of its size.

    One way to do it is to put the prefab inside the scrollview object or inside the viewport object, and make it 1/5 of the size, and set the prefab's anchors at the same positions as its corners. This way, the prefab will always scale with the viewport.
    If you want it to be mathematically exact, you can use a layout group and play with different solutions. For ex., inside a vertical layout group that scales 1:1 with the viewport, add 1 item with flexible height of 4 and the prefab with flexible height of 1. You can shift the elements to the right/left of the viewport so they aren't seen (you only care about the viewport's height, as the items' width is already determined, by default)

    Did you already use TMPro inside OSA's source files? As far as I know, OSA doesn't directly reference TMP, unless you
    import the TableViewTMProSupport package.
    In case you didn't modify the OSA's source code, please let me know which files gave you errors, so I can know where to look. Thanks!

    -- Lucian
     
  28. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Hi! Does it possible to prevent selecting elements in scroll list, while scrolling?
     
  29. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    What do you mean by selecting?
     
  30. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Sorry, it's not related to OSA.
     
  31. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Hi there! I have an issue with delayed appearance of items in horizontal list. OSA 4.3, Unity 2019.2.9f.
    Here is the video

    Here is my Adapter settings


    Veiwport


    Content
     
  32. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    At the first look, I see the scrolling jitters at the right, which can be caused by the same thing that causes delayed appearance of the items.
    I see the game objects in under the Scroll View are placed correctly, so it should be a problem in the code, or maybe the prefabs are up-scaled beyond their real RectTransform bounds.

    I also see you added "Drag Down/Up Possible" properties to OSA and probably you needed to change the OnBeginDrag, OnDrag etc. to support that. Can you debug that and make sure it works as expected? Maybe you'll find the issue there.
    Let me know.

    -- Lucian
     
  33. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Be sure Drag Down/Up Possible don't affect anything. I didn't change core code at all.
    I notice, that this is happening, when i test layout for tablet. So canvas has about 530 width, while with regular 360px width phone layout it works fine.
     
  34. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    In this case I can only browse the project or a stripped-down version of it to discover the issue. Please send it to me on email or discord DM.

    -- Lucian
     
  35. llfranco712

    llfranco712

    Joined:
    May 27, 2017
    Posts:
    1
    @thefallengamesstudio could you give me some help? I'm trying to implement the plugin functionality on a world space canvas. I've already created list adapter, model and view holder classes, everything is well structured.

    The thing is, I'm currently using a SimpleDataHelper (which I initialize on Start before calling the base Start) of MyModelType. I need to be able to add items on runtime. I add items through method "InsertOneAtEnd" of SimpleDataHelper class, but the prefab is not instantiated until I interact with the scrollview for the first time, it doesn't matter if the list is empty or is filled with hundreds of items. And even after instantiating, it instantiates only 1 item and it always remains disabled.

    Below is my code so you can understand a little bit better what I'm doing.

     
  36. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    Help granted. :D
    Please make a vanilla BasicListAdapter via OSA Wizard inside a world-space canvas. Then un-comment the code sections needed for minimal functionality. Then make sure Start and Update are as follows:
    Code (CSharp):
    1.    
    2. protected override void Start()
    3. {
    4.    Data = new SimpleDataHelper<MyListItemModel>(this);
    5.  
    6.    // Calling this initializes internal data and prepares the adapter to handle item count changes
    7.    base.Start();
    8.  
    9.    //// Retrieve the models from your data source and set the items count
    10.    //RetrieveDataAndUpdate(500);
    11. }
    12.  
    13. protected override void Update()
    14. {
    15.    base.Update();
    16.  
    17.    if (Input.GetKeyDown(KeyCode.T))
    18.        RetrieveDataAndUpdate(1);
    19. }
    20.  
    Press T at runtime to incrementally add items and let me know if the issue is also found there. I tried and it works as expected, so this will be a starting point in investigating the issue.
    Also, does it happen only in world-space canvases? And which Unity version and package version do you use?

    -- Lucian
     
  37. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Hi there! My goal is to change scroll viewport dynamically(it's like music player close-open control buttons bar, which located under the track's playlist, and due to this viewport also must changer it's height), but this operation produces a lot of cpu work on MyUpdate() method "if (scrollviewSizeChanged)...". It costs around 30ms every frame! How could i achive this in optimized way? Thanks.
     
  38. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    Yes OSA has to recalculate several things on VP size changes to keep everything consistent. For example, you may have a prefab whose height depends on the viewport height, in case you want the items to always be 1/10 of the viewport's size, but this is only 1 example of many.

    It depends a lot on your context, but the only way I can think of is to add an Image component and a Mask component on the Scroll View and set the Image type to Fill, and animate the Fill amount (through code or animator - you probably already have the code for this, since you are now animating the vp's size) to "open"/"close" the Scroll View.
    Of course, because this is just visual, you need to also disable/enable the game object when it's fully invisible/visible.
    If you want to also make it non-interactable during the animation, add a CanvasGroup & control its Interactable property.

    -- Lucian
     
  39. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Yes, this approach takes much less cpu time, but the problem is that user can't see latest element, cause they under the mask bottom border. I don't want disable these elements. May be there is another solution?
     
    Last edited: Nov 4, 2019
  40. savek1986_unity

    savek1986_unity

    Joined:
    Sep 18, 2018
    Posts:
    1
    Hi.
    I have created an app that show in a scroll view a list of my prefab.
    This list and the content of prefab(text, image, video, etc...) are generated from a json on a server.
    So, Can I integrate "Optimized ScrollView Adapter" in my logic?

    Thank you!
     
  41. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    Without a fully-described desired behavior I'm just guessing what's the exact functionality you need.
    You can either (a) create a script that literally moves the scrollview up and down, (b) see the "nested scrollviews same direction" and identify whether that's what you need, or (c) use profiler to see why it takes so long to rebuild the scrollview when you resize the viewport, because it shouldn't be uncomfortably slow (maybe you're re-creating the models also, or doing any kind of iteration through all the models, and if they're 100+, resizing the viewport by hand is noticeably slow because it's a continuous process across multiple frames).

    If neither of the above works, I'll need an animation with your final desired result (or a complete detailed description of it), in order to find other solutions.


    Hey,

    Sure. You can check the manual beforehand to get a feel about the implementation. The asset's page has multiple useful links for documentation.
    I'm not familiar with using video content in unity as part of a list of items where some of them have a video part,
    but if you already have this working, of course you can keep most of the existing functionality.
    The only thing that you need to take care of is that game objects are recycled, so you won't store any data in the game objects long-term, but in "models"; game objects will be used just to "view" the model, and will be created by OSA whenever needed - you'll just need to provide the prefab and the data binding.

    -- Lucian
     
  42. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    @thefallengamesstudio Thanks. I will try these methods. On my gif you can see panel under the playlist. I want to resize this panel into small one(just change height), and then on click back again. Like in many music players. So playlist's scrollview should also change it's height, following underlying panel.


    Btw, here what is going on in profiler while changing scrollview height
     
    Last edited: Nov 5, 2019
  43. justtime

    justtime

    Joined:
    Oct 6, 2013
    Posts:
    424
    Hi! OSA.LateUpdate always generates 32b garbage each. Could it be somehow fixed?
     
  44. Barritico

    Barritico

    Joined:
    Jun 9, 2017
    Posts:
    374
    I have bought the product and I really like what I have seen so far.

    I have a question.

    Is it possible to link (databind) a list (json) to create a data table? Or some example?

    Thanks!!!
     
  45. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    This is normal. Changing the Viewport's height leads to several re-calculations to keep consistency in as many casses as possible.

    The reason you showed this is because this is the same moment the FPS goes down, right? And you want a smooth resizing experience, right?

    The only solution I can think of is to just move up instead of resize the scroll view, which will be perceived as a resizing - when you detect the user ended the resizing gesture (pointer up), you can actually resize the scrollview, if you want to have the scroll view 100% functional after the resize.

    Alternatively, you can enable ScaleToZeroInsteadOfDisable property in inspector to prevent constant disabling/enabling of items that go outside/inside the viewport. This may have some benign side-effects in certain cases, but generally you shouldn't notice any difference, visually.

    -- Lucian
     
  46. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    I've investigated and found out this is done by the Snapper, which creates a delegate method to pass OSA.SmoothScrollTo as the onProgress parameter, and removing that lambda creation removes the 32b allocation.

    Now, the weird thing I see is the allocation is done even if the method returns before the call to SmoothScrollTo (specifically, at line with "if (SnappingInProgress || !_SnapNeeded ..."), which is done when the snapper is idle. I can't find an explanation for this. Lambdas shouldn't be allocated if the method returns before the line they are declared. Maybe it only happens in editor or it's a compiler "optimization"/"feature"?

    I assume 32 bytes won't have any noticeable effects on UX, but I'm still interested in the issue, so if by any chance you find a solution to this, or know someone that could help (and has a lot of free time), please write to me.

    -- Lucian
     
  47. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey, Welcome!

    Usually, the answer is yes. But first, I need to understand the question. Can you elaborate?

    From a high-level view, you have a list of models (similarly-structured pieces of data, like the infos from some business cards) and you present them through a visual list of similarly-looking game objects, like a list of contacts.
    Building on top of that, you can have grids, tables (in the sense of the database tables). If you check the list of demos, I think you'll find a case similar to what you need, but if not, explain your use-case in more detail and I'll be able to give you better directions.

    -- Lucian
     
    Barritico likes this.
  48. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    518
    What is the proper way to clear all BaseItemViewsHolder? I need to clean up cached Image.sprite for memory optimisation
     
  49. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    Hey,

    OnBeforeRecycleOrDisableViewsHolder() callback. You can further narrow down the cleanup only to cases where newItemIndex=-1, meaning the viewsholder won't be immediately recycled and it'll just stay in memory for a while.

    -- Lucian
     
  50. Gladyon

    Gladyon

    Joined:
    Sep 10, 2015
    Posts:
    389
    I have seen that if you inherit from OSA, you cannot easily use 'Time' anymore.
    You are forced to write 'UnityEngine.Time' everywhere.

    It is really not a good practice to use a standard class name as a non-private field/property.