├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── lzy │ │ └── pullzoomview │ │ ├── ColorUtil.java │ │ ├── MainActivity.java │ │ ├── PullGridViewActivity.java │ │ ├── PullListViewActivity.java │ │ ├── PullRecyclerViewActivity.java │ │ ├── PullScrollViewActivity.java │ │ ├── PullViewActivity.java │ │ ├── PullViewGroupActivity.java │ │ └── PullWebViewActivity.java │ └── res │ ├── layout │ ├── activity_main.xml │ ├── activity_pull_gridview.xml │ ├── activity_pull_listview.xml │ ├── activity_pull_recyclerview.xml │ ├── activity_pull_scrollview.xml │ ├── activity_pull_view.xml │ ├── activity_pull_view_group.xml │ ├── activity_pull_webview.xml │ ├── include_header.xml │ └── include_scrollview.xml │ ├── mipmap-hdpi │ ├── header.png │ ├── ic_img_line_v.png │ ├── ic_launcher.png │ └── splash.jpg │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pullzoom ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── lzy │ │ └── widget │ │ ├── ExpandGridView.java │ │ ├── ExpandListView.java │ │ ├── PullZoomView.java │ │ └── manager │ │ ├── ExpandGridLayoutManager.java │ │ ├── ExpandLinearLayoutManager.java │ │ └── ExpandStaggeredGridLayoutManager.java │ └── res │ └── values │ └── attrs.xml ├── screenshots ├── demo1.png ├── demo2.png ├── demo3.png ├── demo4.gif ├── demo5.gif └── demo6.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | .idea 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | PullZoomView -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 26 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 | 1.8 58 | 59 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PullZoomView 2 | ###类似QQ空间,新浪微博个人主页下拉头部放大的布局效果,支持ListView,GridView,ScrollView,WebView,RecyclerView,以及其他的任意View和ViewGroup。支持头部视差动画和阻尼下拉放大。 3 | 4 | 欢迎大家下载体验本项目,如果使用过程中遇到什么问题,欢迎反馈。 5 | 6 | ### 联系方式 7 | * 邮箱地址: liaojeason@126.com 8 | * QQ群: 489873144 (建议使用QQ群,邮箱使用较少,可能看的不及时) 9 | * 本群旨在为使用我的github项目的人提供方便,如果遇到问题欢迎在群里提问。个人能力也有限,希望一起学习一起进步。 10 | 11 | 12 | ## 演示 13 | ![image](https://github.com/jeasonlzy0216/PullZoomView/blob/master/screenshots/demo1.png)![image](https://github.com/jeasonlzy0216/PullZoomView/blob/master/screenshots/demo2.png)![image](https://github.com/jeasonlzy0216/PullZoomView/blob/master/screenshots/demo3.png)![image](https://github.com/jeasonlzy0216/PullZoomView/blob/master/screenshots/demo4.gif)![image](https://github.com/jeasonlzy0216/PullZoomView/blob/master/screenshots/demo5.gif)![image](https://github.com/jeasonlzy0216/PullZoomView/blob/master/screenshots/demo6.gif) 14 | 15 | ## 1.温馨提示 16 | 该项目和我github上其他的view相关的项目已经一起打包上传到jCenter仓库中(源码地址 [https://github.com/jeasonlzy0216/ViewCore](https://github.com/jeasonlzy0216/ViewCore) ),使用的时候可以直接使用compile依赖,用法如下 17 | ```java 18 | compile 'com.lzy.widget:view-core:0.2.2' 19 | ``` 20 | 或者使用 21 | ```java 22 | compile project(':pullzoom') 23 | ``` 24 | 25 | 26 | ## 2.实现原理 27 | PullZoomView 继承至 ScrollView,通过布局设置 Tag 和 重写滑动事件,达到 PullZoomView 与 其他子View嵌套使用,同时增加了外部监听器。 28 | 29 | ## 3.自定义属性 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
自定义属性名字参数含义
pzv_sensitive图片放大效果相对于手指滑动距离的敏感度,越小越敏感,默认值 1.5
pzv_isZoomEnable是否允许下拉时头部放大效果,默认 true,即为允许
pzv_isParallax滑动时,是否头部具有视差动画,默认 true, 即为有
pzv_zoomTime松手时,缩放头部还原到原始大小的时间,单位毫秒,默认 500毫秒
56 | 57 | ## 4.使用注意事项 58 | * `PullZoomView` 使用时,需要三个Tag同时设置才能正常工作,否者会抛出异常,即在xml布局中对应的View中,加入如下tag,详细请参看Demo。 59 | ```java 60 | android:tag="header" 61 | android:tag="zoom" 62 | android:tag="content" 63 | ``` 64 | * 与ListView嵌套使用时,ListView需要使用本库中提供的 `ExpandListView` 65 | * 与GridView嵌套使用时,GridView需要使用本库中提供的 `ExpandGridView` 66 | * 与RecyclerView嵌套使用时,RecyclerView使用v7包中原生的即可,但是LayoutManager需要使用本库中提供的三个管理者,分别是 `ExpandLinearLayoutManager`, `ExpandGridLayoutManager`, `ExpandStaggeredGridLayoutManager` 67 | * 其他例如 ScrollView,WebView,View子类,ViewGroup子类均使用原生类即可,不用做任何改动。 68 | 69 | ## 5.代码参考 70 | 对外提供了滑动监听器`PullZoomView.OnScrollListener`,其中有三个方法 71 | 72 | * `onScroll(int l, int t, int oldl, int oldt)`: 表示`PullZoomView`滑动全程的监听 73 | * `onHeaderScroll(int currentY, int maxY)`: 表示头部从完全展现,到完全滑出的监听过程 74 | * `onContentScroll(int l, int t, int oldl, int oldt)`: 表示除了头部外,内容布局从最顶部滑动到最底部的监听过程 75 | 76 | 同时提供了下拉放大的监听器`PullZoomView.OnPullZoomListener`,其中有两个方法 77 | * `onPullZoom(int originHeight, int currentHeight)`:表示下拉头部时,头部的的当前大小,单位px 78 | * `onZoomFinish()`:表示头部恢复原始大小,即放大结束后的回调 79 | 80 | 此外允许通过代码对滑动行为动态控制 81 | ```java 82 | pzv.setIsParallax(true); //允许视差动画 83 | pzv.setIsZoomEnable(true); //允许头部放大 84 | pzv.setSensitive(1.5f); //敏感度1.5 85 | pzv.setZoomTime(500); //头部缩放时间500毫秒 86 | ``` 87 | 以上四个方法不是必须设置的,根据需要,可以使用自定义属性设置,也可以使用以上代码动态设置。 88 | 89 | 完整代码参考如下: 90 | ```java 91 | PullZoomView pzv = (PullZoomView) findViewById(R.id.pzv); 92 | pzv.setIsParallax(true); //允许视差动画 93 | pzv.setIsZoomEnable(true); //允许头部放大 94 | pzv.setSensitive(1.5f); //敏感度1.5 95 | pzv.setZoomTime(500); //头部缩放时间500毫秒 96 | pzv.setOnScrollListener(new PullZoomView.OnScrollListener() { 97 | @Override 98 | public void onScroll(int l, int t, int oldl, int oldt) { 99 | System.out.println("onScroll t:" + t + " oldt:" + oldt); 100 | } 101 | 102 | @Override 103 | public void onHeaderScroll(int currentY, int maxY) { 104 | System.out.println("onHeaderScroll currentY:" + currentY + " maxY:" + maxY); 105 | } 106 | 107 | @Override 108 | public void onContentScroll(int l, int t, int oldl, int oldt) { 109 | System.out.println("onContentScroll t:" + t + " oldt:" + oldt); 110 | } 111 | }); 112 | pzv.setOnPullZoomListener(new PullZoomView.OnPullZoomListener() { 113 | @Override 114 | public void onPullZoom(int originHeight, int currentHeight) { 115 | System.out.println("onPullZoom originHeight:" + originHeight + " currentHeight:" + currentHeight); 116 | } 117 | 118 | @Override 119 | public void onZoomFinish() { 120 | System.out.println("onZoomFinish"); 121 | } 122 | }); 123 | ``` 124 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.lzy.pullzoomview" 9 | minSdkVersion 8 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | compile 'com.android.support:appcompat-v7:23.1.1' 25 | // compile 'com.lzy.widget:view-core:0.2.2' 26 | compile project(':pullzoom') 27 | } 28 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\Android\SDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/lzy/pullzoomview/ColorUtil.java: -------------------------------------------------------------------------------- 1 | package com.lzy.pullzoomview; 2 | 3 | import android.graphics.Color; 4 | 5 | import java.util.Random; 6 | 7 | public class ColorUtil { 8 | /** 9 | * 生成漂亮的颜色 10 | */ 11 | public static int generateBeautifulColor() { 12 | Random random = new Random(); 13 | //为了让生成的颜色不至于太黑或者太白,所以对3个颜色的值进行限定 14 | int red = random.nextInt(150) + 50;//50-200 15 | int green = random.nextInt(150) + 50;//50-200 16 | int blue = random.nextInt(150) + 50;//50-200 17 | return Color.rgb(red, green, blue);//使用r,g,b混合生成一种新的颜色 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/lzy/pullzoomview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lzy.pullzoomview; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.AdapterView; 8 | import android.widget.ArrayAdapter; 9 | import android.widget.CheckBox; 10 | import android.widget.CompoundButton; 11 | import android.widget.ListView; 12 | import android.widget.SeekBar; 13 | import android.widget.TextView; 14 | 15 | import java.util.ArrayList; 16 | 17 | public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener, SeekBar.OnSeekBarChangeListener, CompoundButton.OnCheckedChangeListener { 18 | 19 | private float sensitive; //放大的敏感系数 20 | private int zoomTime; //头部缩放时间,单位 毫秒 21 | private boolean isParallax; //是否让头部具有视差动画 22 | private boolean isZoomEnable; //是否允许头部放大 23 | 24 | private TextView tv_sensitive; 25 | private TextView tv_zoomTime; 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_main); 31 | 32 | ArrayList strings = new ArrayList<>(); 33 | strings.add("PullScrollViewActivity"); 34 | strings.add("PullListViewActivity"); 35 | strings.add("PullGridViewActivity"); 36 | strings.add("PullWebViewActivity"); 37 | strings.add("PullRecyclerViewActivity"); 38 | strings.add("PullViewActivity"); 39 | strings.add("PullViewGroupActivity"); 40 | 41 | ListView listView = (ListView) findViewById(R.id.listView); 42 | listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, strings)); 43 | listView.setOnItemClickListener(this); 44 | 45 | tv_sensitive = (TextView) findViewById(R.id.tv_sensitive); 46 | tv_zoomTime = (TextView) findViewById(R.id.tv_zoomTime); 47 | 48 | CheckBox cb_isParallax = (CheckBox) findViewById(R.id.cb_isParallax); 49 | cb_isParallax.setOnCheckedChangeListener(this); 50 | cb_isParallax.setChecked(true); 51 | CheckBox cb_isZoomEnable = (CheckBox) findViewById(R.id.cb_isZoomEnable); 52 | cb_isZoomEnable.setOnCheckedChangeListener(this); 53 | cb_isZoomEnable.setChecked(true); 54 | 55 | SeekBar sb_sensitive = (SeekBar) findViewById(R.id.sb_sensitive); 56 | sb_sensitive.setMax(30); 57 | sb_sensitive.setOnSeekBarChangeListener(this); 58 | sb_sensitive.setProgress(15); 59 | SeekBar sb_zoomTime = (SeekBar) findViewById(R.id.sb_zoomTime); 60 | sb_zoomTime.setMax(2000); 61 | sb_zoomTime.setOnSeekBarChangeListener(this); 62 | sb_zoomTime.setProgress(500); 63 | } 64 | 65 | @Override 66 | public void onItemClick(AdapterView parent, View view, int position, long id) { 67 | Intent intent = new Intent(); 68 | intent.putExtra("sensitive", sensitive); 69 | intent.putExtra("zoomTime", zoomTime); 70 | intent.putExtra("isParallax", isParallax); 71 | intent.putExtra("isZoomEnable", isZoomEnable); 72 | switch (position) { 73 | case 0: 74 | intent.setClass(this, PullScrollViewActivity.class); 75 | break; 76 | case 1: 77 | intent.setClass(this, PullListViewActivity.class); 78 | break; 79 | case 2: 80 | intent.setClass(this, PullGridViewActivity.class); 81 | break; 82 | case 3: 83 | intent.setClass(this, PullWebViewActivity.class); 84 | break; 85 | case 4: 86 | intent.setClass(this, PullRecyclerViewActivity.class); 87 | break; 88 | case 5: 89 | intent.setClass(this, PullViewActivity.class); 90 | break; 91 | case 6: 92 | intent.setClass(this, PullViewGroupActivity.class); 93 | break; 94 | } 95 | startActivity(intent); 96 | } 97 | 98 | @Override 99 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 100 | switch (buttonView.getId()) { 101 | case R.id.cb_isParallax: 102 | isParallax = isChecked; 103 | break; 104 | case R.id.cb_isZoomEnable: 105 | isZoomEnable = isChecked; 106 | break; 107 | } 108 | } 109 | 110 | @Override 111 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 112 | switch (seekBar.getId()) { 113 | case R.id.sb_sensitive: 114 | sensitive = progress / 10.0f; 115 | tv_sensitive.setText(String.valueOf(sensitive)); 116 | break; 117 | case R.id.sb_zoomTime: 118 | zoomTime = progress; 119 | tv_zoomTime.setText(String.valueOf(zoomTime)); 120 | break; 121 | } 122 | } 123 | 124 | @Override 125 | public void onStartTrackingTouch(SeekBar seekBar) { 126 | 127 | } 128 | 129 | @Override 130 | public void onStopTrackingTouch(SeekBar seekBar) { 131 | 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/java/com/lzy/pullzoomview/PullGridViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.lzy.pullzoomview; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Color; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.Gravity; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.AbsListView; 11 | import android.widget.AdapterView; 12 | import android.widget.BaseAdapter; 13 | import android.widget.GridView; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | 17 | import com.lzy.widget.PullZoomView; 18 | 19 | import java.util.ArrayList; 20 | 21 | public class PullGridViewActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_pull_gridview); 27 | 28 | GridView gridView = (GridView) findViewById(R.id.gridView); 29 | gridView.setAdapter(new MyAdapter()); 30 | gridView.setOnItemClickListener(this); 31 | 32 | Intent intent = getIntent(); 33 | float sensitive = intent.getFloatExtra("sensitive", 1.5f); 34 | int zoomTime = intent.getIntExtra("zoomTime", 500); 35 | boolean isParallax = intent.getBooleanExtra("isParallax", true); 36 | boolean isZoomEnable = intent.getBooleanExtra("isZoomEnable", true); 37 | PullZoomView pzv = (PullZoomView) findViewById(R.id.pzv); 38 | pzv.setIsParallax(isParallax); 39 | pzv.setIsZoomEnable(isZoomEnable); 40 | pzv.setSensitive(sensitive); 41 | pzv.setZoomTime(zoomTime); 42 | pzv.setOnScrollListener(new PullZoomView.OnScrollListener() { 43 | @Override 44 | public void onScroll(int l, int t, int oldl, int oldt) { 45 | System.out.println("onScroll t:" + t + " oldt:" + oldt); 46 | } 47 | 48 | @Override 49 | public void onHeaderScroll(int currentY, int maxY) { 50 | System.out.println("onHeaderScroll currentY:" + currentY + " maxY:" + maxY); 51 | } 52 | 53 | @Override 54 | public void onContentScroll(int l, int t, int oldl, int oldt) { 55 | System.out.println("onContentScroll t:" + t + " oldt:" + oldt); 56 | } 57 | }); 58 | pzv.setOnPullZoomListener(new PullZoomView.OnPullZoomListener() { 59 | @Override 60 | public void onPullZoom(int originHeight, int currentHeight) { 61 | System.out.println("onPullZoom originHeight:" + originHeight + " currentHeight:" + currentHeight); 62 | } 63 | 64 | @Override 65 | public void onZoomFinish() { 66 | System.out.println("onZoomFinish"); 67 | } 68 | }); 69 | } 70 | 71 | @Override 72 | public void onItemClick(AdapterView parent, View view, int position, long id) { 73 | Toast.makeText(this, "点击了" + position, Toast.LENGTH_SHORT).show(); 74 | } 75 | 76 | public class MyAdapter extends BaseAdapter { 77 | 78 | private ArrayList strings; 79 | 80 | public MyAdapter() { 81 | strings = new ArrayList<>(); 82 | for (int i = 0; i < 40; i++) { 83 | strings.add("条目" + i); 84 | } 85 | } 86 | 87 | @Override 88 | public int getCount() { 89 | return strings.size(); 90 | } 91 | 92 | @Override 93 | public String getItem(int position) { 94 | return strings.get(position); 95 | } 96 | 97 | @Override 98 | public long getItemId(int position) { 99 | return position; 100 | } 101 | 102 | @Override 103 | public View getView(int position, View convertView, ViewGroup parent) { 104 | if (convertView == null) { 105 | convertView = View.inflate(getApplicationContext(), android.R.layout.simple_list_item_1, null); 106 | } 107 | TextView textView = (TextView) convertView; 108 | textView.setGravity(Gravity.CENTER); 109 | textView.setTextColor(Color.WHITE); 110 | ViewGroup.LayoutParams params = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300); 111 | textView.setLayoutParams(params); 112 | textView.setText(getItem(position)); 113 | textView.setBackgroundColor(ColorUtil.generateBeautifulColor()); 114 | return convertView; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/com/lzy/pullzoomview/PullListViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.lzy.pullzoomview; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Color; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.Gravity; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.AbsListView; 11 | import android.widget.AdapterView; 12 | import android.widget.BaseAdapter; 13 | import android.widget.ListView; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | 17 | import com.lzy.widget.PullZoomView; 18 | 19 | import java.util.ArrayList; 20 | 21 | public class PullListViewActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_pull_listview); 27 | 28 | ListView listView = (ListView) findViewById(R.id.listView); 29 | listView.setAdapter(new MyAdapter()); 30 | listView.setOnItemClickListener(this); 31 | 32 | Intent intent = getIntent(); 33 | float sensitive = intent.getFloatExtra("sensitive", 1.5f); 34 | int zoomTime = intent.getIntExtra("zoomTime", 500); 35 | boolean isParallax = intent.getBooleanExtra("isParallax", true); 36 | boolean isZoomEnable = intent.getBooleanExtra("isZoomEnable", true); 37 | PullZoomView pzv = (PullZoomView) findViewById(R.id.pzv); 38 | pzv.setIsParallax(isParallax); 39 | pzv.setIsZoomEnable(isZoomEnable); 40 | pzv.setSensitive(sensitive); 41 | pzv.setZoomTime(zoomTime); 42 | pzv.setOnScrollListener(new PullZoomView.OnScrollListener() { 43 | @Override 44 | public void onScroll(int l, int t, int oldl, int oldt) { 45 | System.out.println("onScroll t:" + t + " oldt:" + oldt); 46 | } 47 | 48 | @Override 49 | public void onHeaderScroll(int currentY, int maxY) { 50 | System.out.println("onHeaderScroll currentY:" + currentY + " maxY:" + maxY); 51 | } 52 | 53 | @Override 54 | public void onContentScroll(int l, int t, int oldl, int oldt) { 55 | System.out.println("onContentScroll t:" + t + " oldt:" + oldt); 56 | } 57 | }); 58 | pzv.setOnPullZoomListener(new PullZoomView.OnPullZoomListener() { 59 | @Override 60 | public void onPullZoom(int originHeight, int currentHeight) { 61 | System.out.println("onPullZoom originHeight:" + originHeight + " currentHeight:" + currentHeight); 62 | } 63 | 64 | @Override 65 | public void onZoomFinish() { 66 | System.out.println("onZoomFinish"); 67 | } 68 | }); 69 | } 70 | 71 | @Override 72 | public void onItemClick(AdapterView parent, View view, int position, long id) { 73 | Toast.makeText(this, "点击了" + position, Toast.LENGTH_SHORT).show(); 74 | } 75 | 76 | public class MyAdapter extends BaseAdapter { 77 | 78 | private ArrayList strings; 79 | 80 | public MyAdapter() { 81 | strings = new ArrayList<>(); 82 | for (int i = 0; i < 40; i++) { 83 | strings.add("条目" + i); 84 | } 85 | } 86 | 87 | @Override 88 | public int getCount() { 89 | return strings.size(); 90 | } 91 | 92 | @Override 93 | public String getItem(int position) { 94 | return strings.get(position); 95 | } 96 | 97 | @Override 98 | public long getItemId(int position) { 99 | return position; 100 | } 101 | 102 | @Override 103 | public View getView(int position, View convertView, ViewGroup parent) { 104 | if (convertView == null) { 105 | convertView = View.inflate(getApplicationContext(), android.R.layout.simple_list_item_1, null); 106 | } 107 | TextView textView = (TextView) convertView; 108 | textView.setGravity(Gravity.CENTER); 109 | textView.setTextColor(Color.WHITE); 110 | ViewGroup.LayoutParams params = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300); 111 | textView.setLayoutParams(params); 112 | textView.setText(getItem(position)); 113 | textView.setBackgroundColor(ColorUtil.generateBeautifulColor()); 114 | return convertView; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/com/lzy/pullzoomview/PullRecyclerViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.lzy.pullzoomview; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Color; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.RecyclerView; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | import com.lzy.widget.PullZoomView; 13 | import com.lzy.widget.manager.ExpandLinearLayoutManager; 14 | 15 | import java.util.ArrayList; 16 | 17 | public class PullRecyclerViewActivity extends AppCompatActivity { 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_pull_recyclerview); 23 | 24 | RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView); 25 | recyclerView.setLayoutManager(new ExpandLinearLayoutManager(this)); 26 | recyclerView.setAdapter(new MyAdapter()); 27 | 28 | Intent intent = getIntent(); 29 | float sensitive = intent.getFloatExtra("sensitive", 1.5f); 30 | int zoomTime = intent.getIntExtra("zoomTime", 500); 31 | boolean isParallax = intent.getBooleanExtra("isParallax", true); 32 | boolean isZoomEnable = intent.getBooleanExtra("isZoomEnable", true); 33 | PullZoomView pzv = (PullZoomView) findViewById(R.id.pzv); 34 | pzv.setIsParallax(isParallax); 35 | pzv.setIsZoomEnable(isZoomEnable); 36 | pzv.setSensitive(sensitive); 37 | pzv.setZoomTime(zoomTime); 38 | pzv.setOnScrollListener(new PullZoomView.OnScrollListener() { 39 | @Override 40 | public void onScroll(int l, int t, int oldl, int oldt) { 41 | System.out.println("onScroll t:" + t + " oldt:" + oldt); 42 | } 43 | 44 | @Override 45 | public void onHeaderScroll(int currentY, int maxY) { 46 | System.out.println("onHeaderScroll currentY:" + currentY + " maxY:" + maxY); 47 | } 48 | 49 | @Override 50 | public void onContentScroll(int l, int t, int oldl, int oldt) { 51 | System.out.println("onContentScroll t:" + t + " oldt:" + oldt); 52 | } 53 | }); 54 | pzv.setOnPullZoomListener(new PullZoomView.OnPullZoomListener() { 55 | @Override 56 | public void onPullZoom(int originHeight, int currentHeight) { 57 | System.out.println("onPullZoom originHeight:" + originHeight + " currentHeight:" + currentHeight); 58 | } 59 | 60 | @Override 61 | public void onZoomFinish() { 62 | System.out.println("onZoomFinish"); 63 | } 64 | }); 65 | } 66 | 67 | private class MyAdapter extends RecyclerView.Adapter { 68 | 69 | private ArrayList strings; 70 | 71 | public MyAdapter() { 72 | strings = new ArrayList<>(); 73 | for (int i = 0; i < 30; i++) { 74 | strings.add("条目:" + i); 75 | } 76 | } 77 | 78 | @Override 79 | public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 80 | return new SimpleViewHolder(View.inflate(getApplicationContext(), android.R.layout.simple_list_item_1, null)); 81 | } 82 | 83 | @Override 84 | public void onBindViewHolder(SimpleViewHolder holder, int position) { 85 | holder.bindData(position); 86 | } 87 | 88 | @Override 89 | public int getItemCount() { 90 | return strings.size(); 91 | } 92 | 93 | protected class SimpleViewHolder extends RecyclerView.ViewHolder { 94 | 95 | TextView textView; 96 | 97 | public SimpleViewHolder(View itemView) { 98 | super(itemView); 99 | textView = (TextView) itemView; 100 | } 101 | 102 | public void bindData(int position) { 103 | textView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300)); 104 | textView.setText(strings.get(position)); 105 | textView.setTextColor(Color.WHITE); 106 | textView.setBackgroundColor(ColorUtil.generateBeautifulColor()); 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/src/main/java/com/lzy/pullzoomview/PullScrollViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.lzy.pullzoomview; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | import com.lzy.widget.PullZoomView; 8 | 9 | public class PullScrollViewActivity extends Activity { 10 | @Override 11 | public void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_pull_scrollview); 14 | 15 | Intent intent = getIntent(); 16 | float sensitive = intent.getFloatExtra("sensitive", 1.5f); 17 | int zoomTime = intent.getIntExtra("zoomTime", 500); 18 | boolean isParallax = intent.getBooleanExtra("isParallax", true); 19 | boolean isZoomEnable = intent.getBooleanExtra("isZoomEnable", true); 20 | PullZoomView pzv = (PullZoomView) findViewById(R.id.pzv); 21 | pzv.setIsParallax(isParallax); 22 | pzv.setIsZoomEnable(isZoomEnable); 23 | pzv.setSensitive(sensitive); 24 | pzv.setZoomTime(zoomTime); 25 | pzv.setOnScrollListener(new PullZoomView.OnScrollListener() { 26 | @Override 27 | public void onScroll(int l, int t, int oldl, int oldt) { 28 | System.out.println("onScroll t:" + t + " oldt:" + oldt); 29 | } 30 | 31 | @Override 32 | public void onHeaderScroll(int currentY, int maxY) { 33 | System.out.println("onHeaderScroll currentY:" + currentY + " maxY:" + maxY); 34 | } 35 | 36 | @Override 37 | public void onContentScroll(int l, int t, int oldl, int oldt) { 38 | System.out.println("onContentScroll t:" + t + " oldt:" + oldt); 39 | } 40 | }); 41 | pzv.setOnPullZoomListener(new PullZoomView.OnPullZoomListener() { 42 | @Override 43 | public void onPullZoom(int originHeight, int currentHeight) { 44 | System.out.println("onPullZoom originHeight:" + originHeight + " currentHeight:" + currentHeight); 45 | } 46 | 47 | @Override 48 | public void onZoomFinish() { 49 | System.out.println("onZoomFinish"); 50 | } 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/lzy/pullzoomview/PullViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.lzy.pullzoomview; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | 7 | import com.lzy.widget.PullZoomView; 8 | 9 | public class PullViewActivity extends AppCompatActivity { 10 | 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_pull_view); 15 | 16 | Intent intent = getIntent(); 17 | float sensitive = intent.getFloatExtra("sensitive", 1.5f); 18 | int zoomTime = intent.getIntExtra("zoomTime", 500); 19 | boolean isParallax = intent.getBooleanExtra("isParallax", true); 20 | boolean isZoomEnable = intent.getBooleanExtra("isZoomEnable", true); 21 | PullZoomView pzv = (PullZoomView) findViewById(R.id.pzv); 22 | pzv.setIsParallax(isParallax); 23 | pzv.setIsZoomEnable(isZoomEnable); 24 | pzv.setSensitive(sensitive); 25 | pzv.setZoomTime(zoomTime); 26 | pzv.setOnScrollListener(new PullZoomView.OnScrollListener() { 27 | @Override 28 | public void onScroll(int l, int t, int oldl, int oldt) { 29 | System.out.println("onScroll t:" + t + " oldt:" + oldt); 30 | } 31 | 32 | @Override 33 | public void onHeaderScroll(int currentY, int maxY) { 34 | System.out.println("onHeaderScroll currentY:" + currentY + " maxY:" + maxY); 35 | } 36 | 37 | @Override 38 | public void onContentScroll(int l, int t, int oldl, int oldt) { 39 | System.out.println("onContentScroll t:" + t + " oldt:" + oldt); 40 | } 41 | }); 42 | pzv.setOnPullZoomListener(new PullZoomView.OnPullZoomListener() { 43 | @Override 44 | public void onPullZoom(int originHeight, int currentHeight) { 45 | System.out.println("onPullZoom originHeight:" + originHeight + " currentHeight:" + currentHeight); 46 | } 47 | 48 | @Override 49 | public void onZoomFinish() { 50 | System.out.println("onZoomFinish"); 51 | } 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/lzy/pullzoomview/PullViewGroupActivity.java: -------------------------------------------------------------------------------- 1 | package com.lzy.pullzoomview; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | 7 | import com.lzy.widget.PullZoomView; 8 | 9 | public class PullViewGroupActivity extends AppCompatActivity { 10 | 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_pull_view_group); 15 | 16 | Intent intent = getIntent(); 17 | float sensitive = intent.getFloatExtra("sensitive", 1.5f); 18 | int zoomTime = intent.getIntExtra("zoomTime", 500); 19 | boolean isParallax = intent.getBooleanExtra("isParallax", true); 20 | boolean isZoomEnable = intent.getBooleanExtra("isZoomEnable", true); 21 | PullZoomView pzv = (PullZoomView) findViewById(R.id.pzv); 22 | pzv.setIsParallax(isParallax); 23 | pzv.setIsZoomEnable(isZoomEnable); 24 | pzv.setSensitive(sensitive); 25 | pzv.setZoomTime(zoomTime); 26 | pzv.setOnScrollListener(new PullZoomView.OnScrollListener() { 27 | @Override 28 | public void onScroll(int l, int t, int oldl, int oldt) { 29 | System.out.println("onScroll t:" + t + " oldt:" + oldt); 30 | } 31 | 32 | @Override 33 | public void onHeaderScroll(int currentY, int maxY) { 34 | System.out.println("onHeaderScroll currentY:" + currentY + " maxY:" + maxY); 35 | } 36 | 37 | @Override 38 | public void onContentScroll(int l, int t, int oldl, int oldt) { 39 | System.out.println("onContentScroll t:" + t + " oldt:" + oldt); 40 | } 41 | }); 42 | pzv.setOnPullZoomListener(new PullZoomView.OnPullZoomListener() { 43 | @Override 44 | public void onPullZoom(int originHeight, int currentHeight) { 45 | System.out.println("onPullZoom originHeight:" + originHeight + " currentHeight:" + currentHeight); 46 | } 47 | 48 | @Override 49 | public void onZoomFinish() { 50 | System.out.println("onZoomFinish"); 51 | } 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/lzy/pullzoomview/PullWebViewActivity.java: -------------------------------------------------------------------------------- 1 | package com.lzy.pullzoomview; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.webkit.WebView; 7 | 8 | import com.lzy.widget.PullZoomView; 9 | 10 | public class PullWebViewActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_pull_webview); 16 | 17 | WebView webView = (WebView) findViewById(R.id.webView); 18 | webView.loadUrl("https://github.com/jeasonlzy0216"); 19 | 20 | Intent intent = getIntent(); 21 | float sensitive = intent.getFloatExtra("sensitive", 1.5f); 22 | int zoomTime = intent.getIntExtra("zoomTime", 500); 23 | boolean isParallax = intent.getBooleanExtra("isParallax", true); 24 | boolean isZoomEnable = intent.getBooleanExtra("isZoomEnable", true); 25 | PullZoomView pzv = (PullZoomView) findViewById(R.id.pzv); 26 | pzv.setIsParallax(isParallax); 27 | pzv.setIsZoomEnable(isZoomEnable); 28 | pzv.setSensitive(sensitive); 29 | pzv.setZoomTime(zoomTime); 30 | pzv.setOnScrollListener(new PullZoomView.OnScrollListener() { 31 | @Override 32 | public void onScroll(int l, int t, int oldl, int oldt) { 33 | System.out.println("onScroll t:" + t + " oldt:" + oldt); 34 | } 35 | 36 | @Override 37 | public void onHeaderScroll(int currentY, int maxY) { 38 | System.out.println("onHeaderScroll currentY:" + currentY + " maxY:" + maxY); 39 | } 40 | 41 | @Override 42 | public void onContentScroll(int l, int t, int oldl, int oldt) { 43 | System.out.println("onContentScroll t:" + t + " oldt:" + oldt); 44 | } 45 | }); 46 | pzv.setOnPullZoomListener(new PullZoomView.OnPullZoomListener() { 47 | @Override 48 | public void onPullZoom(int originHeight, int currentHeight) { 49 | System.out.println("onPullZoom originHeight:" + originHeight + " currentHeight:" + currentHeight); 50 | } 51 | 52 | @Override 53 | public void onZoomFinish() { 54 | System.out.println("onZoomFinish"); 55 | } 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 21 | 22 | 30 | 31 | 36 | 37 | 38 | 44 | 45 | 51 | 52 | 60 | 61 | 66 | 67 | 68 | 73 | 74 | 81 | 82 | 89 | 90 | 91 | 96 | 97 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_pull_gridview.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_pull_listview.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_pull_recyclerview.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_pull_scrollview.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_pull_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 15 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_pull_view_group.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 15 | 20 | 21 | 28 | 29 | 38 | 39 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_pull_webview.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/include_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 24 | 25 | 36 | 37 | 46 | 47 | 57 | 58 | 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/res/layout/include_scrollview.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 49 | 50 | 54 | 55 | 59 | 60 | 64 | 65 | 69 | 70 | 74 | 75 | 79 | 80 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeasonlzy/PullZoomView/060ffa31399713be0a7c4a77517ead11694295d2/app/src/main/res/mipmap-hdpi/header.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_img_line_v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeasonlzy/PullZoomView/060ffa31399713be0a7c4a77517ead11694295d2/app/src/main/res/mipmap-hdpi/ic_img_line_v.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeasonlzy/PullZoomView/060ffa31399713be0a7c4a77517ead11694295d2/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/splash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeasonlzy/PullZoomView/060ffa31399713be0a7c4a77517ead11694295d2/app/src/main/res/mipmap-hdpi/splash.jpg -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | PullZoomView 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.1.2' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeasonlzy/PullZoomView/060ffa31399713be0a7c4a77517ead11694295d2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jun 28 19:57:41 CST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /pullzoom/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /pullzoom/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | minSdkVersion 8 9 | targetSdkVersion 22 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile fileTree(include: ['*.jar'], dir: 'libs') 23 | compile 'com.android.support:appcompat-v7:23.1.1' 24 | compile 'com.android.support:recyclerview-v7:23.1.1' 25 | } 26 | -------------------------------------------------------------------------------- /pullzoom/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in E:\Android\SDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /pullzoom/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /pullzoom/src/main/java/com/lzy/widget/ExpandGridView.java: -------------------------------------------------------------------------------- 1 | package com.lzy.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.GridView; 6 | 7 | /** 8 | * ================================================ 9 | * 作 者:廖子尧 10 | * 版 本:1.0 11 | * 创建日期:2016/3/13 12 | * 描 述: 13 | * 修订历史: 14 | * ================================================ 15 | */ 16 | public class ExpandGridView extends GridView { 17 | public ExpandGridView(Context context) { 18 | super(context); 19 | } 20 | 21 | public ExpandGridView(Context context, AttributeSet attrs) { 22 | super(context, attrs); 23 | } 24 | 25 | public ExpandGridView(Context context, AttributeSet attrs, int defStyleAttr) { 26 | super(context, attrs, defStyleAttr); 27 | } 28 | 29 | @Override 30 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 31 | int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); 32 | super.onMeasure(widthMeasureSpec, expandSpec); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pullzoom/src/main/java/com/lzy/widget/ExpandListView.java: -------------------------------------------------------------------------------- 1 | package com.lzy.widget; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.ListView; 6 | 7 | /** 8 | * ================================================ 9 | * 作 者:廖子尧 10 | * 版 本:1.0 11 | * 创建日期:2016/3/13 12 | * 描 述: 13 | * 修订历史: 14 | * ================================================ 15 | */ 16 | public class ExpandListView extends ListView { 17 | public ExpandListView(Context context) { 18 | super(context); 19 | } 20 | 21 | public ExpandListView(Context context, AttributeSet attrs) { 22 | super(context, attrs); 23 | } 24 | 25 | public ExpandListView(Context context, AttributeSet attrs, int defStyleAttr) { 26 | super(context, attrs, defStyleAttr); 27 | } 28 | 29 | @Override 30 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 31 | int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); 32 | super.onMeasure(widthMeasureSpec, expandSpec); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pullzoom/src/main/java/com/lzy/widget/PullZoomView.java: -------------------------------------------------------------------------------- 1 | package com.lzy.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.support.v4.view.ViewCompat; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | import android.view.View; 9 | import android.view.ViewConfiguration; 10 | import android.view.ViewGroup; 11 | import android.view.ViewTreeObserver; 12 | import android.widget.ScrollView; 13 | import android.widget.Scroller; 14 | 15 | /** 16 | * ================================================ 17 | * 作 者:廖子尧 18 | * 版 本:1.0 19 | * 创建日期:2016/3/13 20 | * 描 述: 21 | * 修订历史: 22 | * ================================================ 23 | */ 24 | public class PullZoomView extends ScrollView { 25 | 26 | private static final String TAG_HEADER = "header"; //头布局Tag 27 | private static final String TAG_ZOOM = "zoom"; //缩放布局Tag 28 | private static final String TAG_CONTENT = "content"; //内容布局Tag 29 | 30 | private float sensitive = 1.5f; //放大的敏感系数 31 | private int zoomTime = 500; //头部缩放时间,单位 毫秒 32 | private boolean isParallax = true; //是否让头部具有视差动画 33 | private boolean isZoomEnable = true; //是否允许头部放大 34 | 35 | private Scroller scroller; //辅助缩放的对象 36 | private boolean isActionDown = false; //第一次接收的事件是否是Down事件 37 | private boolean isZooming = false; //是否正在被缩放 38 | private MarginLayoutParams headerParams;//头部的参数 39 | private int headerHeight; //头部的原始高度 40 | private View headerView; //头布局 41 | private View zoomView; //用于缩放的View 42 | private View contentView; //主体内容View 43 | private float lastEventX; //Move事件最后一次发生时的X坐标 44 | private float lastEventY; //Move事件最后一次发生时的Y坐标 45 | private float downX; //Down事件的X坐标 46 | private float downY; //Down事件的Y坐标 47 | private int maxY; //允许的最大滑出距离 48 | private int touchSlop; 49 | 50 | private OnScrollListener scrollListener; //滚动的监听 51 | 52 | public void setOnScrollListener(OnScrollListener scrollListener) { 53 | this.scrollListener = scrollListener; 54 | } 55 | 56 | /** 滚动的监听,范围从 0 ~ maxY */ 57 | public static abstract class OnScrollListener { 58 | public void onScroll(int l, int t, int oldl, int oldt) { 59 | } 60 | 61 | public void onHeaderScroll(int currentY, int maxY) { 62 | } 63 | 64 | public void onContentScroll(int l, int t, int oldl, int oldt) { 65 | } 66 | } 67 | 68 | private OnPullZoomListener pullZoomListener; //下拉放大的监听 69 | 70 | public void setOnPullZoomListener(OnPullZoomListener pullZoomListener) { 71 | this.pullZoomListener = pullZoomListener; 72 | } 73 | 74 | public static abstract class OnPullZoomListener { 75 | public void onPullZoom(int originHeight, int currentHeight) { 76 | } 77 | 78 | public void onZoomFinish() { 79 | } 80 | } 81 | 82 | public PullZoomView(Context context) { 83 | this(context, null); 84 | } 85 | 86 | public PullZoomView(Context context, AttributeSet attrs) { 87 | this(context, attrs, android.R.attr.scrollViewStyle); 88 | } 89 | 90 | public PullZoomView(Context context, AttributeSet attrs, int defStyleAttr) { 91 | super(context, attrs, defStyleAttr); 92 | 93 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.PullZoomView); 94 | sensitive = a.getFloat(R.styleable.PullZoomView_pzv_sensitive, sensitive); 95 | isParallax = a.getBoolean(R.styleable.PullZoomView_pzv_isParallax, isParallax); 96 | isZoomEnable = a.getBoolean(R.styleable.PullZoomView_pzv_isZoomEnable, isZoomEnable); 97 | zoomTime = a.getInt(R.styleable.PullZoomView_pzv_zoomTime, zoomTime); 98 | a.recycle(); 99 | 100 | scroller = new Scroller(getContext()); 101 | touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 102 | 103 | getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 104 | @Override 105 | public void onGlobalLayout() { 106 | getViewTreeObserver().removeGlobalOnLayoutListener(this); 107 | maxY = contentView.getTop();//只有布局完成后才能获取到正确的值 108 | } 109 | }); 110 | } 111 | 112 | @Override 113 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 114 | super.onSizeChanged(w, h, oldw, oldh); 115 | findTagViews(this); 116 | if (headerView == null || zoomView == null || contentView == null) { 117 | throw new IllegalStateException("content, header, zoom 都不允许为空,请在Xml布局中设置Tag,或者使用属性设置"); 118 | } 119 | headerParams = (MarginLayoutParams) headerView.getLayoutParams(); 120 | headerHeight = headerParams.height; 121 | smoothScrollTo(0, 0);//如果是滚动到最顶部,默认最顶部是ListView的顶部 122 | } 123 | 124 | /** 递归遍历所有的View,查询Tag */ 125 | private void findTagViews(View v) { 126 | if (v instanceof ViewGroup) { 127 | ViewGroup vg = (ViewGroup) v; 128 | for (int i = 0; i < vg.getChildCount(); i++) { 129 | View childView = vg.getChildAt(i); 130 | String tag = (String) childView.getTag(); 131 | if (tag != null) { 132 | if (TAG_CONTENT.equals(tag) && contentView == null) contentView = childView; 133 | if (TAG_HEADER.equals(tag) && headerView == null) headerView = childView; 134 | if (TAG_ZOOM.equals(tag) && zoomView == null) zoomView = childView; 135 | } 136 | if (childView instanceof ViewGroup) { 137 | findTagViews(childView); 138 | } 139 | } 140 | } else { 141 | String tag = (String) v.getTag(); 142 | if (tag != null) { 143 | if (TAG_CONTENT.equals(tag) && contentView == null) contentView = v; 144 | if (TAG_HEADER.equals(tag) && headerView == null) headerView = v; 145 | if (TAG_ZOOM.equals(tag) && zoomView == null) zoomView = v; 146 | } 147 | } 148 | } 149 | 150 | private boolean scrollFlag = false; //该标记主要是为了防止快速滑动时,onScroll回调中可能拿不到最大和最小值 151 | 152 | @Override 153 | protected void onScrollChanged(int l, int t, int oldl, int oldt) { 154 | super.onScrollChanged(l, t, oldl, oldt); 155 | if (scrollListener != null) scrollListener.onScroll(l, t, oldl, oldt); 156 | if (t >= 0 && t <= maxY) { 157 | scrollFlag = true; 158 | if (scrollListener != null) scrollListener.onHeaderScroll(t, maxY); 159 | } else if (scrollFlag) { 160 | scrollFlag = false; 161 | if (t < 0) t = 0; 162 | if (t > maxY) t = maxY; 163 | if (scrollListener != null) scrollListener.onHeaderScroll(t, maxY); 164 | } 165 | if (t >= maxY) { 166 | if (scrollListener != null) scrollListener.onContentScroll(l, t - maxY, oldl, oldt - maxY); 167 | } 168 | if (isParallax) { 169 | if (t >= 0 && t <= headerHeight) { 170 | headerView.scrollTo(0, -(int) (0.65 * t)); 171 | } else { 172 | headerView.scrollTo(0, 0); 173 | } 174 | } 175 | } 176 | 177 | /** 178 | * 主要用于解决 RecyclerView嵌套,直接拦截事件,可能会出现其他问题 179 | * 如果不需要使用 RecyclerView,可以将这里代码注释掉 180 | */ 181 | @Override 182 | public boolean onInterceptTouchEvent(MotionEvent e) { 183 | int action = e.getAction(); 184 | switch (action) { 185 | case MotionEvent.ACTION_DOWN: 186 | downX = e.getX(); 187 | downY = e.getY(); 188 | break; 189 | case MotionEvent.ACTION_MOVE: 190 | float moveY = e.getY(); 191 | if (Math.abs(moveY - downY) > touchSlop) { 192 | return true; 193 | } 194 | } 195 | return super.onInterceptTouchEvent(e); 196 | } 197 | 198 | @Override 199 | public boolean onTouchEvent(MotionEvent ev) { 200 | if (!isZoomEnable) return super.onTouchEvent(ev); 201 | 202 | float currentX = ev.getX(); 203 | float currentY = ev.getY(); 204 | switch (ev.getActionMasked()) { 205 | case MotionEvent.ACTION_DOWN: 206 | downX = lastEventX = currentX; 207 | downY = lastEventY = currentY; 208 | scroller.abortAnimation(); 209 | isActionDown = true; 210 | break; 211 | case MotionEvent.ACTION_MOVE: 212 | if (!isActionDown) { 213 | downX = lastEventX = currentX; 214 | downY = lastEventY = currentY; 215 | scroller.abortAnimation(); 216 | isActionDown = true; 217 | } 218 | float shiftX = Math.abs(currentX - downX); 219 | float shiftY = Math.abs(currentY - downY); 220 | float dx = currentX - lastEventX; 221 | float dy = currentY - lastEventY; 222 | lastEventY = currentY; 223 | if (isTop()) { 224 | if (shiftY > shiftX && shiftY > touchSlop) { 225 | int height = (int) (headerParams.height + dy / sensitive + 0.5); 226 | if (height <= headerHeight) { 227 | height = headerHeight; 228 | isZooming = false; 229 | } else { 230 | isZooming = true; 231 | } 232 | headerParams.height = height; 233 | headerView.setLayoutParams(headerParams); 234 | if (pullZoomListener != null) pullZoomListener.onPullZoom(headerHeight, headerParams.height); 235 | } 236 | } 237 | break; 238 | case MotionEvent.ACTION_UP: 239 | case MotionEvent.ACTION_CANCEL: 240 | isActionDown = false; 241 | if (isZooming) { 242 | scroller.startScroll(0, headerParams.height, 0, -(headerParams.height - headerHeight), zoomTime); 243 | isZooming = false; 244 | ViewCompat.postInvalidateOnAnimation(this); 245 | } 246 | break; 247 | } 248 | return isZooming || super.onTouchEvent(ev); 249 | } 250 | 251 | private boolean isStartScroll = false; //当前是否下拉过 252 | 253 | @Override 254 | public void computeScroll() { 255 | super.computeScroll(); 256 | if (scroller.computeScrollOffset()) { 257 | isStartScroll = true; 258 | headerParams.height = scroller.getCurrY(); 259 | headerView.setLayoutParams(headerParams); 260 | if (pullZoomListener != null) pullZoomListener.onPullZoom(headerHeight, headerParams.height); 261 | ViewCompat.postInvalidateOnAnimation(this); 262 | } else { 263 | if (pullZoomListener != null && isStartScroll) { 264 | isStartScroll = false; 265 | pullZoomListener.onZoomFinish(); 266 | } 267 | } 268 | } 269 | 270 | private boolean isTop() { 271 | return getScrollY() <= 0; 272 | } 273 | 274 | public void setSensitive(float sensitive) { 275 | this.sensitive = sensitive; 276 | } 277 | 278 | public void setIsParallax(boolean isParallax) { 279 | this.isParallax = isParallax; 280 | } 281 | 282 | public void setIsZoomEnable(boolean isZoomEnable) { 283 | this.isZoomEnable = isZoomEnable; 284 | } 285 | 286 | public void setZoomTime(int zoomTime) { 287 | this.zoomTime = zoomTime; 288 | } 289 | } -------------------------------------------------------------------------------- /pullzoom/src/main/java/com/lzy/widget/manager/ExpandGridLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.lzy.widget.manager; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.GridLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | /** 10 | * ================================================ 11 | * 作 者:廖子尧 12 | * 版 本:1.0 13 | * 创建日期:2016/3/14 14 | * 描 述: 15 | * 修订历史: 16 | * ================================================ 17 | */ 18 | public class ExpandGridLayoutManager extends GridLayoutManager { 19 | public ExpandGridLayoutManager(Context context, int spanCount) { 20 | super(context, spanCount); 21 | } 22 | 23 | public ExpandGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) { 24 | super(context, spanCount, orientation, reverseLayout); 25 | } 26 | 27 | private int[] mMeasuredDimension = new int[2]; 28 | 29 | @Override 30 | public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { 31 | final int widthMode = View.MeasureSpec.getMode(widthSpec); 32 | final int heightMode = View.MeasureSpec.getMode(heightSpec); 33 | final int widthSize = View.MeasureSpec.getSize(widthSpec); 34 | final int heightSize = View.MeasureSpec.getSize(heightSpec); 35 | 36 | int width = 0; 37 | int height = 0; 38 | int count = getItemCount(); 39 | int span = getSpanCount(); 40 | for (int i = 0; i < count; i++) { 41 | measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); 42 | 43 | if (getOrientation() == HORIZONTAL) { 44 | if (i % span == 0) { 45 | width = width + mMeasuredDimension[0]; 46 | } 47 | if (i == 0) { 48 | height = mMeasuredDimension[1]; 49 | } 50 | } else { 51 | if (i % span == 0) { 52 | height = height + mMeasuredDimension[1]; 53 | } 54 | if (i == 0) { 55 | width = mMeasuredDimension[0]; 56 | } 57 | } 58 | } 59 | 60 | switch (widthMode) { 61 | case View.MeasureSpec.EXACTLY: 62 | width = widthSize; 63 | case View.MeasureSpec.AT_MOST: 64 | case View.MeasureSpec.UNSPECIFIED: 65 | } 66 | 67 | switch (heightMode) { 68 | case View.MeasureSpec.EXACTLY: 69 | height = heightSize; 70 | case View.MeasureSpec.AT_MOST: 71 | case View.MeasureSpec.UNSPECIFIED: 72 | } 73 | 74 | setMeasuredDimension(width, height); 75 | } 76 | 77 | private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { 78 | if (position < getItemCount()) { 79 | try { 80 | View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException 81 | if (view != null) { 82 | RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); 83 | int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); 84 | int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); 85 | view.measure(childWidthSpec, childHeightSpec); 86 | measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; 87 | measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; 88 | recycler.recycleView(view); 89 | } 90 | } catch (Exception e) { 91 | e.printStackTrace(); 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /pullzoom/src/main/java/com/lzy/widget/manager/ExpandLinearLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.lzy.widget.manager; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | /** 10 | * ================================================ 11 | * 作 者:廖子尧 12 | * 版 本:1.0 13 | * 创建日期:2016/3/14 14 | * 描 述: 15 | * 修订历史: 16 | * ================================================ 17 | */ 18 | 19 | public class ExpandLinearLayoutManager extends LinearLayoutManager { 20 | 21 | private static final String TAG = ExpandLinearLayoutManager.class.getSimpleName(); 22 | 23 | private int[] mMeasuredDimension = new int[2]; 24 | 25 | public ExpandLinearLayoutManager(Context context) { 26 | super(context); 27 | } 28 | 29 | public ExpandLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 30 | super(context, orientation, reverseLayout); 31 | } 32 | 33 | @Override 34 | public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { 35 | 36 | final int widthMode = View.MeasureSpec.getMode(widthSpec); 37 | final int heightMode = View.MeasureSpec.getMode(heightSpec); 38 | final int widthSize = View.MeasureSpec.getSize(widthSpec); 39 | final int heightSize = View.MeasureSpec.getSize(heightSpec); 40 | 41 | int width = 0; 42 | int height = 0; 43 | for (int i = 0; i < getItemCount(); i++) { 44 | measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); 45 | 46 | if (getOrientation() == HORIZONTAL) { 47 | width = width + mMeasuredDimension[0]; 48 | if (i == 0) { 49 | height = mMeasuredDimension[1]; 50 | } 51 | } else { 52 | height = height + mMeasuredDimension[1]; 53 | if (i == 0) { 54 | width = mMeasuredDimension[0]; 55 | } 56 | } 57 | } 58 | switch (widthMode) { 59 | case View.MeasureSpec.EXACTLY: 60 | width = widthSize; 61 | case View.MeasureSpec.AT_MOST: 62 | case View.MeasureSpec.UNSPECIFIED: 63 | } 64 | 65 | switch (heightMode) { 66 | case View.MeasureSpec.EXACTLY: 67 | height = heightSize; 68 | case View.MeasureSpec.AT_MOST: 69 | case View.MeasureSpec.UNSPECIFIED: 70 | } 71 | 72 | setMeasuredDimension(width, height); 73 | } 74 | 75 | private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { 76 | try { 77 | View view = recycler.getViewForPosition(0);//fix 动态添加时报IndexOutOfBoundsException 78 | if (view != null) { 79 | RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); 80 | int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); 81 | int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); 82 | view.measure(childWidthSpec, childHeightSpec); 83 | measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; 84 | measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; 85 | recycler.recycleView(view); 86 | } 87 | } catch (Exception e) { 88 | e.printStackTrace(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /pullzoom/src/main/java/com/lzy/widget/manager/ExpandStaggeredGridLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.lzy.widget.manager; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.support.v7.widget.StaggeredGridLayoutManager; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | /** 9 | * ================================================ 10 | * 作 者:廖子尧 11 | * 版 本:1.0 12 | * 创建日期:2016/3/14 13 | * 描 述: 14 | * 修订历史: 15 | * ================================================ 16 | */ 17 | public class ExpandStaggeredGridLayoutManager extends StaggeredGridLayoutManager { 18 | 19 | public ExpandStaggeredGridLayoutManager(int spanCount, int orientation) { 20 | super(spanCount, orientation); 21 | } 22 | 23 | // 尺寸的数组,[0]是宽,[1]是高 24 | private int[] measuredDimension = new int[2]; 25 | 26 | // 用来比较同行/列那个item罪宽/高 27 | private int[] dimension; 28 | 29 | @Override 30 | 31 | public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { 32 | // 宽的mode+size 33 | final int widthMode = View.MeasureSpec.getMode(widthSpec); 34 | final int widthSize = View.MeasureSpec.getSize(widthSpec); 35 | // 高的mode + size 36 | final int heightMode = View.MeasureSpec.getMode(heightSpec); 37 | final int heightSize = View.MeasureSpec.getSize(heightSpec); 38 | 39 | // 自身宽高的初始值 40 | int width = 0; 41 | int height = 0; 42 | // item的数目 43 | int count = getItemCount(); 44 | // item的列数 45 | int span = getSpanCount(); 46 | // 根据行数或列数来创建数组 47 | dimension = new int[span]; 48 | 49 | for (int i = 0; i < count; i++) { 50 | measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), measuredDimension); 51 | 52 | // 如果是竖直的列表,计算item的高,否则计算宽度 53 | //Log.d("LISTENER", "position " + i + " height = " + measuredDimension[1]); 54 | if (getOrientation() == VERTICAL) { 55 | dimension[findMinIndex(dimension)] += measuredDimension[1]; 56 | } else { 57 | dimension[findMinIndex(dimension)] += measuredDimension[0]; 58 | } 59 | } 60 | if (getOrientation() == VERTICAL) { 61 | height = findMax(dimension); 62 | } else { 63 | width = findMax(dimension); 64 | } 65 | 66 | switch (widthMode) { 67 | // 当控件宽是match_parent时,宽度就是父控件的宽度 68 | case View.MeasureSpec.EXACTLY: 69 | width = widthSize; 70 | break; 71 | case View.MeasureSpec.AT_MOST: 72 | break; 73 | case View.MeasureSpec.UNSPECIFIED: 74 | break; 75 | } 76 | switch (heightMode) { 77 | // 当控件高是match_parent时,高度就是父控件的高度 78 | case View.MeasureSpec.EXACTLY: 79 | height = heightSize; 80 | break; 81 | case View.MeasureSpec.AT_MOST: 82 | break; 83 | case View.MeasureSpec.UNSPECIFIED: 84 | break; 85 | } 86 | // 设置测量尺寸 87 | setMeasuredDimension(width, height); 88 | } 89 | 90 | private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { 91 | 92 | // 挨个遍历所有item 93 | if (position < getItemCount()) { 94 | try { 95 | View view = recycler.getViewForPosition(position);//fix 动态添加时报IndexOutOfBoundsException 96 | 97 | if (view != null) { 98 | RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); 99 | int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), lp.width); 100 | int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), lp.height); 101 | // 子view进行测量,然后可以通过getMeasuredWidth()获得测量的宽,高类似 102 | view.measure(childWidthSpec, childHeightSpec); 103 | // 将item的宽高放入数组中 104 | measuredDimension[0] = view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; 105 | measuredDimension[1] = view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; 106 | recycler.recycleView(view); 107 | } 108 | } catch (Exception e) { 109 | e.printStackTrace(); 110 | } 111 | } 112 | } 113 | 114 | private int findMax(int[] array) { 115 | int max = array[0]; 116 | for (int value : array) { 117 | if (value > max) { 118 | max = value; 119 | } 120 | } 121 | return max; 122 | } 123 | 124 | /** 125 | * 得到最数组中最小元素的下标 126 | */ 127 | private int findMinIndex(int[] array) { 128 | int index = 0; 129 | int min = array[0]; 130 | for (int i = 0; i < array.length; i++) { 131 | if (array[i] < min) { 132 | min = array[i]; 133 | index = i; 134 | } 135 | } 136 | return index; 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /pullzoom/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /screenshots/demo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeasonlzy/PullZoomView/060ffa31399713be0a7c4a77517ead11694295d2/screenshots/demo1.png -------------------------------------------------------------------------------- /screenshots/demo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeasonlzy/PullZoomView/060ffa31399713be0a7c4a77517ead11694295d2/screenshots/demo2.png -------------------------------------------------------------------------------- /screenshots/demo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeasonlzy/PullZoomView/060ffa31399713be0a7c4a77517ead11694295d2/screenshots/demo3.png -------------------------------------------------------------------------------- /screenshots/demo4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeasonlzy/PullZoomView/060ffa31399713be0a7c4a77517ead11694295d2/screenshots/demo4.gif -------------------------------------------------------------------------------- /screenshots/demo5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeasonlzy/PullZoomView/060ffa31399713be0a7c4a77517ead11694295d2/screenshots/demo5.gif -------------------------------------------------------------------------------- /screenshots/demo6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeasonlzy/PullZoomView/060ffa31399713be0a7c4a77517ead11694295d2/screenshots/demo6.gif -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':pullzoom' 2 | --------------------------------------------------------------------------------