├── .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 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------