Content with multiple RecyclerView and a ViewPager not scrolling inside NestedScrollView - android

I'm facing a strange problem. I have a xml with two RecyclerView and a ViewPager with circle page indicator inside a NestedScrollview. I have used layout_weight and weightSum to show widget on screen. One of RecyclerView has horizontal layout which is scrolling horizontally fine. I want to achieve single vertical scrolling but unfortunately it is not working.
I have used "app:layout_behavior="#string/appbar_scrolling_view_behavior" in xml and "recyclerview.setNestedScrollingEnabled(false)" in java code.
here is my fragment_home.xml
<android.support.v4.widget.NestedScrollView 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:id="#+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
android:fillViewport="true"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="com.e2e.qnamo.fragment.HomeFragment">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="10"
android:descendantFocusability="blocksDescendants">
<include
layout="#layout/layout_pager_indicator"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_hot_topic"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="20dp"
android:layout_weight="2"
tools:listitem="#layout/hot_topic_row" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_category"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="20dp"
android:layout_weight="5"
tools:listitem="#layout/category_row"/>
</LinearLayout>
here is my layout_pager_indicator.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="#+id/bannerViewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="#layout/viewpager_indicator_single_item" />
<com.e2e.qnamo.widget.CirclePageIndicator
android:id="#+id/pager_indicator"
style="#style/CustomCirclePageIndicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_gravity="center|bottom"/>

Seems like every one was busy else where :) Anyway I managed to find solution by myself.
There were three potential problem which causing problem:
Using "weightSum" in parent layout and
ViewPager in layout
ViewPager takes whole screen by default.
I have used "weightSum" to control size of ViewPager and showing other two Recyclerview. To remove "weightSum" I customised ViewPager so that it only takes height of its biggest children. Below are the customised ViewPager code:
public class CustomViewPager extends ViewPager
{
private int mCurrentPagePosition = 0;
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
for(int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if(h > height) height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void reMeasureCurrentPage(int position) {
mCurrentPagePosition = position;
requestLayout();
}
}
Second step I did that I removed "layout_weight" from both of RecyclerView and set it "layout_height" to "wrap_content" and work done.
Below are the updated layout code:
fragment_home.xml
<android.support.v4.widget.NestedScrollView 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:id="#+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
android:fillViewport="true"
tools:context="com.e2e.qnamo.fragment.HomeFragment">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="#dimen/main_container_margin"
android:layout_marginTop="#dimen/main_container_margin"
android:layout_marginRight="#dimen/main_container_margin"
android:layout_marginBottom="#dimen/main_container_margin"
android:orientation="vertical">
<include
layout="#layout/layout_pager_indicator"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_hot_topic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/recycler_margin_top"
tools:listitem="#layout/hot_topic_row" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_category"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/recycler_margin_top"
android:layout_marginBottom="10dp"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:listitem="#layout/category_row"/>
</LinearLayout>
layout_pager_indicator.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.e2e.qnamo.widget.CustomViewPager
android:id="#+id/bannerViewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="#layout/viewpager_indicator_single_item" />
<com.e2e.qnamo.widget.CirclePageIndicator
android:id="#+id/pager_indicator"
style="#style/CustomCirclePageIndicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_gravity="center|bottom"/>
Thats it!!!

Related

SquareView does not layout it's children correctly

I've a simply layout like following for a custom view:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="#+id/someInnerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="#+id/iv_arrow"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerHorizontal="true"
android:src="#drawable/arrow"/>
</RelativeLayout>
And this function in my custom view:
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mForceSquareLayout) {
int w = getMeasuredWidth();
int h = getMeasuredHeight();
if (w != h) {
int size = Math.min(w, h);
setMeasuredDimension(size, size);
}
}
}
Any my layout looks like following:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
style="?toolbar_view_theme"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?colorPrimary"
android:elevation="16dp" />
<CustomView
android:id="#+id/wheel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="16dp"
app:square_layout="true"
app:arrow_image="#drawable/ic_arrow_drop_down_white_24dp"
app:background_color="?colorPrimary" />
</LinearLayout>
</layout>
The problem is, that if the square logic does resize the view (which it does in landscape mode), my custom views child iv_arrow is not shown anymore. It looks like my square logic does not relayout the children. Any ideas how to solve this?
I tried requestLayout on the view and on the child with no success...

Using weightsum to adjust layout?

