Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Collapsing Linearlayout instead of Collapsing Toolbar

I'm trying to create a Master/Detail transaction in a single fragment. I thought of using LinearLayout as the container of my edittext for my header. Then a RecyclerView for details.

How would one implement the collapsing/expanding of LinearLayout similar to that of the CollapsingToolbar effect?

Here's a screenshot of what I'm trying to do.

enter image description here

My xml code so far.

<?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="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:background="@color/colorAccent"
        android:padding="@dimen/activity_horizontal_margin">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:src="@drawable/ic_date_range_black_24dp"
                android:tint="@android:color/darker_gray" />

            <android.support.v4.widget.Space
                android:layout_width="@dimen/activity_horizontal_margin"
                android:layout_height="wrap_content" />

            <android.support.design.widget.TextInputLayout
                android:id="@+id/date_til"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Date">

                <android.support.design.widget.TextInputEditText
                    android:id="@+id/date"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:cursorVisible="false"
                    android:focusable="false"
                    android:longClickable="false" />

            </android.support.design.widget.TextInputLayout>

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:src="@drawable/ic_store_black_24dp"
                android:tint="@android:color/darker_gray" />

            <android.support.v4.widget.Space
                android:layout_width="@dimen/activity_horizontal_margin"
                android:layout_height="wrap_content" />

            <android.support.design.widget.TextInputLayout
                android:id="@+id/store_til"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Store">

                <android.support.design.widget.TextInputEditText
                    android:id="@+id/store"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content" />

            </android.support.design.widget.TextInputLayout>

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:src="@drawable/ic_place_black_24dp"
                android:tint="@android:color/darker_gray" />

            <android.support.v4.widget.Space
                android:layout_width="@dimen/activity_horizontal_margin"
                android:layout_height="wrap_content" />

            <android.support.design.widget.TextInputLayout
                android:id="@+id/location_til"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <android.support.design.widget.TextInputEditText
                    android:id="@+id/location"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="Location" />

            </android.support.design.widget.TextInputLayout>

        </LinearLayout>

    </LinearLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        app:layoutManager="LinearLayoutManager"
        tools:listitem="@layout/list_car" />

</LinearLayout>

Also, not sure if this can be done with the CollapsingToolbar since I've mostly seen only ImageView collapsing in the Toolbar.

Appreaciate any help.

UPDATE: Basically what I want to do here is to be able to collapse the header view when scrolling up. Then expand when scrolling down.

like image 337
ads Avatar asked Aug 25 '16 05:08

ads


People also ask

How do I know if my collapsing Toolbar is collapsed?

To detect if fully collapsed check if Math. abs(offset) == appBarLayout.

How do I fix the view to the bottom of LinearLayout?

You can set the layout_height="0dp" of your header, footer and ScrollView and define a layout_weight .


2 Answers

You can use a timer and gradually shrink the height/margin of the topbar's LinearLayout (Edit: flaws in Anton Maiorov's answer is fixed here)

enter image description here

See below snippet (tested on devices)

Approach I: Anton Maiorov's answer, flaws fixed, this is much simpler than the 2nd implementation below

public class MainActivity extends AppCompatActivity {

    LinearLayout toolbar;
    int mOriginalHeight;
    boolean initialSizeObtained = false;    
    boolean isShrink = false;

    Animation _hideAnimation = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) toolbar.getLayoutParams();
            params.topMargin = -(int) (mOriginalHeight * interpolatedTime);
            toolbar.setLayoutParams(params);
        }
    };

    Animation _showAnimation = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) toolbar.getLayoutParams();
            params.topMargin = (int) (mOriginalHeight * (interpolatedTime - 1));
            toolbar.setLayoutParams(params);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        toolbar = (LinearLayout) findViewById(R.id.toolbar);
        //Get the original height, which is measured according to WRAP_CONTENT
        toolbar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (initialSizeObtained)
                    return;
                initialSizeObtained = true;
                mOriginalHeight = toolbar.getMeasuredHeight();
            }
        });
        _hideAnimation.setDuration(2000);
        _showAnimation.setDuration(2000);
    }

    //Click on the Olimpic image --> Toggles the top toolbar
    public void ToggleTopBar(View view) {
        isShrink = !isShrink;

        toolbar.clearAnimation();  //Important            
        toolbar.startAnimation(isShrink? _hideAnimation : _showAnimation);
    }
}

Approach II: my original answer by changing the toolbar's height, and also using timer manually, which is more involved:

public class MainActivity extends AppCompatActivity {

    LinearLayout toolbar;
    int mOriginalHeight;
    boolean initialSizeObtained = false;
    int currentHeight;
    boolean isShrink = false;
    Timer timer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        toolbar = (LinearLayout) findViewById(R.id.toolbar);
        toolbar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (initialSizeObtained)
                    return;
                initialSizeObtained = true;
                mOriginalHeight = toolbar.getMeasuredHeight();
                currentHeight = mOriginalHeight;
                Log.d("Demo", "Original height is " + mOriginalHeight);
            }
        });
    }

    //Click on the Olimpic image --> Toggles the top toolbar
    public void ToggleTopBar(View view) {
        isShrink = !isShrink;
        Resize(isShrink, toolbar, 250);
    }


    void Resize(final boolean isShrink, final LinearLayout layout, final int minHeight) {
        final int H0 = mOriginalHeight;
        timer = runTimerAction(10, new Runnable() {
                    public void run() {
                        Log.d("demo", "Current Height= " + currentHeight);
                        if (isShrink && currentHeight > minHeight) {
                            currentHeight -= 10;
                            layout.getLayoutParams().height = currentHeight;
                            refreshToolbar();
                        } else if (!isShrink && currentHeight < H0) {
                            currentHeight += 10;
                            layout.getLayoutParams().height = currentHeight;
                            refreshToolbar();
                        } else {
                            layout.getLayoutParams().height = isShrink ? minHeight : H0;
                            refreshToolbar();
                            if (timer != null)
                                timer.cancel();
                        }
                    }
                }
        );
    }

    public void refreshToolbar() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                toolbar.requestLayout();
            }
        });
    }
like image 86
David Avatar answered Oct 15 '22 08:10

David


Improved my answer using David's comment.

I would animate "topMargin" of your header:

LinearLayout _headerLayout; // expected to be set in "onCreateView"
int _headerHeight; // expected to be set in "onCreateView" as _headerHeight = getHeaderHeight();

Animation _hideAnimation = new Animation() {
  @Override
  protected void applyTransformation(float interpolatedTime, Transformation t) {
    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) _headerLayout.getLayoutParams();
    params.topMargin = -(int) (_headerHeight * interpolatedTime);
    _headerLayout.setLayoutParams(params);
  }
};

Animation _showAnimation = new Animation() {
  @Override
  protected void applyTransformation(float interpolatedTime, Transformation t) {
    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) _headerLayout.getLayoutParams();
    params.topMargin = (int) (_headerHeight * (interpolatedTime - 1));
    _headerLayout.setLayoutParams(params);
  }
};

private int getHeaderHeight()
{
  _headerLayout.measure(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
  return _headerLayout.getMeasuredHeight();
}

Hiding header:

_headerLayout.clearAnimation();
_headerLayout.startAnimation(_hideAnimation);

Showing header:

_headerLayout.clearAnimation();
_headerLayout.startAnimation(_showAnimation);

You can also easily set duration for your animations:

_hideAnimation.setDuration(2000) // will hide in 2 seconds
_showAnimation.setDuration(2000) // will show in 2 seconds
like image 3
Anton Maiorov Avatar answered Oct 15 '22 08:10

Anton Maiorov