ListView with Parallax Header Android

     As you can see at my previous post (about make Parallax Toolbar), with Material Design style, developer can make a lot of animations with Toolbar/ActionBar when scrolling screen. Unfortunately, we can only make them with RecyclerView or NestedScrollView, it is a pity that ListView and other "traditional" scrollable views have not been supported. If we would like to create these effections, we must handle ListView scroll event (through OnScrollListener). Today, in this post, I will provide a solution to make ListView with parallax header, please see this DEMO VIDEO to see output first:

Design layouts

    Firstly, providing activity layout contains a ListView and ImageView included in a FrameLayout as root view (always use FrameLayout, remember!):
    TextView in this layout acts as ListView header then.
    Now, design a "reality" ListView header, only includes two Spaces (subclass of View, used to occupy invisible, transparent space on the screen):
    These two Spaces height must be same with ImageView and TextView in activity layout, these invisible views will be useful to calculate the view position and will help to create the parallax effect.
    Adding a layout for each ListView item to complete xml design:
    Always providing a background for each rows like above, when ListView scrolled, this background will cover the ImageView.

Activity programmatically code

    Handling ListView scroll event is the point in code. We will check if the first ListView item is reached to top and set image header scrolls half of the amount that of ListView:
private AbsListView.OnScrollListener onScrollListener () {
        return new AbsListView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

                // Check if the first item is already reached to top
                if (listView.getFirstVisiblePosition() == 0) {
                    View firstChild = listView.getChildAt(0);
                    int topY = 0;
                    if (firstChild != null) {
                        topY = firstChild.getTop();
                    }

                    int headerTopY = headerSpace.getTop();
                    headerText.setY(Math.max(0, headerTopY + topY));

                    // Set the image to scroll half of the amount that of ListView
                    headerView.setY(topY * 0.5f);
                }
            }
        };
    }

Final code

    Over here, impotant codes have done. Adding some necessary methods, we have complete activity code:
package info.devexchanges.parallaxheaderlistview;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private TextView headerText;
    private ListView listView;
    private View headerView;
    private View headerSpace;

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

        listView = (ListView) findViewById(R.id.list_view);
        headerView = findViewById(R.id.header_image_view);
        headerText = (TextView) findViewById(R.id.header_text);

        setListViewHeader();
        setListViewData();

        // Handle list View scroll events
        listView.setOnScrollListener(onScrollListener());
    }

    private void setListViewHeader() {
        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        @SuppressLint("InflateParams") View listHeader = inflater.inflate(R.layout.listview_header, null, false);
        headerSpace = listHeader.findViewById(R.id.header_space);

        listView.addHeaderView(listHeader);
    }

    private void setListViewData() {
        List<String> modelList = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            modelList.add("Item " + (i+1));
        }

        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.item_listview, R.id.item, modelList);
        listView.setAdapter(adapter);
    }

    private AbsListView.OnScrollListener onScrollListener () {
        return new AbsListView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

                // Check if the first item is already reached to top
                if (listView.getFirstVisiblePosition() == 0) {
                    View firstChild = listView.getChildAt(0);
                    int topY = 0;
                    if (firstChild != null) {
                        topY = firstChild.getTop();
                    }

                    int headerTopY = headerSpace.getTop();
                    headerText.setY(Math.max(0, headerTopY + topY));

                    // Set the image to scroll half of the amount that of ListView
                    headerView.setY(topY * 0.5f);
                }
            }
        };
    }
}
    Dimensions resource (ImageView and ListView header height):

Output & Conclusions

    After running app, we have this screen:

    With some simple steps in code, we now have a ListView with parallax header. From now on, you can see my previous post to learn about this effect with RecyclerView (recomended use it instead of ListView) or go to this post to know a powerful libary which making this "scroll style". And, finally, see full project source code on @Github.

Share


Previous post
« Prev Post
Next post
Next Post »