This is the screenshot of one of my fragments (device: Xiaomi Redmi 2)
The fragment is divided into 4 parts:
A ViewPager to display image ads
A ViewPager indicator
A RecyclerView which contains a bunch of CardViews (to display news)
An AHBottomNavigation
And this is its corresponding XML layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="3">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:orientation="vertical"
android:weightSum="1.5">
<!-- for displaying ads -->
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0" />
<LinearLayout
android:id="#+id/llDots"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_weight="0.5"
android:gravity="center"
android:background="#android:color/black"
android:orientation="horizontal" />
</LinearLayout>
<!-- for displaying news -->
<android.support.v7.widget.RecyclerView
android:id="#+id/news_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1.5" />
<!-- bottom navigation -->
<com.aurelhubert.ahbottomnavigation.AHBottomNavigation
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
I think the height of the ViewPager Indicator and the AHBottomView is right. But the ViewPager height is too much. What I want is the height of the ViewPager to fit the height of the image ads, and the height of the RecyclerView is bigger. I tried reducing the value weightSum (of the top LinearLayout) from 3 to 2.5, turns out the the AHbottomNavigation is not visible anymore.
Change like below
1. Replace xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical"
>
<!-- for displaying ads -->
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<LinearLayout
android:id="#+id/llDots"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:background="#android:color/black"
android:orientation="horizontal" />
<!-- for displaying news -->
<android.support.v7.widget.RecyclerView
android:id="#+id/news_list"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
/>
</LinearLayout>
<!-- bottom navigation -->
<com.aurelhubert.ahbottomnavigation.AHBottomNavigation
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
2.change view pager with wrap content
You can use the below code.
public class WrapContentViewPager extends ViewPager {
private int mCurrentPagePosition = 0;
public WrapContentViewPager(Context context) {
super(context);
}
public WrapContentViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
View child = getChildAt(mCurrentPagePosition);
if (child != null) {
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
}
} catch (Exception e) {
e.printStackTrace();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void reMeasureCurrentPage(int position) {
mCurrentPagePosition = position;
requestLayout();
}
}
Declare it in xml:
<your.package.name.WrapContentViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</your.package.name.WrapContentViewPager>
And call reMeasureCurrentpage function on page swipe.
final WrapContentViewPager wrapContentViewPager = (WrapContentViewPager) findViewById(R.id.view_pager);
wrapContentViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
wrapContentViewPager.reMeasureCurrentPage(wrapContentViewPager.getCurrentItem());
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
happy coding :)

RecyclerView in SwipeRefreshLayout not "wrap_content"

I have a RecyclerView as the only child of SwipeRefreshLayout, I want the RecyclerView wrap_content. When I set both of them "wrap_content", it doesn't work. The RecyclerView with fewer items also match_parent. When i delete SwipeRefreshLayout, the RecyclerView will wrap_content. Can anyone help me? My English is poor, Maybe you cann't understand. Anyone can help me? Thank you very much.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container_v"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00ffff">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ff00ff">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
Finally, I work out it. From this Answer
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container_v"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00ffff">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ff00ff"/>
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
The key is to use Layout wrap RecyclerView. Beacause of SwipeRefreshLayout use MeasureSpec.EXACTLY mode measure mTarget size when onMeasure():
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mTarget == null) {
ensureTarget();
}
if (mTarget == null) {
return;
}
mTarget.measure(MeasureSpec.makeMeasureSpec(
getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY));
......
}
And mTarget is the first child view of SwipeRefreshLayout. So this will cause the mTarget always match parent.
private void ensureTarget() {
// Don't bother getting the parent height if the parent hasn't been laid
// out yet.
if (mTarget == null) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (!child.equals(mCircleView)) {
mTarget = child;
break;
}
}
}
}
FrameLayout is used because it is more lightweight.
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
tools:itemCount="2"
tools:listitem="#layout/item_msg_detail_text_style" />
</FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

ListView is not scrolling inside NestedScrollView in android

