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.
Related
On the screen, I want to show RecyclerView which start from the top and at the end of the RecyclerView, there is a button.
When RecyclerView is not occupying the entire screen then the Button will sit just below the RecyclerView.
When RecylerView is scrollable then in this case button will sit on the bottom of the screen.
I tried this code. But the problem here, RecyclerView sits in the centre. In my case, it will always start from the top(after the action bar).
Following code I tried:
main 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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#b6d7a8" >
<LinearLayout
android:id="#+id/actionBarTermsAndCondition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/dark_gray"
android:gravity="center_vertical"
android:minHeight="70dp"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingTop="10dp"
android:paddingEnd="16dp"
android:paddingBottom="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" >
<ImageView
android:id="#+id/closeButton"
android:layout_width="#dimen/spacing_2x"
android:layout_height="#dimen/spacing_2x"
android:layout_gravity="center_vertical"
android:layout_marginEnd="#dimen/spacing_2_5x"
android:clickable="true"
android:contentDescription="#string/close"
android:foreground="?selectableItemBackground"
android:src="#drawable/cross"
android:visibility="invisible" />
<TextView
android:id="#+id/route"
style="#style/ScreenTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="Import Venue"
android:fontFamily="#font/roboto"
android:text="Import Venue"
android:textColor="#color/white"
tools:text="Something" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/list"
android:name="DataManagementFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="LinearLayoutManager"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="#id/constraint_layout_cloud_access"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/actionBarTermsAndCondition"
app:layout_constraintVertical_chainStyle="packed"
tools:itemCount="3"
tools:listitem="#layout/fragment_data_management_list_row" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraint_layout_cloud_access"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FEAB8C"
android:paddingStart="16dp"
android:paddingTop="10dp"
android:paddingBottom="21dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/list">
<TextView
android:id="#+id/text_cloud_access"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/roboto"
android:text="Cloud access"
android:textColor="#3C3C41"
android:textSize="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Cloud access" />
<TextView
android:id="#+id/text_import_venue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/roboto"
android:text="Import venue"
android:textColor="#3C3C41"
android:textSize="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/text_cloud_access"
tools:text="Import place" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
RecyclerView row 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"
xmlns:tools="http://schemas.android.com/tools"
android:background="#fff2cc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="#dimen/spacing_3x"
android:paddingStart="#dimen/spacing_2x"
android:paddingBottom="#dimen/spacing_2_5x"
android:paddingTop="#dimen/spacing_2_5x"
android:orientation="horizontal"
tools:showIn="#layout/fragment_data_management">
<TextView
android:id="#+id/text_floor_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/roboto"
android:textAppearance="?attr/textAppearanceListItem"
android:textColor="#3C3C41"
android:textSize="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Preloaded" />
<TextView
android:id="#+id/text_venue_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/roboto"
android:textAppearance="?attr/textAppearanceListItem"
android:textColor="#636367"
android:textSize="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/text_floor_name"
tools:text="ABC" />
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="?android:attr/listChoiceIndicatorMultiple"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="#+id/imageView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="?attr/dividerHorizontal" />
</androidx.constraintlayout.widget.ConstraintLayout>
Your RecyclerView is centering between two other layouts. Add
app:layout_constraintVertical_bias="0.0"
to the XML for the RecyclerView to move it to the top. You can look up how bias works in the ConstraintLayout documentation.
I think you can achieve what you want with a guideline. The steps would be the following:
Create an horizontal guideline that sits at the 90% of the screen, meaning that it will be really close to the bottom, on the 10% left is where your button will be when the recycler view becomes scrollable.
Set the bottom constraint of the recycler view to match the top of the guideline and the top of the recycler view to the top of the screen. This will cause that the recycler view appears on the half of your screen.
Set the vertical bias of your recycler view to 0.0, this will stick the recycler view to the top.
Set the top constraint of the button to match the bottom of the recycler view, this will cause that it will stick to the bottom of the recycler view, but since the recycler view (thanks to the guideline) will only grow to 90% of the screen, the button will sit at the bottom of the screen as you want.
It's important to add the following attribute to the recycler view:
app:layout_constrainedHeight="true"
The layout related to the button and recycler view should look something like:
<?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=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constrainedHeight="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#+id/guideline"
app:layout_constraintVertical_bias="0.0"
tools:itemCount="130"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button text"
app:layout_constraintTop_toBottomOf="#+id/list"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.9"/>
</androidx.constraintlayout.widget.ConstraintLayout>
With few items:
With many items:
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).
I am trying to place a Viewpager above another view in constraint layout, however the Viewpager extends all the way to the bottom of parent unless I use a set height such as "150dp". How would I make it so that it only extends until it reaches the top of the "Create a new account" Layout?
Things I have tried:
-Making height of viewpager a set amount of dp (150dp), it works but I prefer to make it match multiple device heights
-Adding constraints Constraint Bottom of Viewpager to top of Create Account Layout, doesn't work
Here is my code for the layout:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:fillViewport="true">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true">
<TextView
android:id="#+id/appname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="MyApp"
android:textColor="#color/colorBlackFont"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.2"/>
<TextView
android:id="#+id/textview_betterexperience"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text=""
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/appname"/>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:drawablePadding="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="48dp"
android:layout_marginEnd="8dp"
android:hint="Username"
android:textSize="14dp"
android:textCursorDrawable="#drawable/cursor_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/textview_betterexperience"
app:layout_constraintWidth_percent=".75"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="1">
<View
android:id="#+id/divider_bottom"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/dividerColor"
android:layout_alignParentTop="true"/>
<TextView
android:id="#+id/textview_newaccount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Create a new account"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:textStyle="bold" />
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
</ScrollView>
The end result I want it to look like would be like this:
app:layout_constraintBottom_toTopOf="#id/unnamed_relativeLayout"
app:layout_constrainedHeight="true" (or set layout_height to 0dp)
The preview might be weirded out but will display once inflated with content in app
Add an id to your Relative layout
<RelativeLayout
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="1">
Then in your viewpager add
app:layout_constraintBottom_toTopOf="#id/relativeLayout"
and set android:layout_height="0dp" so it matches the constraints.
You are missing the bottom constraint for view pager . Use below
app:
layout_constraintBottom_toTopOf="#id/relativeLayoutId"
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()
}
I have a RecyclerView with CardView as its items.
Below is the layout for my main activity.
<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"
tools:context="com.example.personal.newz.ui.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"></android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
And below is the layout for my recycler view items
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="16dp"
card_view:cardCornerRadius="4dp"
>
<android.support.constraint.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.Guideline
android:id="#+id/vertical_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.6" />
<TextView
android:id="#+id/date_tv"
android:layout_width="wrap_content"
android:layout_height="16dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="16dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="2 hours ago" />
<TextView
android:id="#+id/source_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:textStyle="bold"
app:layout_constraintLeft_toRightOf="#id/date_tv"
app:layout_constraintTop_toTopOf="#id/date_tv"
tools:text="BBC News" />
<TextView
android:id="#+id/headline_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="32dp"
android:maxLines="2"
android:textStyle="bold"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="#id/vertical_guideline"
app:layout_constraintTop_toBottomOf="#id/date_tv"
tools:text="Apple admits slowing down older iphones" />
<TextView
android:id="#+id/description_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:maxLines="4"
app:layout_constraintLeft_toLeftOf="#id/date_tv"
app:layout_constraintRight_toLeftOf="#id/vertical_guideline"
app:layout_constraintTop_toBottomOf="#id/headline_tv"
tools:text="Customers have long suspected iPhones slow down over time. Now, Apple has confirmed some models do." />
<ImageView
android:id="#+id/article_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitXY"
app:layout_constraintHorizontal_bias=".7"
app:layout_constraintLeft_toRightOf="#id/vertical_guideline"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#id/source_tv"
tools:src="#drawable/placeholder" />
<ImageView
android:id="#+id/bookmark_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:src="#drawable/bookmark"
app:layout_constraintEnd_toStartOf="#+id/share_image"
app:layout_constraintStart_toEndOf="#+id/vertical_guideline"
app:layout_constraintTop_toBottomOf="#id/article_image"
app:layout_constraintHorizontal_chainStyle="packed"/>
<ImageView
android:id="#+id/share_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/share_icon"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/bookmark_image"
app:layout_constraintTop_toBottomOf="#id/article_image" />
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>
Now when I first start my app the individual cardview's width don't match to the parent and kind of behave like their width is wrap content. After a few seconds the width of all the cardviews adjusts itself to match the parent.
ConstraintLayout does not actually support match_parent for its children. If you try to use match_parent, sometimes it will work, and sometimes it will not. Android Studio is also sort of weird about match_parent, and sometimes will allow it and sometimes will automatically replace it with a hardcoded value that matches the last emulator you ran. Regardless, do not use match_parent for children of a ConstraintLayout.
Instead of android:layout_width="match_parent", use this:
android:layout_width="0dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
Instead of android:layout_height="match_parent", use this:
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
When I make these changes to your RecyclerView tag, the problem goes away.