Spotify ListView Header Image Effect

The Android version of Spotify has a unique ListView title effect when viewing an artist. Basically, the header image appears to support its own scroll speed, different from the actual list. Does anyone know what I'm talking about? If so, can someone explain how to achieve this effect?

Here is a link to the video that depicts the effect of the header image. I mean:

http://servestream.sourceforge.net/20130911_200347.mp4

+17
android android-listview
Jul 29 '13 at 19:42 on
source share
4 answers

I created an open source library that doses only what you need - https://github.com/nirhart/ParallaxScroll can you see an example here - https://play.google.com/store/apps/details? id = com.nirhart.parallaxscrollexample

+21
Mar 08 '14 at 20:38
source share

Thanks for posting the video. This is the parallax effect. The following library can help you with this:

ParallaxScrollView: A Parallax ScrollView that takes background and foreground in a ParallexScrollView.

Link

So, I went ahead and changed the demo offered by the link. If this is what you need, let me know and I will add details about the changes that I made to make it work.

APK Link

How to do it:

If there was anything besides the ListView in the scrollable part, the effect would be easy to achieve. Since the container containing the ListView is an extended ScrollView, things get more complicated. The following changes have been made:

In this operation, inflate the following layout:

<couk.jenxsol.parallaxscrollview.views.ParallaxScrollView xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/scroll_view" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".DemoActivity" > <!-- Top Image: Here, the height is set to 300dp. You can set this in code --> <!-- depending on screen dimensions --> <ImageView android:id="@+id/iv" android:layout_width="match_parent" android:layout_height="300dp" android:gravity="center" android:scaleType="fitXY" android:src="@drawable/image_to_use" /> <!-- Foreground --> <!-- You can place any of the items below as the foreground, --> <!-- but for most control, add the scroll view yourself. --> <!-- This is the area that will hold the ListView --> <!-- Also note that the LinearLayout top margin will <!-- depend on ImageView height. Here, there an overalp of 100dp --> <!-- between the ImageView and the LinearLayout --> <!-- Reason: The first TextView(with a transparent background) inside the <!-- LinearLayout is displayed over the ImageView. --> <!-- So, the overlapping height should be equal to first TextView height --> <!-- LinearLayout top margin = ImageView height - firstTextView height --> <!-- AnotherView is an extended LinearLayout that I added to the library --> <couk.jenxsol.parallaxscrollview.views.AnotherView android:id="@+id/anotherView" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:id="@+id/llMainHolder" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="200dp" android:orientation="vertical" > <TextView android:id="@+id/tvTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/transparent" android:gravity="center" android:padding="@dimen/spacing" android:text="Parallax Effect" android:textColor="@android:color/white" android:textSize="21sp" tools:ignore="NewApi" /> <!-- ListView --> <LinearLayout android:id="@+id/llMain" android:layout_width="match_parent" android:layout_height="wrap_content" > <ListView android:id="@+id/lvMain" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="@android:color/black" android:dividerHeight="2px" > </ListView> </LinearLayout> </LinearLayout> </couk.jenxsol.parallaxscrollview.views.AnotherView> </couk.jenxsol.parallaxscrollview.views.ParallaxScrollView> 

Operation code:

 public class DemoActivity extends Activity { private ParallaxScrollView mScrollView; private ListView lvMain; private LinearLayout llMain, llMainHolder; private AnotherView anotherView; private ImageView iv; private TextView tvTitle; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Inflated layout View mContent = getLayoutInflater().inflate(R.layout.activity_demo, null); // Initialize components mScrollView = (ParallaxScrollView) mContent.findViewById(R.id.scroll_view); llMain = (LinearLayout) mContent.findViewById(R.id.llMain); llMainHolder = (LinearLayout) mContent.findViewById(R.id.llMainHolder); lvMain = (ListView) mContent.findViewById(R.id.lvMain); iv = (ImageView) mContent.findViewById(R.id.iv); tvTitle = (TextView) mContent.findViewById(R.id.tvTitle); anotherView = (AnotherView) mContent.findViewById(R.id.anotherView); String[] array = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "evelen", "twelve", "thirteen", "fourteen"}; ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.text, array); lvMain.setAdapter(adapter); // Set Content setContentView(mContent); lvMain.post(new Runnable() { @Override public void run() { // Adjusts llMain height to match ListView height setListViewHeight(lvMain, llMain); // LayoutParams to set the top margin of LinearLayout holding // the content. // topMargin = iv.getHeight() - tvTitle.getHeight() LinearLayout.LayoutParams p = (LinearLayout.LayoutParams)llMainHolder.getLayoutParams(); p.topMargin = iv.getHeight() - tvTitle.getHeight(); llMainHolder.setLayoutParams(p); } }); } // Sets the ListView holder height public void setListViewHeight(ListView listView, LinearLayout llMain) { ListAdapter listAdapter = listView.getAdapter(); if (listAdapter == null) { return; } int totalHeight = 0; int firstHeight = 0; int desiredWidth = MeasureSpec.makeMeasureSpec( listView.getWidth(), MeasureSpec.AT_MOST); for (int i = 0; i < listAdapter.getCount(); i++) { if (i == 0) { View listItem = listAdapter.getView(i, null, listView); listItem.measure(desiredWidth, MeasureSpec.UNSPECIFIED); firstHeight = listItem.getMeasuredHeight(); } totalHeight += firstHeight; } LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)llMain.getLayoutParams(); params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); llMain.setLayoutParams(params); anotherView.requestLayout(); } } 

Presented by a library containing content (ObservableScrollView) extends ScrollView. this caused problems with the ListView that you want to display. I added AnotherView , which extends LinearLayout instead:

 public class AnotherView extends LinearLayout { private ScrollCallbacks mCallbacks; static interface ScrollCallbacks { public void onScrollChanged(int l, int t, int oldl, int oldt); } public void setCallbacks(ScrollCallbacks listener) { mCallbacks = listener; } public AnotherView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public void draw(Canvas canvas) { super.draw(canvas); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (mCallbacks != null) { mCallbacks.onScrollChanged(l, t, oldl, oldt); } } @Override public int computeVerticalScrollRange() { return super.computeVerticalScrollRange(); } } 

Finally: the library provides a parallax effect. The effect in your video is the reverse parallax effect. To get the desired result, a small change is required in ParallaxScrollView.onLayout() . Instead of final int scrollYCenterOffset = -mScrollView.getScrollY() use final int scrollYCenterOffset = mScrollView.getScrollY() .

Modified library: Link .

Demo project: Link .

APK (revised / c ListView): Link .

+28
Sep 12 '13 at 5:03 on
source share

You can try using FadingActionBar to play back how Google Play Music handles artist titles

0
Jul 29 '13 at 19:45
source share

This is done just like that if you have a scrollview containing an image that you both refer to:

  scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { int top = scrollView.getScrollY(); // Increases when scrolling up ^ int newTop = (int) (top * .5f); imageFrame.setTranslationY(newTop < 0 ? 0 : newTop); } }); 

This will allow you to scroll the image up at half speed compared to the rest of the scroll, and also verify that it never scrolls more than it should (minute 0)

0
May 22 '15 at 9:45
source share



All Articles