I've inflated a fragment from view pager which uses the listview. And list view does not support setNestedScrollingEnabled in pre lollipop devices. So I've added the listview inside a NestedScrollView but when scrolling the list it does not scrolling.
<RelativeLayout
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:orientation="vertical"
android:id="#+id/fragment_item_view"
android:background="#color/white"
android:isScrollContainer="true">
<ProgressBar
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/progressBar"
android:layout_centerInParent="true" />
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:fillViewport="true">
<ListView
android:id="#+id/list_item_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:clipToPadding="false"
android:divider="#color/gray_stroke_color"
android:dividerHeight="0.5dp"
android:paddingBottom="#dimen/padding_64dp" >
</ListView>
</android.support.v4.widget.NestedScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
Can anyone suggest me any solution. Thanks in advance.
Try using
setNestedScrollingEnabled(true);
yourtListViewName.setNestedScrollingEnabled(true);
Use ViewCompat.setNestedScrollingEnabled() and your problem will be solved.
I am not sure why you would want to use a ListView inside a ScrollView if it will take up the entire height/width of it's parent.
I would suggest just using the ListView in the root layout of your fragment and eliminate the ScrollView.
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<ListView
android:id="#+id/list_item_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:clipToPadding="false"
android:divider="#color/gray_stroke_color"
android:dividerHeight="0.5dp"
android:paddingBottom="#dimen/padding_64dp"/>
</ListView>
</android.support.v4.widget.SwipeRefreshLayout>
Also, I would suggest using RecyclerView instead of ListView as it implements NestedScrollingChild.
But if you are set on having nested scroll views in your layout you need to understand that NestedScrollView is intended to work as parent or a child scroll view while list. Here is a good example of a scrollview containing 2 other scrollviews:
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="100dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</android.support.v4.widget.NestedScrollView>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="100dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
</ScrollView>
Also, I noticed that you are maybe using CoordinatorLayout because you are setting the layout behavior of your child views. If so, you only need to specify the 'app:layout_behavior="#string/appbar_scrolling_view_behavior"' for your main and only ScrollView: A ListView, RecyclerView or NestedScrollView.
You have to intercept the touch event for smooth scrolling
outerScrollView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
findViewById(R.id.inner_scroll).getParent()
.requestDisallowInterceptTouchEvent(false);
return false;
}
});
innerScrollView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
its work if add this code
android:nestedScrollingEnabled="true"
<ExpandableListView
android:id="#+id/lvExp"
android:nestedScrollingEnabled="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="right"
/>
Inside the XML file, while defining the ListView, kindly write,
android:nestedScrollingEnabled="true"
It will solve the issue
Create a Custom NonScrollListView
NonScrollListView.java
public class NonScrollListView extends ListView {
public NonScrollListView(Context context) {
super(context);
}
public NonScrollListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NonScrollListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}
customListView
<?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"
android:fadingEdgeLength="0dp"
android:fillViewport="true"
android:overScrollMode="never"
android:scrollbars="none" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<!-- com.Example Changed with your Package name -->
<com.Example.NonScrollListView
android:id="#+id/lv_nonscroll_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</com.Example.NonScrollListView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/lv_nonscroll_list" >
</RelativeLayout>
</RelativeLayout></ScrollView>
In Java File
Create a object of your customListview instead of ListView like :
NonScrollListView non_scroll_list = (NonScrollListView) findViewById(R.id.lv_nonscroll_list);

Scroll a hidden view/layout from bottom

