How to place the controls of an Exoplayer outside of the PlayerView - android

I'm using Exoplayer2 to show videos in my application. I need the controls to be visible at all time. I can archive this by setting app:show_timeout="0". But when the controls are always visible they take up space in the PlayerView.
I would like to show the controls beneath the PlayerView, so that I can always show to whole video.
This is my layout file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MediaPlayerActivity"
android:theme="#android:style/Theme.NoTitleBar"
android:contentDescription="hf_hide_help">
<com.google.android.exoplayer2.ui.PlayerView
android:id="#+id/playerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:use_controller="true"
app:rewind_increment="0"
app:fastforward_increment="0"
app:repeat_toggle_modes="none"
app:show_timeout="0"
/>
</RelativeLayout>
And this is my exo_player_control_view.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layoutDirection="ltr"
android:background="#CC000000"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="4dp"
android:orientation="horizontal">
<ImageButton android:id="#id/exo_play"
style="#style/ExoMediaButton.Play"/>
<ImageButton android:id="#id/exo_pause"
style="#style/ExoMediaButton.Pause"/>
<TextView android:id="#id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textStyle="bold"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:includeFontPadding="false"
android:textColor="#FFBEBEBE"/>
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="#id/exo_progress"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="26dp"/>
<TextView android:id="#id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textStyle="bold"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:includeFontPadding="false"
android:textColor="#FFBEBEBE"/>
</LinearLayout>
</LinearLayout>
This is what it looks like:
And this is what it looks like, when I click on the player - the controls hide.

