Spotify ListView header image effect

后端 未结 4 408
无人共我
无人共我 2020-12-23 00:24

The Android version of Spotify has a unique ListView header effect when viewing an artist. Basically the header image appears to maintain it\'s own scrolling speed apart fro

4条回答
  •  谎友^
    谎友^ (楼主)
    2020-12-23 00:46

    Thanks for posting the video. This is a parallax effect. The following library can help you achieve it:

    ParallaxScrollView: A Parallax ScrollView which takes a background and foreground view, in the ParallexScrollView.

    Link

    So, I went ahead and modified the demo offered on the link. If this is what you are after, let me know and I'll add details on the modifications I did to get this working.

    APK Link

    How to get this:

    If there was anything but a ListView in the scrollable part, the effect would have been easy to achieve. Because the container holding the ListView is an extended ScrollView, things get complicated. The following modifications were made:

    In the activity, inflate the following layout:

    
    
    
    
    
        
    
    
    
    
    
    
    
    
    
    
    
    
    
    
        
    
            
    
                
    
                
    
                
    
                    
    
                    
    
                            
    
            
    
        
    
    
    

    Activity 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 adapter = new ArrayAdapter(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();    
        }
    }
    

    The view provided by the library that holds the content (ObservableScrollView) extends a ScrollView. this was causing problems with the ListView that you want to display. I addedAnotherView that extends a 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();
        }              
    
    }
    

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

    Modified library: Link.

    Demo project: Link.

    APK (revised / with ListView): Link.

提交回复
热议问题