This is what I want to achieve :
I wanted to use AbsoluteLayout but it is deprecated.
So I made a RelativeLayout beneath the blue view in the image above, and then put everything inside a ScrollView, but the hidden view is still 'on' the blue view, and not below it. Also, the screen scrolls, but the hidden part is just cut , and instead I see the my app's default background..
Any ideas?
EDIT :
my current try :
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:fillViewport="true"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical" >
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="#drawable/imageView" />
<LinearLayout
android:id="#+id/centerHolder"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical" >
.....
.....
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="1000dp"
android:layout_below="#id/main_holder"
android:background="#color/black_color">
</RelativeLayout>
</RelativeLayout>
</ScrollView>
I am taking this from a project of mine which displays a RecyclerView where you can add data if you click on a row - because the click "opens" the bottom sheet.
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="#+id/rl_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.fragment.BlockFragment">
<include
android:id="#+id/ll_header"
layout="#layout/layout_header_names" />
<include
android:id="#+id/divider_header"
layout="#layout/layout_divider_horizontal"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="#+id/ll_header" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_block"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/divider_footer"
android:layout_below="#+id/divider_header" />
<include
android:id="#+id/divider_footer"
layout="#layout/layout_divider_horizontal"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#767676"
android:layout_above="#+id/ll_footer" />
<include
android:id="#+id/ll_footer"
layout="#layout/layout_footer_score"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
<!-- Here comes my bottom sheet.
It is wrapped inside a FrameLayout, because an include cannot
have a behaviour. The included layout is every layout you
can imagine - mine is a RelativeLayout with two EditTexts
for example. The layout_behaviour is the second important line. -->
<FrameLayout
android:id="#+id/container_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#e3e3e3"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
<include layout="#layout/layout_bottom_sheet"/>
</FrameLayout>
</android.support.design.widget.CoordinatorLayout>
For the behaviour itself, you'll need to get the FrameLayout (the View with the app:layout_behavior="android.support.design.widget.BottomSheetBehavior").
private BottomSheetBehavior bottomSheetBehavior;
bottomSheetBehavior = BottomSheetBehavior.from((FrameLayout)findViewById(R.id.container_bottom_sheet);
//for the sheet to "peek":
bottomSheetBehavior.setPeekHeight(200);
//now you can set the states:
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
It is also possible to set a BottomSheetCallback() in which you can get all the state changes and also the slideOffset!
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
switch (newState) {
case BottomSheetBehavior.STATE_DRAGGING:
case BottomSheetBehavior.STATE_EXPANDED:
break;
case BottomSheetBehavior.STATE_COLLAPSED:
default:
}
}
#Override
public void onSlide(#NonNull View bottomSheet, float slideOffset) {
}
});
I think you should just use LinearLayout to wrap the ImageView and other layouts.
Edited the answer based on comments.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:fillViewport="true"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/centerHolder"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical" >
.....
.....
</LinearLayout>
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="#drawable/imageView"
android:layout_alignParentTop="true" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="1000dp"
android:layout_below="#+id/centerHolder"
android:background="#color/black_color">
</RelativeLayout>
</RelativeLayout>
</ScrollView>
Just replace your ScrollView with NestedScrollView
Example:
<android.support.v4.widget.NestedScrollView
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"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
I realized you need something like this, just tested it out , just copy two files and see how it works.
import android.animation.ObjectAnimator;
import android.content.Context;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ScrollView;
public class MainActivity extends AppCompatActivity {
LinearLayout movableLL;
boolean mDragLockGained;
float initialTouchPoint;
// int cutoffElevation;
boolean inAnimation;
ScrollView scrollView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
scrollView = (ScrollView) findViewById(R.id.scrollView);
movableLL = (LinearLayout) findViewById(R.id.mobileLL);
movableLL.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (inAnimation) return true;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDragLockGained = true;
initialTouchPoint = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
performTranslation(event);
initialTouchPoint = event.getRawY();
break;
case MotionEvent.ACTION_UP:
mDragLockGained = false;
performSettlingAnimation();
break;
}
return true;
}
});
}
private void performTranslation(MotionEvent event) {
float newDelta = event.getRawY() - initialTouchPoint;
float currentY = movableLL.getTranslationY();
if (currentY + newDelta > 0) {
movableLL.setTranslationY(currentY + newDelta);
if (scrollView.getTranslationY() + newDelta < 0){
}
scrollView.setTranslationY(scrollView.getTranslationY() + newDelta);
} else {
movableLL.setTranslationY(currentY);
scrollView.setTranslationY(scrollView.getTranslationY());
}
}
final int SETTLE_ANIMATION_DURATION = 300;
private void performSettlingAnimation() {
ObjectAnimator animLl = ObjectAnimator.ofFloat(movableLL, "translationY",
movableLL.getTranslationY(), convertDpToPixels(this, 150));
inAnimation = true;
animLl.setDuration(SETTLE_ANIMATION_DURATION);
animLl.start();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
inAnimation = false;
}
}, SETTLE_ANIMATION_DURATION);//use animationListener here as a best practice and toggle values in
//on animation start and cancel/finish
ObjectAnimator animSv = ObjectAnimator.ofFloat(scrollView, "translationY", scrollView.getTranslationY()
, 0);
animSv.setDuration(SETTLE_ANIMATION_DURATION);
animSv.start();
}
public static int convertDpToPixels(Context context, int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()
);
}
}
and the xml......
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center"
android:text="Hello There" />
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center"
android:text="Hello Again" />
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center"
android:text="WhatsUp" />
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center"
android:text="See ya" />
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="#+id/mobileLL"
android:layout_width="match_parent"
android:layout_height="300dp"
android:translationY="150dp"
android:layout_gravity="bottom"
android:background="#android:color/white"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#99ff0000" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#990000ff" />
</LinearLayout>
Please let me know if I missed something.
For this purposes the best way is to use BottomSheet from Design Support Library from my point of view.
<?xml version="1.0" encoding="utf-8"?><android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="1000dp"
android:background="#android:color/white">
<!-- other layout -->
</RelativeLayout>
</RelativeLayout>
</ScrollView>
<FrameLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:behavior_peekHeight="120dp"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
<View
android:layout_width="match_parent"
android:layout_height="240dp"
android:background="#android:color/darker_gray" />
</FrameLayout>
</android.support.design.widget.CoordinatorLayout>
It also possible to combine it with your own a CoordinatorLayout Behavior to change scroll position for your scroll view, if needed.

Categories

Resources