├── .classpath
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── AndroidManifest.xml
├── README.md
├── ic_launcher-web.png
├── img_sample.gif
├── libs
└── android-support-v4.jar
├── proguard-project.txt
├── project.properties
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ ├── ic_launcher.png
│ └── img_header.png
├── layout
│ ├── activity_parallax.xml
│ └── listview_header.xml
├── menu
│ └── parallax.xml
└── values
│ ├── dimen.xml
│ ├── strings.xml
│ └── styles.xml
└── src
└── com
└── gnod
└── parallaxlistview
├── ParallaxActivity.java
└── ParallaxScollListView.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | ParallaxListView
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ParallaxListView
2 | ================
3 |
4 | A ListView demo with a [parallax effect](http://en.wikipedia.org/wiki/Parallax) header like [Path](https://path.com/).
5 |
6 |
7 |
8 |
9 | The key of the effect is :
10 | android:scaleType="centerCrop"
11 |
12 | request:
13 | android:minSdkVersion="9"
14 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gnod/ParallaxListView/b6002d878ebb3d2efcdc4ee28f6a37043eab7d85/ic_launcher-web.png
--------------------------------------------------------------------------------
/img_sample.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gnod/ParallaxListView/b6002d878ebb3d2efcdc4ee28f6a37043eab7d85/img_sample.gif
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gnod/ParallaxListView/b6002d878ebb3d2efcdc4ee28f6a37043eab7d85/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gnod/ParallaxListView/b6002d878ebb3d2efcdc4ee28f6a37043eab7d85/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gnod/ParallaxListView/b6002d878ebb3d2efcdc4ee28f6a37043eab7d85/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gnod/ParallaxListView/b6002d878ebb3d2efcdc4ee28f6a37043eab7d85/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/img_header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gnod/ParallaxListView/b6002d878ebb3d2efcdc4ee28f6a37043eab7d85/res/drawable-xhdpi/img_header.png
--------------------------------------------------------------------------------
/res/layout/activity_parallax.xml:
--------------------------------------------------------------------------------
1 |
5 |
17 |
--------------------------------------------------------------------------------
/res/layout/listview_header.xml:
--------------------------------------------------------------------------------
1 |
5 |
12 |
--------------------------------------------------------------------------------
/res/menu/parallax.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/res/values/dimen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 | 160dp
9 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ParallaxListView
5 | Settings
6 | Hello world!
7 |
8 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/src/com/gnod/parallaxlistview/ParallaxActivity.java:
--------------------------------------------------------------------------------
1 | package com.gnod.parallaxlistview;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.Menu;
7 | import android.view.View;
8 | import android.widget.ArrayAdapter;
9 | import android.widget.ImageView;
10 |
11 | public class ParallaxActivity extends Activity {
12 |
13 | private ParallaxScollListView mListView;
14 | private ImageView mImageView;
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_parallax);
20 |
21 | mListView = (ParallaxScollListView) findViewById(R.id.layout_listview);
22 | View header = LayoutInflater.from(this).inflate(R.layout.listview_header, null);
23 | mImageView = (ImageView) header.findViewById(R.id.layout_header_image);
24 |
25 | mListView.setZoomRatio(ParallaxScollListView.ZOOM_X2);
26 | mListView.setParallaxImageView(mImageView);
27 | mListView.addHeaderView(header);
28 |
29 | ArrayAdapter adapter = new ArrayAdapter(this,
30 | android.R.layout.simple_expandable_list_item_1,
31 | new String[]{
32 | "First Item",
33 | "Second Item",
34 | "Third Item",
35 | "Fifth Item",
36 | "Sixth Item",
37 | "Seventh Item",
38 | "Eighth Item",
39 | "Ninth Item",
40 | "Tenth Item",
41 | "....."
42 | }
43 | );
44 | mListView.setAdapter(adapter);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/com/gnod/parallaxlistview/ParallaxScollListView.java:
--------------------------------------------------------------------------------
1 | package com.gnod.parallaxlistview;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.MotionEvent;
6 | import android.view.View;
7 | import android.view.animation.Animation;
8 | import android.view.animation.Transformation;
9 | import android.widget.AbsListView;
10 | import android.widget.AbsListView.OnScrollListener;
11 | import android.widget.ImageView;
12 | import android.widget.ListView;
13 | import android.widget.Toast;
14 |
15 | public class ParallaxScollListView extends ListView implements OnScrollListener {
16 |
17 | public final static double NO_ZOOM = 1;
18 | public final static double ZOOM_X2 = 2;
19 |
20 | private ImageView mImageView;
21 | private int mDrawableMaxHeight = -1;
22 | private int mImageViewHeight = -1;
23 | private int mDefaultImageViewHeight = 0;
24 | private double mZoomRatio;
25 |
26 | private interface OnOverScrollByListener {
27 | public boolean overScrollBy(int deltaX, int deltaY, int scrollX,
28 | int scrollY, int scrollRangeX, int scrollRangeY,
29 | int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent);
30 | }
31 |
32 | private interface OnTouchEventListener {
33 | public void onTouchEvent(MotionEvent ev);
34 | }
35 |
36 | public ParallaxScollListView(Context context, AttributeSet attrs,
37 | int defStyle) {
38 | super(context, attrs, defStyle);
39 | init(context);
40 | }
41 |
42 | public ParallaxScollListView(Context context, AttributeSet attrs) {
43 | super(context, attrs);
44 | init(context);
45 | }
46 |
47 | public ParallaxScollListView(Context context) {
48 | super(context);
49 | init(context);
50 | }
51 |
52 | public void init(Context context) {
53 | mDefaultImageViewHeight = context.getResources().getDimensionPixelSize(R.dimen.size_default_height);
54 | }
55 |
56 | @Override
57 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
58 | super.onLayout(changed, l, t, r, b);
59 | initViewsBounds(mZoomRatio);
60 | }
61 |
62 | @Override
63 | public void onScrollStateChanged(AbsListView view, int scrollState) {
64 | }
65 |
66 | @Override
67 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,
68 | int scrollY, int scrollRangeX, int scrollRangeY,
69 | int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
70 | boolean isCollapseAnimation = false;
71 |
72 | isCollapseAnimation = scrollByListener.overScrollBy(deltaX, deltaY,
73 | scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX,
74 | maxOverScrollY, isTouchEvent)
75 | || isCollapseAnimation;
76 |
77 | return isCollapseAnimation ? true : super.overScrollBy(deltaX, deltaY,
78 | scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX,
79 | maxOverScrollY, isTouchEvent);
80 | }
81 |
82 | @Override
83 | public void onScroll(AbsListView view, int firstVisibleItem,
84 | int visibleItemCount, int totalItemCount) {
85 | }
86 |
87 | @Override
88 | protected void onScrollChanged(int l, int t, int oldl, int oldt) {
89 | super.onScrollChanged(l, t, oldl, oldt);
90 | if (mImageView != null) {
91 | View firstView = (View) mImageView.getParent();
92 | // firstView.getTop < getPaddingTop means mImageView will be covered by top padding,
93 | // so we can layout it to make it shorter
94 | if (firstView.getTop() < getPaddingTop() && mImageView.getHeight() > mImageViewHeight) {
95 | mImageView.getLayoutParams().height = Math.max(mImageView.getHeight() - (getPaddingTop() - firstView.getTop()), mImageViewHeight);
96 | // to set the firstView.mTop to 0,
97 | // maybe use View.setTop() is more easy, but it just support from Android 3.0 (API 11)
98 | firstView.layout(firstView.getLeft(), 0, firstView.getRight(), firstView.getHeight());
99 | mImageView.requestLayout();
100 | }
101 | }
102 | }
103 |
104 | @Override
105 | public boolean onTouchEvent(MotionEvent ev) {
106 | touchListener.onTouchEvent(ev);
107 | return super.onTouchEvent(ev);
108 | }
109 |
110 | public void setParallaxImageView(ImageView iv) {
111 | mImageView = iv;
112 | mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
113 | }
114 |
115 | private void initViewsBounds(double zoomRatio) {
116 | if (mImageViewHeight == -1 && mImageView != null) {
117 | mImageViewHeight = mImageView.getHeight();
118 | if (mImageViewHeight <= 0) {
119 | mImageViewHeight = mDefaultImageViewHeight;
120 | }
121 | double ratio = ((double) mImageView.getDrawable().getIntrinsicWidth()) / ((double) mImageView.getWidth());
122 |
123 | mDrawableMaxHeight = (int) ((mImageView.getDrawable().getIntrinsicHeight() / ratio) * (zoomRatio > 1 ?
124 | zoomRatio : 1));
125 | }
126 | }
127 |
128 | public void setZoomRatio(double zoomRatio) {
129 | mZoomRatio = zoomRatio;
130 | }
131 |
132 | private OnOverScrollByListener scrollByListener = new OnOverScrollByListener() {
133 | @Override
134 | public boolean overScrollBy(int deltaX, int deltaY, int scrollX,
135 | int scrollY, int scrollRangeX, int scrollRangeY,
136 | int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
137 | if (mImageView != null && mImageView.getHeight() <= mDrawableMaxHeight && isTouchEvent) {
138 | if (deltaY < 0) {
139 | if (mImageView.getHeight() - deltaY / 2 >= mImageViewHeight) {
140 | mImageView.getLayoutParams().height = mImageView.getHeight() - deltaY / 2 < mDrawableMaxHeight ?
141 | mImageView.getHeight() - deltaY / 2 : mDrawableMaxHeight;
142 | mImageView.requestLayout();
143 | }
144 | } else {
145 | if (mImageView.getHeight() > mImageViewHeight) {
146 | mImageView.getLayoutParams().height = mImageView.getHeight() - deltaY > mImageViewHeight ?
147 | mImageView.getHeight() - deltaY : mImageViewHeight;
148 | mImageView.requestLayout();
149 | return true;
150 | }
151 | }
152 | }
153 | return false;
154 | }
155 | };
156 |
157 | private OnTouchEventListener touchListener = new OnTouchEventListener() {
158 | @Override
159 | public void onTouchEvent(MotionEvent ev) {
160 | if (ev.getAction() == MotionEvent.ACTION_UP) {
161 | if (mImageView != null && mImageViewHeight < mImageView.getHeight()) {
162 | ResetAnimimation animation = new ResetAnimimation(
163 | mImageView, mImageViewHeight);
164 | animation.setDuration(300);
165 | mImageView.startAnimation(animation);
166 | }
167 | }
168 | }
169 | };
170 |
171 | public class ResetAnimimation extends Animation {
172 | int targetHeight;
173 | int originalHeight;
174 | int extraHeight;
175 | View mView;
176 |
177 | protected ResetAnimimation(View view, int targetHeight) {
178 | this.mView = view;
179 | this.targetHeight = targetHeight;
180 | originalHeight = view.getHeight();
181 | extraHeight = this.targetHeight - originalHeight;
182 | }
183 |
184 | @Override
185 | protected void applyTransformation(float interpolatedTime,
186 | Transformation t) {
187 |
188 | int newHeight;
189 | newHeight = (int) (targetHeight - extraHeight * (1 - interpolatedTime));
190 | mView.getLayoutParams().height = newHeight;
191 | mView.requestLayout();
192 | }
193 | }
194 | }
195 |
--------------------------------------------------------------------------------