Does Exoplayer2 always cause onSurfaceSizeChanged() to be called at least twice? - android-layout

My app utilizes a custom FrameLayout that contains a PlayerView and another custom FrameLayout ("DrawView"). The goal is to draw customized shapes on the FrameLayout that appear on top of the video being played.
In the layout, I have PlayerView first, then the DrawView:
<!-- customized FrameLayout encompassing ViewPager2 and DrawView, which implements that capability of
detecting taps -->
<com.reddragon.intouch.utils.ViewPager2Host
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/viewpager2host"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
tools:context=".ui.MediaPlayerActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/activity_media_player_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.viewpager2.widget.ViewPager2>
<com.reddragon.intouch.ui.drawview.DrawView
android:id="#+id/drawView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
The DrawView component is used to draw shapes and text over the top of the video, and so I need to resize it to match the size and position of the rect that represents where the video is played.
I have the logic correctly implemented to do this, and I have implemented VideoListener and placed this logic within onSurfaceSizeChanged().
This works fine - and as you can see from the screenshot, the red line (representing the bounds of the enclosing parent ViewPager2Host) and the green line (representing the bounds/placement of the video surface view I calculated at runtime for this particular video) is correct:
Here is my question - onSurfaceSizeChanged() gets called multiple times, and I only want to do the calculation of size and location once for DrawView. From my testing it appears that as long as the ViewPager2Host parent does not change dimensions, then onSurfaceSizeChanged() gets called twice, and the second time is the "final" dimension.
Can anyone confirm that this is the case? Or are there cases - perhaps where the parent dimensions exactly match the video surface view dimension that onSurfaceSizeChanged() would only be called once?
Or is there some way to know when the surface size is 'final'?
I'm trying to implement some sort of 'if video is initializing then' logic so that only AFTER all layout is complete and the video surface rect has been finalized, do I calculate the size and placement of DrawView.

