How to avoid usage of Task.Delay while scrolling to a particular point in Scroll View as I am adding children in runtime which are out of view? - xamarin.ios

Here, I have used a scroll view and children are added upto visible height and the other children are added in runtime while scrolling down. Hence I need to scrollTo a particular point like Scrollview.ScrollTo(x,y) for this I need to calculate y by adding children height.Height is calculated by using delay to bring the view in screen first and I calculate. But need to calculate once the children displayed while scrolling without delay
ScrollViewer.ScrollTo(0, (int)totalheight);
await Task.Delay(500);
totalheight += this.CalculateHeight(ScrollView.GetChildrenAt(i));
Where children view height is return in the last method, but without delay the view become null.

Related

Kivy RecycleView: Get indices of all data items that are currently in view

I need to get the indices of all data items that are currently in view in a Kivy RecycleView widget. I want to display many Image widgets for which I render a texture and apply it to them. To save memory I need to delete these textures if they are not in view any more. I tried using the Kivy RecycleView example and modify it for my needs:
class SelectableImage(RecycleDataViewBehavior, Image):
def refresh_view_attrs(self, rv, index, data):
#Catch and handle the view changes
#print in view
print(index)
#call initial function of image class
return super(SelectableImage, self).refresh_view_attrs(rv, index, data)
The problem here is that refresh_view_attrs() only fires when a new Image widget gets added to the view. Thus I can only know what that last Image is, but not if there are any other in view. For this I'd also need to know which widget disappeared from the view. Is there any function in the RecycleView widget that I can use to obtain such information? Maybe there is a function that gets called whenever the view changes?
Okay, I found three possible solutions for my issue:
1. Solution
Calculate the position of all the Image widgets yourself and compare it with the position of the scrollbar to get the widgets that are currently displayed.
This is quite hacky and the RecycleView already does this internally. Thus I'd save the unnecessary computing and avoid this approach.
2. Solution
Use the get_visible_view(index) function of the view_adapter property of a RecycleView. This returns the currently visible view associated with index. If it return None, the view associated with index is not visible right now. This is how it's called for example:
self.myRecycleView.view_adapter.get_visible_view(index)
You could loop through the entire length of your data list and check for each item (the index in the list) if it is currently displayed or not.
3. Solution
My favourite approach: Use get_view_index_at(pos) of the layout_manager property to check what view index is at the given coordinates. This way you could check which item is at the top of the RecycleLayout widget and which one is at the bottom. You need to use coordinate transformation though. Example:
#get position at top center of RecycleView (upper limit)
pos = self.myRecycleView.to_local(self.myRecycleView.center_x, self.myRecycleView.height)
#check which items collides with the given position
print(self.myRecycleView.layout_manager.get_view_index_at(pos))
I hope this clears some things up!

CellMeasurer with function child ({ measure }) => <div />) renders excessive number of times

REF: demo at https://codesandbox.io/s/1vy7ljo877
The demo uses 2 approaches to render some images.
The first row renders an element inside CellMeasurer
while the second row renders a function with signature: ({ measure }) => )
I find that for the function render, while the size is calculated correctly, the render method gets called n^2 # of times where n is approximately the number of items that it tries to display.
If deferredMeasurementCache is turned on, the problem gets exponentially worse since n becomes the size of the collection.
The above results in crazy long page load times and very sluggish behavior when scrolling.
Is this normal behavior or am I implementing the function inside CellMeasurer incorrectly?
If deferredMeasurementCache is turned on, the problem gets exponentially worse since n becomes the size of the collection.
If you're not passing the CellMeasurerCache to Grid as a deferredMeasurementCache prop, then Grid isn't guaranteed to work correctly. It's necessary for it to know about the cache for a couple of cases. (Maybe check out this part of the docs to make sure you aren't misunderstanding how CellMeasurer is supposed to work when used to calculate the width/height of a row/column.)
That being said, what you're describing sounds like a bug, but I'm not sure it actually is. At a high level, it's caused by the CellMeasurer measure method calling Grid.recomputeGridSize() which synchronously triggers forceUpdate() (causing all visible cells to re-render).
At first I thought the solution might be to just debounce or queue a setState action instead. Unfortunately that only pushes the problem away a little; the possibility still exists. I can't debounce for too long, or I risk the Grid showing cells to the user that are clearly the wrong size. (A debounce that's too big might even allow a user to keep scrolling ahead of it, outrunning the re-render.) It's also possible that images will load far apartĀ (after a delay) in a real app, defeating any debounce I might do anyway.
The problem here is that any time a cell reports it has a different size, the Grid needs to relayout the other cells (potentially shifting them up or down). In this simple example it seems unnecessary- because all images end up having a uniform width and height. This isn't typical though. Since you've configured your CellMeasurerCache to measure height, and since the height of each column impacts the height of the row (and thus all other columns, as explained in the docs I linked to above), Grid has to re-render all cells each time one reports a change.
Put another way, if you added more rows, and changed your example to be more like this:
const makeImages = (count=10, startIndex=0) => {
const width = 100;
const imagesArray = _.times(count, (index) => {
const height = 100 + Math.round(Math.random() * 200);
return {
key: startIndex+index,
src: `http://placehold.it/${width}x${height}/${((1<<24)*Math.random()|0).toString(16)}/fff?text=${startIndex+index}`,
}
});
return imagesArray;
};
Then maybe it's easier to see why a change to a cell in row 1, column 1 could potential impact the height of all cells in row 1, and the position of all cells in the grid.