Your use-case is not something general and ExoPlayer doesn't support it. In your case, you will need to disable the default controls and add your own custom controls beneath the player view. It would be very easy to build the layout and add the actions to them.
Update: (To answer Gyan's comment about time-bar)
Two approaches: (I explain the strategies since thousands of code examples are available already on SO and other sites)
1 - My suggestion which has worked perfectly for my needs:
override default controls view of the exoplayer (having a view with the same name in your layouts folder would do the job). Then remove all unnecessary buttons which you brought down in your custom view but just keep the time bar in overridden view. By doing that, all the appearing / disappearing behaviors are automatically kept for the time bar and you will have the rest in your own view. You wouldn't need to worry about handling the time bar if you liked that approach.
2- Just in case you insist on bringing the time bar out under the player window, what you need is a horizontal progressbar (Notice that you can customize the look of progress view to match your design). Then write a listener on exoplayer to take the timings (passed / remaining / current play-time) and calculate and update the progress of the bar based upon them. Remember that the progress bar needs to be touchable (and focusable) so that the user could drag it back and forth. Then you would need to re-calculate playtime when user drags the progress. (lots of coding and calculations obviously but almost easy stuff). Hope it works for you. If you needed code samples please let me know.

Here I have play/pause button on the video and other controls under the video, here is how to do it.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.exoplayer2.ui.StyledPlayerView
android:id="#+id/video_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
app:auto_show="true"
app:controller_layout_id="#layout/custom_exo_overlay_controller_view"
app:layout_constraintBottom_toTopOf="#id/exoBottomControls"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0"
app:repeat_toggle_modes="none"
app:resize_mode="fixed_width"
app:surface_type="surface_view"
app:use_controller="true" />
<com.google.android.exoplayer2.ui.StyledPlayerControlView
android:id="#+id/exoBottomControls"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:controller_layout_id="#layout/custom_exo_bottom_controller_view"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/video_view"
app:show_timeout="0" />
</androidx.constraintlayout.widget.ConstraintLayout>
here i am using both use_controller=true and StyledPlayerControlView.
controls which should appear on the video should go in your custom_exo_overlay_controller_view other views which should appear under the video should go in custom_exo_bottom_controller_view.
here is custom_exo_overlay_controller_view.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageButton
android:id="#id/exo_play_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
and custom_exo_bottom_controller_view.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="#id/exo_controls_background"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#color/color_secondary_black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:textColor="#color/colorWhite"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="#+id/exo_progress"
app:layout_constraintStart_toStartOf="parent"
tools:text="00:50" />
<TextView
android:id="#id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:textColor="#color/colorWhite"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="#+id/exo_progress"
app:layout_constraintEnd_toEndOf="parent"
tools:text="02:50" />
<ImageButton
android:id="#+id/exo_sound_on"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="32dp"
android:background="#android:color/transparent"
android:src="#drawable/ic_sound_on"
app:layout_constraintBottom_toTopOf="#+id/exo_duration"
app:layout_constraintEnd_toEndOf="parent" />
<ImageButton
android:id="#+id/exo_sound_off"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="32dp"
android:background="#android:color/transparent"
android:src="#drawable/ic_sound_off"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="#+id/exo_duration"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="#id/exo_progress"
android:layout_width="0dp"
android:layout_height="52dp"
app:buffered_color="#android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:played_color="#android:color/transparent"
app:scrubber_drawable="#drawable/ic_scrubber"
app:touch_target_height="52dp"
app:unplayed_color="#android:color/transparent" />
</androidx.constraintlayout.widget.ConstraintLayout>
also you should assign your controller like this
player = ExoPlayer.Builder(binding.root.context).build().also {
binding.videoView.player = it
binding.exoBottomControls.player = it
val mediaItem = MediaItem.fromUri(video.url)
it.setMediaItem(mediaItem)
it.seekTo(video.currentWindow, video.playbackPosition)
it.prepare()
}

Related

Searchable RecyclerView inside BottomSheetDialogFragment that stays expanded and does not wrap content

My app has a BottomSheetDialogFragment which displays list of countries with RecyclerView. When user types in the EditText bottomsheet shrinks in size so as to wrap content. What can I do to make BottomSheet remain the same and not shrink in height. I could not find anything similar on SO, closest thing I got was this article:
https://medium.com/#oshanm1/how-to-implement-a-search-dialog-using-full-screen-bottomsheetfragment-29ceb0af3d41
Can someone explain why this shrinking happens, why the article uses 2 LinearLayouts nested inside each other and provide solution to this problem with thorough explanation?
Here is xml code
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tvCountry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/tilSearchCountries"
style="#style/ReBankTIL"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/rtvCountry"
app:startIconDrawable="#drawable/ic_search" >
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/tieSearchCountries"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionSearch"
android:inputType="text"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvCountries"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="#id/tilSearchCountries" />
</androidx.constraintlayout.widget.ConstraintLayout>

ConstraintWidth_percent looks different in design tab and in practice

I am trying to use ConstraintWidth_percent in my shop_list_item.xml, which is used inside my shopadapter. The problem I encounter is, that the design tab (how it should look like) and the in-app design (how it looks) are totally different. What am I doing wrong here?
How it should look like
How it looks
Code
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="#+id/mcv_product_item"
android:layout_width="0dp"
android:layout_height="210dp"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="#android:color/white"
app:cardCornerRadius="4dp"
app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.40">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/iv_product_image"
android:layout_width="match_parent"
android:layout_height="110dp"
android:contentDescription="TODO"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:loadImage="#{product.images[0]}"
tools:ignore="ContentDescription, HardcodedText"
tools:src="#drawable/ic_calibrate" />
<ImageView
android:id="#+id/iv_service_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="#null"
android:src="#drawable/ic_service"
app:hideView="#{product.hasService}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/tv_product_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="2"
android:text="#{product.name}"
android:textAlignment="textStart"
android:textColor="#color/color_text_dark"
android:textSize="#dimen/textDescriptionNormal1"
app:layout_constraintEnd_toEndOf="#+id/iv_product_image"
app:layout_constraintStart_toStartOf="#+id/iv_product_image"
app:layout_constraintTop_toBottomOf="#+id/iv_product_image"
tools:text="TEST TITLE TO ENSURE STUFF" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/tv_product_price"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:text="#{product.price}"
android:textColor="#color/color_text_blue"
android:textSize="#dimen/textHeadlineNormal1"
android:textStyle="italic|bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="#+id/tv_product_name"
tools:text="4870.00" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/tv_euro"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#string/currency"
android:textColor="#color/color_text_blue"
android:textSize="#dimen/textHeadlineNormal1"
android:textStyle="italic|bold"
app:layout_constraintBottom_toBottomOf="#+id/tv_product_price"
app:layout_constraintEnd_toEndOf="#+id/tv_product_name"
app:layout_constraintStart_toEndOf="#+id/tv_product_price"
app:layout_constraintTop_toTopOf="#+id/tv_product_price" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Currently not using -->
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.30"/>
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.70"/>
</androidx.constraintlayout.widget.ConstraintLayout>
When I change constraintWidth_percent to something really high like 0.8, it works like it should (but looks weird in the design tab).
This is a normal behavior as the design preview of Android Studio can have different screen size than your mobile set or the emulator.. You can change the design preview width/height to have similar width/height like your testing emulator/mobile and you'll notice there is no change.
You can change this form:
This can be very obvious as your CardView apparently takes the 40% of the RecyclerView item width. You can notice this will the green guidelines in below pic.
What you can do is to teak the 40% until you feel comfortable with a certain width that can fits for the cart items.
Last thing you can try the below layout for aligning the item in the middle of the RecyclerView item layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="#+id/mcv_product_item"
android:layout_width="0dp"
android:layout_height="210dp"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="#android:color/white"
app:cardCornerRadius="4dp"
app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.4">
<!-- Add CardView items -->
</com.google.android.material.card.MaterialCardView>
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3" />
</androidx.constraintlayout.widget.ConstraintLayout>

Constraint Layout Vertical Align Center - Two Child Views

I have two TextViews, one above the other. I would like the vertical middle of the two TextViews to be at the same position as the vertical middle of the ImageView. (This is so, regardless of the amount of text that may go into either TextView, everything will always look neat, vertically.)
I created what I need perfectly using two LinearLayouts (as the space above the title is the same as the space beneath the description):
But Android Studio was unable to covert it to ConstraintLayout successfully, as it just dumped the TextViews at the bottom of the layout. I've played with a lot of attributes, but could not quite arrive at the desired layout.
My question is similar to this one, except that I am trying to center_vertical align a pair of views rather than a single one - which means I have no view edge to align to the centre of the ImageView/container.
Is it possible to achieve what I'm after with ConstraintLayout? (I expect I may be able to do it with a single RelativeLayout, but I would like to use the layout_constraintDimensionRatio attribute on my ImageView which presumably leave me needing to use ConstraintLayout.)
In case it helps, here's the code for my aforementioned LinearLayout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/imageView"
android:layout_width="#dimen/resources_list_image_size"
android:layout_height="#dimen/resources_list_image_size"
android:layout_marginEnd="#dimen/activity_horizontal_margin"
android:layout_marginRight="#dimen/activity_horizontal_margin"
android:layout_gravity="center_vertical"
android:contentDescription="#string/resource_image"
android:scaleType="centerCrop"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="#+id/textViewTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="#style/MyTextAppearanceMedium"
tools:text="Title" />
<TextView
android:id="#+id/textViewDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="#style/MyTextAppearanceSmall"
tools:text="Description" />
</LinearLayout>
</LinearLayout>
Update: Solution
Thanks to Ben P's answer, this is my final code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Add guideline to align imageView to. -->
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3" />
<ImageView
android:id="#+id/imageView"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="#string/resource_image"
android:scaleType="centerCrop"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="#id/guideline"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="#+id/textViewTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/activity_horizontal_margin"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
app:layout_constraintBottom_toTopOf="#id/textViewDescription"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/imageView"
app:layout_constraintTop_toTopOf="parent"
android:textAppearance="#style/MyTextAppearanceMedium"
app:fontFamily="#font/roboto_slab_regular"
app:layout_constraintVertical_chainStyle="packed"
tools:text="#string/enter_title_colon" />
<TextView
android:id="#+id/textViewDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/activity_horizontal_margin"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/imageView"
app:layout_constraintTop_toBottomOf="#id/textViewTitle"
app:fontFamily="#font/roboto_slab_light"
android:textAppearance="#style/MyTextAppearanceSmall"
tools:text="Description" />
</androidx.constraintlayout.widget.ConstraintLayout>
It sounds like you could solve this problem by using a packed chain anchored to the top and bottom of the ImageView. You'll also need to use horizontal bias and a constrained width in order to get wrapping to work correctly.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="#+id/anchor"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="64dp"
android:background="#caf"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="#+id/one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintHorizontal_bias="0"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toTopOf="#id/anchor"
app:layout_constraintStart_toEndOf="#id/anchor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#id/two"/>
<TextView
android:id="#+id/two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_bias="0"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="#id/one"
app:layout_constraintStart_toEndOf="#id/anchor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="#id/anchor"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The important attributes here are:
app:layout_constraintVertical_chainStyle="packed" on the first view, which causes the two textviews to stack right on top of each other
app:layout_constraintHorizontal_bias="0" on both views, which means that when the text is not long enough to reach the edge of the screen it will stick to the edge of the anchor view
app:layout_constrainedWidth="true" on both views, which prevents the textview from ever being wider than its constraints, and so the text wraps to a new line
If you want to use ConstraintLayout you could use something like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imageView"
android:layout_width="200dp"
android:layout_height="200dp"
android:contentDescription="description"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="#+id/linearLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="#+id/imageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/imageView"
app:layout_constraintTop_toTopOf="#+id/imageView">
<TextView
android:id="#+id/textViewTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Title" />
<TextView
android:id="#+id/textViewDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Description" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
You can use this layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView2"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#color/colorAccent"
android:text="I am 5% of the screen height"
app:layout_constraintBottom_toTopOf="#+id/textView3"
app:layout_constraintEnd_toEndOf="#+id/textView3"
app:layout_constraintHeight_percent="0.05"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="#+id/textView3"
app:layout_constraintTop_toTopOf="#+id/imageView2" />
<TextView
android:id="#+id/textView3"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.15"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="#color/colorPrimary"
android:text="I am 15% of the screen height (And the image is 20% screen size in height) "
app:layout_constraintBottom_toBottomOf="#+id/imageView2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/imageView2"
app:layout_constraintTop_toBottomOf="#+id/textView2" />
<ImageView
android:id="#+id/imageView2"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.20"
app:layout_constraintDimensionRatio="1:1"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/rose" />
</android.support.constraint.ConstraintLayout>
It will look like this:
One important thing about this layout:
You can control your aspect ratio (for the image) with app:layout_constraintDimensionRatio="x:y" and by passing "1:1" make it square
And by the way - I am using support library for no reason on this example, you can use androidx

Android Studio - Why does my recycler-view get stuck without letting me scrolling to its bottom?

Description
I am using a recycler view that has more items inside than screen´s height, so to see them we should scroll down. The problem is that I can only scroll some dps down (like 10 or 20 dps).
What do I need?
I need to scroll to it´s bottom in order to see all recycler´s views.
My design is private so I am going to show you a graphic:
The blue box is my recycler view. It has ten views loaded, but we can only see two and a half. I can slide so that I can see "Daddy Yankee" full name, but not more than that.
My fragment XML code (in which I have my recycler)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="#string/ayuda">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraint_bar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:elevation="2dp"
android:contentDescription="#string/ayuda"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:background="#8d6e63"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="#+id/imgbt_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:contentDescription="#string/ayuda"
android:background="?selectableItemBackgroundBorderless"
android:src="#drawable/icon_back_negro"
android:tint="#ffffff"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/txt_lobby"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:contentDescription="#string/ayuda"
android:text="Lobby"
android:textColor="#ffffff"
android:textSize="15.5sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/imgbt_back"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="#+id/txt_names"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:text="(10) names"
android:fontFamily="#font/arimo"
android:textColor="#000000"
android:textSize="16sp"
app:layout_constraintTop_toBottomOf="#id/constraint_bar"
app:layout_constraintLeft_toLeftOf="parent"
android:contentDescription="#string/ayuda"/>
<View
android:id="#+id/view_barra0"
android:layout_width="match_parent"
android:layout_height="1.5dp"
android:layout_marginTop="5dp"
android:background="#eeeeee"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="#id/txt_names"
android:contentDescription="#string/ayuda"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_names"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/view_barra0"
app:layout_constraintLeft_toLeftOf="parent"
android:contentDescription="#string/ayuda"/>
</androidx.constraintlayout.widget.ConstraintLayout>
What can I do to slide my recycler to the bottom to be able to see all the other views?
Add this constraint on your id/recycler_names:
app:layout_constraintBottom_toBottomOf="parent"
and then set layout_height to 0dp (match constraint).

Can not scroll recyclerview

I am trying to implement a Recyclerview but I just can not make it scrollable. I read several threads here in StackOverflow, but I was not able to find one to fix my problem. I think there is something related to the MapView because I have another similar activity without maps that I can scroll, but I still do not know how to fix in this case.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"
tools:context=".activities.BookmarkActivity">
<com.google.android.gms.maps.MapView android:id="#+id/bk_mapView" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintHeight_default="percent" app:layout_constraintHeight_percent="0.4" android:layout_marginTop="0dp" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout android:id="#+id/bk_linearlayout" android:visibility="visible" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/bk_mapView">
<TextView android:id="#+id/radlabel" android:layout_width="wrap_content" android:layout_height="25dp" android:layout_marginStart="4dp" android:layout_marginTop="2dp" android:layout_marginBottom="8dp" android:hint="#string/bk_radiusName" android:textAlignment="center"
android:textSize="15sp" android:textStyle="bold" app:layout_constraintBottom_toTopOf="#+id/bk_textName" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />
<SeekBar android:id="#+id/bk_seekBar" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="2dp" android:layout_marginEnd="8dp" android:max="1000" android:min="10" android:progress="50" app:layout_constraintEnd_toStartOf="#+id/bk_imageButton"
app:layout_constraintHorizontal_weight="1" app:layout_constraintStart_toEndOf="#+id/radlabel" app:layout_constraintTop_toTopOf="parent" />
<ImageButton android:id="#+id/bk_imageButton" android:layout_width="25dp" android:layout_height="25dp" android:layout_marginEnd="8dp" android:background="#android:drawable/ic_input_add" app:layout_constraintEnd_toEndOf="parent" />
<EditText android:id="#+id/bk_textName" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="2dp" android:layout_marginTop="4dp" android:layout_marginEnd="8dp" android:hint="#string/bk_bookmarkName" android:inputType="textCapWords"
android:maxLength="30" android:textSize="15sp" app:layout_constraintEnd_toStartOf="#+id/bk_notify" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="#+id/bk_seekBar" />
<CheckBox android:id="#+id/bk_notifyWifiOnly" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="WifiOnly" android:visibility="gone" android:layout_marginTop="4dp" android:layout_marginEnd="4dp" android:layout_marginBottom="2dp"
app:layout_constraintBottom_toBottomOf="#+id/bk_textName" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="#+id/bk_seekBar" />
<CheckBox android:id="#+id/bk_notify" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Notify" android:layout_marginTop="4dp" android:layout_marginEnd="4dp" android:layout_marginBottom="2dp" app:layout_constraintBottom_toBottomOf="#+id/bk_textName"
app:layout_constraintEnd_toStartOf="#+id/bk_notifyWifiOnly" app:layout_constraintTop_toBottomOf="#+id/bk_seekBar" />
</androidx.constraintlayout.widget.ConstraintLayout>
<FrameLayout android:id="#+id/emptyFrameLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="2dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="#+id/bk_linearlayout">
<TextView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginStart="2dp" android:layout_marginTop="4dp" android:textAlignment="center" android:layout_marginBottom="150dp" android:textSize="20sp" android:textStyle="bold|italic"
android:text="None Here" app:layout_constraintTop_toTopOf="parent" />
</FrameLayout>
<FrameLayout android:id="#+id/topFrameLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="2dp" android:layout_marginEnd="2dp" android:visibility="visible" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="#+id/bk_linearlayout">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id="#+id/bk_swipeRefreshLayout" android:layout_width="match_parent" android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView android:id="#+id/bk_recycler_view" app:layout_behavior="#string/appbar_scrolling_view_behavior" android:layout_width="match_parent" android:layout_height="wrap_content" android:scrollbars="vertical" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
The actual layout screenshot is here:
Put your RecyclerView inside a ScrollView. The SwipeRefreshLayout extends ViewGroup and is used to allow the user to refresh/update the contents of a view via a vertical swipe gesture. It can, therefore, detect vertical swipe gestures made by the users, but it in and off itself does not behave as a scroll view.
Your code should be converted to something along the following lines:
<FrameLayout
android:id="#+id/topFrameLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:layout_marginEnd="2dp"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/bk_linearlayout">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="#+id/bk_swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/bk_recycler_view"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical" />
</ScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</FrameLayout>
To get a basic idea on how to use SwipeRefreshLayout, refer to this article. Also, refer to the official documentation.
Summary about SwipeRefreshLayout (from official documentation)
The SwipeRefreshLayout should be used whenever the user can refresh the contents of a view via a vertical swipe gesture. The activity that instantiates this view should add an OnRefreshListener to be notified whenever the swipe to refresh gesture is completed. The SwipeRefreshLayout will notify the listener each and every time the gesture is completed again; the listener is responsible for correctly determining when to actually initiate a refresh of its content. If the listener determines there should not be a refresh, it must call setRefreshing(false) to cancel any visual indication of a refresh. If an activity wishes to show just the progress animation, it should call setRefreshing(true). To disable the gesture and progress animation, call setEnabled(false) on the view.
This layout should be made the parent of the view that will be refreshed as a result of the gesture and can only support one direct child. This view will also be made the target of the gesture and will be forced to match both the width and the height supplied in this layout. The SwipeRefreshLayout does not provide accessibility events; instead, a menu item must be provided to allow refresh of the content wherever this gesture is used.

Categories

Resources