10

I'm making a layout similar to recent android's status bar.

enter image description here

I have two Views inside container. ViewPager and RecyclerView. The default behavior should be that when I scroll RecyclerView, I want ViewPager to decrease in size and vice versa.

enter image description here

Logic:

viewPagerMaxHeight = 200;
if scrollTop
  is ViewPager.height > viewPagerMaxHeight?
    YES: Prevent Scroll and Decrease ViewPager size apropriatry
    No: Scroll RecyclerView
if scrollBottom
  did we scroll to position 0?
    YES: Start increasing ViewPager size
    No: Scroll RecyclerView

Few notes: - RecyclerView contains items of various size. - Sometimes items are removed and added - It is a simple RecyclerView, not like in notifications where they collapse on each other.

I can construct most of the logic myself but I could not make a proper listener for RecyclerView which will return direction and amount that was scrolled. preventing RecyclerView from scrolling is a bonus

EDIT:

I have made an example on github

v.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
    @Override
    public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
        Log.e("scrollY", ""+scrollY);
        Log.e("oldScrollY", ""+oldScrollY);
        Log.e("currentHeight", ""+currentHeight);
        if(scrollY == 200) {
            Log.e("==200", "JU");
        } else if (scrollY < 200) {
            Log.e("<200", ""+currentHeight);

            if(currentHeight < fullHeight) {
                Log.e("current<full", Integer.toString(deltaScroll));
                deltaScroll = oldScrollY - scrollY;
                currentHeight = currentHeight + deltaScroll;
                if(currentHeight > fullHeight) {
                    currentHeight = fullHeight;
                }
                ku.getLayoutParams().height = currentHeight;
                ku.requestLayout();
            }
            v.scrollTo(0, 200);
        } else if (scrollY > oldScrollY) {
            Log.e("Scroll DOWN", "" + Integer.toString(scrollY));
            deltaScroll = scrollY - oldScrollY;
            currentHeight = currentHeight - deltaScroll;
            if(currentHeight > minHeight) {
                ku.getLayoutParams().height = currentHeight;
                ku.requestLayout();
                v.scrollTo(0, 200);

            } else {
                currentHeight = minHeight;
                ku.getLayoutParams().height = minHeight;
                ku.requestLayout();
            }

        }
    }
});

I'm setting padding for RecycleView and scrolling NestedScrollView to the first item so the padding is not visible. this allows me to scroll TOP even when already at the TOP.

Everything seems to work, but as you will notice scrolling is "jumpy" when scrolling slowly (won't happen if scrolled fast enough). My guess is that is happening because NestedScrollView itself changes height and while scrolling up for example, scroll down happens as well.

Dipali Shah
  • 3,355
  • 28
  • 46
CBeTJlu4ok
  • 1,002
  • 3
  • 18
  • 45

3 Answers3

3

According to your current code:

Everything seems to work, but as you will notice scrolling is "jumpy" when scrolling slowly...

YES , this is because the method onScrollChange might be called a number of times when a user scroll slowly (due to the system misinterpret that like scroll ==> stop ==> scroll ==> stop ==> scroll ==> stop ==>...) and therefore the logic inside the method will be executed a number of times or/and altogether and since there are calculations and logic (if and else) this may lead to an effect in the responsiveness of the UI. This will not occur if the the user will scroll fast it will called probably once, interpreted as a single scroll.

SOLUTION:

  1. Reduce some tiresome calculations and logic in your code if possible.
  2. Use the Coordinator Layout as a root layout instead of LinearLayout and set some Behaviours that your child Views should follow inside the CoordinatorLayout. Otherwise prepare the logic using a boolean that will make sure that the contents of your method onSrollChange will not be called a number of times but only once when the user finally finishes the whole slow scroll.
Xenolion
  • 9,344
  • 6
  • 25
  • 39
  • I cannot use CoordinatorLayout or ConstraintLayout. number of calls is not a problem. I dont think you spent time to inspect my code. – CBeTJlu4ok Dec 15 '17 at 23:45
  • i read your code! Even in github but the issue is the method being called a number of times! Why don't you try an animation that will load only if the previous animation has finished and any updates of an animation will be saved later after a previous animation has finished. This is user friendly too! – Xenolion Dec 16 '17 at 00:38
  • sounds like an idea, and since there is little time to award bounty here you go – CBeTJlu4ok Dec 16 '17 at 01:06
  • Ooooh thanks and **Happy Coding!**. Its my first answer with a bounty! – Xenolion Dec 16 '17 at 01:09
2

The above is totally doable with the help of ConstraintLayout and Guideline. So when you scroll up set the guideline programmatically to minimum and when you scroll down set the guildline back to normal.

Below is a xml sample for the same

<?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"
    tools:context=".MainActivity">


    <android.support.constraint.Guideline
        android:id="@+id/viewPagerTopGuideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.1" />


    <android.support.constraint.Guideline
        android:id="@+id/viewPagerBottomGuideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.5" />

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/viewPagerBottomGuideline"
        app:layout_constraintTop_toBottomOf="@+id/viewPagerTopGuideline">

    </android.support.v4.view.ViewPager>

    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/viewPagerBottomGuideline"/>

</android.support.constraint.ConstraintLayout>

You can place the views inside a NestedScrollViewas well. Then onscroll you can set the guideline like this

 @BindView(R.id.viewPagerBottomGuideline)
Guideline guideLine;//have Used ButterKnife

ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) guideLine.getLayoutParams();
            params.guidePercent = 0.20f; // 45% // range: 0 <-> 1
            guideLine.setLayoutParams(params);

I have just implemented on scroll up, then you would have to implement the same for scroll down as well.

Jeffy Lazar
  • 1,763
  • 11
  • 19
1

I would suggest you to use CoOrdinate Layout as you main layout (root layout.). then All you need to do is to set this behaviour some thing like Collapsing Toolbar via Java or coding. I have implemented same thing and this link really helped me alot. please click on the link and give it a try.

(https://github.com/teocci/AndroidGuidenotes/wiki/Handling-Scrolls-with-CoordinatorLayout)

A.s.ALI
  • 1,659
  • 1
  • 14
  • 41