How to remove focus from LWUIT Textfield and resize Form correctly when Virtual Keyboard hides?

I'm facing a problem with the LWUIT's Textfield.
In some of my Forms I display a CategoryBar, while in others I hide it.
In some of the Forms I have Textfields, the problem presents itself when I focus on one and make the Virtual Keyboard (VKB) to appear. When the VKB appears, the screen components resize themselves to adjust to the Textfield to be visible while text is entered, but when I hide the VKB, either through the back button or the return key on the VKB, the Textfield remains with the focus, not only that, when the screen components resize themselves, the current visible Form resizes itself as if there was no CategoryBar present, so any components that are at the bottom of the Form are hidden by the CategoryBar.
This is fixed by displaying another Form (this includes PopupChoiceGroup and DatePicker) and then going back to the Form that is covered by the CategoryBar.
In other Forms where no CategoryBar is visible, sometimes the resizing when the VKB is shown causes the Forms to resize themselves as if the CategoryBar was visible, making it possible to interact with it when it shouldn't be available.
How can I make sure the focus is completely lost on the Textfield? Also, how to make sure a Form is resized correctly whether a CategoryBar is visible or not?
EDIT
I've been digging through the class reference for TextField, Form and VKB, in the later I found a method called autoAdjust which according to documentation:
Auto adjust size of the dialog. This method is triggered from a
sizeChanged event.
The method sizeChanged sounded like something I should check and in the Form's reference the description for this method is:
This method is only invoked when the underlying canvas for the form
gets a size changed event. This method will trigger a relayout of the
Form. This method will get the callback only if this Form is the
Current Form
This method seemed like the callback for resizing I was looking for, so I overrode it and placed a NotificatioBar to be displayed with the width and height values sent when the method was called.
What I found after testing this on my device was that when the Form was being resized after the VKB was shown or hidden, the height value sometimes instead of being 270 (the height for the Form when the CategoryBar is being displayed) it was sent as 320 (the full screen height, as if no CategoryBar was being displayed).
So far I haven't been able to understand why would the Form ignores the fact that the CategoryBar is being displayed or not when resizing the itself.
I tried to change the Form height inside its sizeChanged method but the Form wasn't affected by it. It seems to me what I have to modify is the canvas where the Form is being drawn, but I don't really know for sure since the canvas is hidden in LWUIT.
Could it be the canvas where my Form is being drawn is the one at fault? What is provoking this behaviour?
At the moment I found a workaround to avoid having my Components hidden by the CategoyBar because the Form resized wrongly after the VKB hid, for the scenario in which the Form resizes wrongly and displays the CategoryBar (which I don't know why is visible if I'm calling to its setVisibility method and passing false).
First I overrode the sizeChanged method:
protected void sizeChanged(int w, int h){
if(h > 270){
mainContainer.getStyle().setMargin(Component.BOTTOM, 50);
}
else{
mainContainer.getStyle().setMargin(Component.BOTTOM, 0);
}
}
I check the height value, if the value is greater than the expected height when the CategoryBar is being displayed then I set the bottom of my Container to 50, so it'll be visible.
But this wasn't enough because if I show again the same form and it resizes correctly then the Container will remain with a bottom of 50. So I overrode the onShow method too:
protected void onShow(){
int containerBottom = mainContainer.getStyle().getMargin(Component.BOTTOM);
if(this.getHeight() == 270 && containerBottom == 50){
mainContainer.getStyle().setMargin(Component.BOTTOM, 0);
}
}
I had to make sure if the height was 270 and my Container's bottom was 50 then the Container's bottom should be 0.
Since I haven't found a way to avoid having my Form to resize and show the CategoryBar when it shouldn't be displayed at all, I don't consider myself with a full answer. Will update if I find a workaround for this.
EDIT
I tried with explicitly setting the shown/hidden status by calling the setVisibility method inside the onShow method of every Form I have. So far I've been able to avoid the visual problems I experienced previously. I'm not sure if this problem was due to LWUIT or due to J2ME restrictions but this is how I worked around it.

iPhone alike sliding header, or: how to force immediate redraw on top margin change?

