Эффект изображения заголовка Spotify ListView

Версия Android Spotify имеет уникальный эффект заголовка ListView при просмотре исполнителя. В основном изображение заголовка, похоже, поддерживает собственную скорость прокрутки, отличную от фактического списка. Кто-нибудь знает, о чем я говорю? Если да, может кто-нибудь объяснить, как добиться этого эффекта?

Вот ссылка на видео, в котором изображен эффект изображения заголовка. Я имею в виду:

Solutions Collecting From Web of "Эффект изображения заголовка Spotify ListView"

Я создал библиотеку с открытым исходным кодом, которая дозирует только то, что вам нужно – https://github.com/nirhart/ParallaxScroll вы можете увидеть здесь пример – https://play.google.com/store/apps/details?id=com .nirhart.parallaxscrollexample

Благодарим за размещение видео. Это параллаксный эффект. Следующая библиотека может помочь вам в этом:

ParallaxScrollView: Parallax ScrollView, который отображает фоновый и передний план в ParallexScrollView.

Ссылка

Итак, я пошел дальше и изменил демо, предложенное по ссылке. Если это то, что вам нужно, сообщите мне, и я добавлю подробные сведения о тех изменениях, которые я сделал, чтобы заставить это работать.

Ссылка APK

Как это получить:

Если в прокручиваемой части было что угодно, кроме ListView, эффекта было бы легко достичь. Поскольку контейнер, содержащий ListView, является расширенным ScrollView, все становится сложнее. Были внесены следующие изменения:

В этом упражнении раздуйте следующий макет:

<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's top margin will <!-- depend on ImageView's height. Here, there's 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's height --> <!-- LinearLayout's top margin = ImageView's height - firstTextView's 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> 

Код мероприятия:

 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's height to match ListView's 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's 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(); } } 

Представление библиотеки, которая содержит контент (ObservableScrollView), расширяет ScrollView. Это вызывало проблемы с ListView, которые вы хотите отобразить. Я добавил AnotherView который расширяет LinearLayout, а не:

 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(); } } 

Наконец: библиотека обеспечивает эффект параллакса. Эффект в вашем видео – эффект обратного параллакса. Чтобы получить желаемый результат, требуется небольшое изменение в ParallaxScrollView.onLayout() . Вместо final int scrollYCenterOffset = -mScrollView.getScrollY() используйте final int scrollYCenterOffset = mScrollView.getScrollY() .

Измененная библиотека: ссылка .

Демо-проект: ссылка .

APK (пересмотренный / с ListView): ссылка .

Вы можете попытаться использовать FadingActionBar для репликации того, как Google Play Music обрабатывает его заголовки артистов

Это делается просто так, если у вас есть scrollview, содержащий изображение, к которому у вас есть ссылки:

  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); } }); 

Это позволит прокрутить изображение вверх с половинной скоростью по сравнению с остальной частью scrollview, а также проверяет, что она никогда не прокручивается больше, чем она должна (минус 0)