The simple answer to my original question appears to be: NO - there is no guarantee on how many times this gets called in the normal rendering pipeline, except that it is called at least once.
Upon further testing, it also becomes clear that even after onPlayerStateChanged() gets called with STATE_READY, the video size is not yet stable (I've seen numerous times now that onSurfaceSizeChanged() gets called with updated values even after STATE_READY gets called).
So my original idea of handling everything in onPlayerStateChanged() after playWhenReady is TRUE does not now seem to be a good answer.
Still looking for how I can tell the video surface has settled down to the size it thinks is "final" for playing the video!
As a refresher, my customized FrameLayout has three "layers" (each of which completely fill the holder associated with the current ViewPager2 adapter):
Media display (either a Glide implementation for images, or ExoPlayer2 PlayerView for Media)
DrawView (customized FrameLayout which holds the various shapes drawn on top)
FrameLayout (holding an indeterminant progress bar and text shown when video media from the Internet is taking a while to buffer)
My solution ended up being (upon the "next" item in the ViewPager2 recyclerview being ready to display a video):
Initially hide the DrawView and Media Display layers
Show the "loading" components
setPlayerWhenReady(true) on the ExoPlayer
prepare(mediaDataSource)
Within the onPlayerStateChanged() callback from ExoPlayer that my activity implements, test for both playWhenReady being TRUE and the plabackState being STATE_READY, and then
remove the "loading" views
set the ExoPlayer view visible
calculate the various drawing widgets within DrawView given the ExoPlayer's videoSurfaceView dimensions and position
set the DrawView Visible.
I'm not sure why, but it seems overriding other callbacks like those from VideoListener didn't "jog" the views to draw themselves properly. I saw numerous cases where onDraw() of the DrawView class would be called, but neither onDraw of the widgets, or onDispatchDraw of the DrawView class would be called.

Related

Can't get buttons in custom layout to return a minimal width measurement with WRAP_CONTENT

I created a custom flow layout class to lay out a series of buttons of equal height and various widths depending on what text each button displays. Each row contains as many buttons as possible, and any remaining space is distributed evenly to fill up the row. This generally works, but I can't get the buttons to be just small enough to fit their text, and as a result most of the buttons are exactly the same size.
This is how I'm creating the button and adding it to the flow:
Button button = new Button(this);
button.setMinimumWidth(0);
button.setText(eventType.get("name").asString());
button.setAllCaps(false);
button.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
button.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
eventTypeFlow.addView(button);
In the layout class' onLayout, I measure each button like this:
View child = getChildAt(childIndex);
measureChild(child, MeasureSpec.makeMeasureSpec(clientWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(clientHeight, MeasureSpec.AT_MOST));
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
But unless the text in the button is especially long, the width is always the same (264 in my case). I tried creating a linear layout XML with the button in it, setting minimum width to 0 and layout:width to wrap_content and that way the button is smaller, but when I distribute the extra space the linear layout grows, not the button.
Is there a way to get the buttons to return a smaller measurement when adding them directly to the custom layout?
Try using a LinearLayout using the layout_weight attribute. Give each button equal weight (1) and set your android:width attribute to 0dp.
Linear Layout layout_weight
More on LinearLayout's LayoutParams can be found here.
What ended up working is inflating the same linear layout XML I described in my question, then removing the button from the linear layout and adding it directly to my custom layout. Not very elegant, but it's the only thing I found that gets those buttons to be smaller.

JavaFX-8: Custom Layout and Layout Passes (layout pass/css pass): Where should I add child nodes?

I'm developing a custom table component for very large and very custom content. Therefore I decided not to go with TableView, but start from scratch (i.e. Control). I adopted the idea of the VirtualFlow to create and manage only the visible part of the table and reuse cells that have become invisible. However I needed virtual rows and columns.
My CustomVirtualFlow implements layoutChildren(). From the component's size and scrollbar positions I know which cells are visible. If necessary, I add cells to the VirtualFlow. I then update the content and css pseudo class states (selected, focused, ...).
This works almost fine ... currently, my only problem is that the css styles are sometimes lagging: newly created cells are shown with e.g. wrong backgrounds for a moment and then get correcteted with the next pulse.
My explanation for this is that JavaFX performs layout in two passes:
first a css pass and secondly the layout pass that calls layoutChildren().
The css of newly added children (during layoutChildren) is therefore not processes correctly.
I tried to call applyCss() directly, which helps, but seems to do too much because it takes a lot of time.
My question is:
How is the correct way to add nodes during layout, i.e. if the size of the component makes it neccessary to use further nodes?
If it is not during layoutChildren(), where else should I do it?

Input with overlapping elements inside a FrameLayout

I have a game (Unity3d) that is running on Android where its View is contained in a FrameLayout. The game view covers the entire screen.
I am adding (in code) another view next to this view another button view (the 2 views overlap - see images below).
The button does not receive any input.
According to the documentation for FrameLayout, it is advisable to use a single element under it, but multiple views are also possible.
Why does my button not receive any input?
Attaching the view state as captured in Eclipse.

Android UI Best Practices for Display Field turning into Input Field

I'm programming an android app with a list displaying images and some additional information like title, place, etc...
I'm new to the android ecosystem and wondering what are the best practices to implement a list element which normally displays the mentioned information and turns into input fields on a press on the listelement.
Do I have to draw the display elements (e.g. TextField) and the edit element on top of each other and set visible for either display or input? How to handle this generally? Or do I replace the layout responsible for the list element altogether?
I know that for the simple case of a text there is a simple solution for making the EditText field look like an uneditable TextField. I'm, however, looking for a general answer covering broader cases than just a TextField.
Thanks!
you can make the elements of the listview a view flipper having two views.one you textview and other and edittext.
<ViewFlipper
...
<TextView
... />
<EditText
... />
/ViewFlipper>
Then in the onItemClickListener of the list items you can call flipper.showNext();
So, flipper works like, it shows only the first view defined in it and on the subsequent call of showNext() it displays the next view defined in it.
So if you have 2 elements in it, it will behave as the coin with two sides.
So in your situation there are a few ways you can achieve this.
First way is to build a layout which contains TextView and EditText and in first initialization your edit text won't be visible. And in your OnItemClick you have to hide your textview and show editext with value of your current data and option to edit.
The second way which I think is more user friendly is that you can show an AlertDialog after OnItemClick with custom layout where you can change the values and update the listview after user press Yes or do nothing is he selects No.
It depends on you which way you will do that, but I think the second option is the better one.

Zooming Entire ScrollView

I have a series of PDFScrollViews (like the one given in the sample code on Apple Developer) inside a UIScrollView but I want them to all scroll at once no matter where in the ScrollView the Pinch-Zoom action happens, currently this only happens to the one PDFScrollView that is pinched and the other stay the same size. The basic goal I'm trying to achieve here is to make a series of PDFs act as one homogeneous PDF to the user.
implement scroll view's delegate method
-(UIView *) viewForZoomingInScrollView: (UIScrollView *) ScrollView
And return your PDF view.
hope it 'll work

Resources