I am trying to implement a similar effect like the iPhone-alike sliding header in the iPhone contact app (the sliding header that group the contacts by it's starting letter).
This is the screen of my app, and what I want to achieve is the following:
I have a 'guide header' and three 'tabs' for sorting the list. When the user scrolls the list up, I want everything to scroll up (guide header, tabs, list). However, when the tabs reach the top of the screen (and the guide header will just be gone off the screen), I want the tabs to stop and stay there (remain as "sticky header"), and only the list items scroll as in any regular list view.
I have a view group (guide header) above a list view.
First of all, I want to have the guide header adjust it's position depending on the scrolling position of the list view.
First approach:
My idea was to set an onScrollListener to the list view and change the top margin of the guide header to whatever the scroll position of the first item in the list view is (which would be a negative value).
The logic is correct, but the problem I'm facing is that the guide header view doesn't get redrawn fast enough while I'm scrolling in the list view. The guide header view only updates (to my changed top margin value) when the list view fling comes to an end. Even slow scrolling doesn't work. Invalidating (invalidate()) the guide header view or it's parent also doesn't help, since it would just put an invalidation request to the queue, but the invalidation and redrawing doesn't happen immediately, but only when the UI thread becomes idle, which doesn't seem to happen while the user still has his fingers on the scroll list view. Seems that flinging the list view blocks the whole UI thread or keeps it busy for itself.
So the main problem is: changing the margin of the guide header view doesn't become visible immediately while the user is scrolling the list view. The code I'm using it this:
#Override
public void onScroll(final AbsListView view, final int firstVisibleItem,
final int visibleItemCount, final int totalItemCount) {
// Get the first list item and check it's scroll position. This will be the value (top), that we also
// use the scroll the header parallel.
View v = mainList.getChildAt(0);
final int top = (v==null)?0:v.getTop();
// This logs the current scroll position of the first list item element/view group.
Log.d("onScroll", "onScroll: " + top);
// Here we finally change the margin (setting a negative margin) to the header element.
((LinearLayout.LayoutParams)(findViewById(R.id.header_container).getLayoutParams())).setMargins(0, top, 0, 0);
// was just a test: invalidating the outer container/view group, doesn't help
// findViewById(R.id.ll_container).invalidate();
}
I do see the "onScroll:" log output I inserted in the code above in the logcat, but the following adjustment of the top margin just doesn't become visible.
My second approach: is to use a scrollview for the guide header + tabs and work with those. Scrolling the guide header (which is then a scroll view) from code with scrollView.scrollTo(0,Math.abs(Math.abs(top)) from the onScroll method of the list view does work and almost immediately shows on the screen, however, it's not very accurate/stable when the user flings the list view very fast - meaning it jumps in intervals and doesn't look smooth; it's only accurate/stable when scrolling slowly.
My question is now: is there any best practice to accomplish such a sliding header effect, and more concrete: is there a way to force the guide header view to be redrawn while the user is still scrolling the list view (in my first mentioned approach).
For this you should use some tricks (afaik there is no ready-to-use implementation of such a feature).
For instance, you could detect gestures on your view, and
if the current gesture matches a
scroll down, and the first list item
is visible, animate-shrink the
header's size to 0, the tab view's
size to match_parent. Start scrolling
the list only when the header is not
present anymore.
if the current gesture matches scroll
up, and the first is already visible,
animate-expand the header to it's
original size.
So using Animation on the header view might be your solution.
Update
An other workaround would be to extend your List (the value array of your adapter):
Inster a new (dummy) item at the top for the header representation, and modify your ListAdapter's getView method:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (position == 0)
{
convertView = inflater.inflate(R.layout.sliding_header, parent,
false);
return convertView;
}
//TODO: your original method body comes here
}
where the xml referenced by R.layout.sliding_header should contain the header layout of your list.
A custom OnScrollListener implementation applied to the ListView would make unnoticeable that the header actually is an item of the list, since it would hide the scrollbar.
You should add this listener to your listView in the activity's onCreate method:
listView.setOnScrollListener(new MyScrollListener());
where MyScrollListener is:
/**
* Custom OnScrollListener
*/
private final class MyScrollListener implements OnScrollListener
{
#Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount)
{
if (view.getFirstVisiblePosition() == 0)
view.setVerticalScrollBarEnabled(false);
else if (!view.isVerticalScrollBarEnabled())
view.setVerticalScrollBarEnabled(true);
}
}
I think you can also try and use my ExpandAnimation for that.
http://udinic.wordpress.com/2011/09/03/expanding-listview-items/
Just pass the animation class that "guide header" view, and let the animation do the work for you, no scrolling is needed in that case, and it's smooth.

How do I calculate HorizontalScrollView length including its child view

I want to calculate length of horizontal scroll view and I want to identify when user scroll to its end.
I solved it.
we have to use Hscrollview.getMeasuredWidth();
this method have to be used after all child updated in that scrollview completely.
After UIThread completed its work.

Resources