├── App
├── .gitignore
├── .idea
│ ├── .name
│ ├── compiler.xml
│ ├── encodings.xml
│ └── runConfigurations.xml
├── app
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── jingbin
│ │ │ └── app
│ │ │ └── ApplicationTest.java
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── jingbin
│ │ │ │ └── app
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── childpager
│ │ │ │ ├── GoodsDetailEvaluListView.java
│ │ │ │ └── GoodsDetailWebListView.java
│ │ │ │ └── stickview
│ │ │ │ ├── HeaderLinearLayout.java
│ │ │ │ ├── HeaderScrollView.java
│ │ │ │ ├── NestingWebViewScrollView.java
│ │ │ │ ├── NotifyingListenerScrollView.java
│ │ │ │ ├── PlaceHoderHeaderLayout.java
│ │ │ │ ├── ScrollHolder.java
│ │ │ │ ├── StickHeaderLayout.java
│ │ │ │ ├── StickHeaderViewPagerManager.java
│ │ │ │ ├── ptr
│ │ │ │ ├── PtrClassicDefaultHeader.java
│ │ │ │ ├── PtrClassicFrameLayout.java
│ │ │ │ ├── PtrDefaultHandler.java
│ │ │ │ ├── PtrFrameLayout.java
│ │ │ │ ├── PtrHandler.java
│ │ │ │ ├── PtrIndicator.java
│ │ │ │ ├── PtrUIHandler.java
│ │ │ │ ├── PtrUIHandlerHolder.java
│ │ │ │ └── PtrUIHandlerHook.java
│ │ │ │ └── view
│ │ │ │ └── NoScrollViewPager.java
│ │ └── res
│ │ │ ├── drawable-hdpi
│ │ │ ├── app_loading0.png
│ │ │ ├── app_loading1.png
│ │ │ ├── app_loading2.png
│ │ │ └── app_loading3.png
│ │ │ ├── drawable-xhdpi
│ │ │ ├── app_loading0.png
│ │ │ ├── app_loading1.png
│ │ │ ├── app_loading2.png
│ │ │ └── app_loading3.png
│ │ │ ├── drawable
│ │ │ └── kaws_anim.xml
│ │ │ ├── layout
│ │ │ ├── activity_main.xml
│ │ │ ├── cube_ptr_classic_default_header.xml
│ │ │ ├── item_goods_detail_empty.xml
│ │ │ ├── item_goods_detail_web.xml
│ │ │ ├── item_goods_evaluation_empty.xml
│ │ │ └── layout_goods_detail_listview.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ │ └── values
│ │ │ ├── attrs.xml
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── jingbin
│ │ └── app
│ │ └── ExampleUnitTest.java
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
├── README.md
└── sample.gif
/App/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/App/.idea/.name:
--------------------------------------------------------------------------------
1 | App
--------------------------------------------------------------------------------
/App/.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 |
--------------------------------------------------------------------------------
/App/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/App/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/App/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/App/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 | defaultConfig {
7 | applicationId "com.example.jingbin.app"
8 | minSdkVersion 15
9 | targetSdkVersion 28
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 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | testImplementation 'junit:junit:4.12'
24 | implementation 'com.android.support:appcompat-v7:28.0.0'
25 | }
26 |
--------------------------------------------------------------------------------
/App/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 /Users/jingbin/Documents/AndroidStudio/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/app/src/androidTest/java/com/example/jingbin/app/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/App/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app;
2 |
3 | import android.content.pm.ActivityInfo;
4 | import android.graphics.Color;
5 | import android.graphics.Point;
6 | import android.graphics.Rect;
7 | import android.os.Bundle;
8 | import android.support.v4.view.PagerAdapter;
9 | import android.support.v4.view.ViewPager;
10 | import android.support.v7.app.AppCompatActivity;
11 | import android.util.DisplayMetrics;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 | import android.view.Window;
15 | import android.widget.LinearLayout;
16 | import android.widget.TextView;
17 |
18 | import com.example.jingbin.app.childpager.GoodsDetailEvaluListView;
19 | import com.example.jingbin.app.childpager.GoodsDetailWebListView;
20 | import com.example.jingbin.app.stickview.PlaceHoderHeaderLayout;
21 | import com.example.jingbin.app.stickview.StickHeaderLayout;
22 | import com.example.jingbin.app.stickview.StickHeaderViewPagerManager;
23 | import com.example.jingbin.app.stickview.ptr.PtrClassicFrameLayout;
24 | import com.example.jingbin.app.stickview.ptr.PtrDefaultHandler;
25 | import com.example.jingbin.app.stickview.ptr.PtrFrameLayout;
26 | import com.example.jingbin.app.stickview.ptr.PtrHandler;
27 | import com.example.jingbin.app.stickview.view.NoScrollViewPager;
28 |
29 | import java.util.ArrayList;
30 | import java.util.List;
31 |
32 | public class MainActivity extends AppCompatActivity {
33 |
34 | private LinearLayout llDetailTab;
35 | private NoScrollViewPager mViewPager;
36 | private ArrayList viewList;
37 | private StickHeaderViewPagerManager manager;
38 | private GoodsDetailWebListView photoDetailWebView;
39 | private GoodsDetailWebListView productParameterWebView;
40 | private GoodsDetailEvaluListView evaluateListView;
41 | private TextView tv_refresh_data;
42 |
43 | @Override
44 | protected void onCreate(Bundle savedInstanceState) {
45 | super.onCreate(savedInstanceState);
46 | // 设置成竖屏禁止转屏
47 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
48 | // 取消标题
49 | requestWindowFeature(Window.FEATURE_NO_TITLE);
50 | DisplayMetrics d = new DisplayMetrics();
51 | getWindowManager().getDefaultDisplay().getMetrics(d);
52 | setContentView(R.layout.activity_main);
53 |
54 | initView();
55 | initTabData();
56 | }
57 |
58 | private void initView() {
59 | mViewPager = (NoScrollViewPager) findViewById(R.id.viewPager_scroll);
60 | tv_refresh_data = (TextView) findViewById(R.id.tv_refresh_data);
61 | }
62 |
63 |
64 | /**
65 | * 图文详情 产品参数 用户评价
66 | */
67 | private void initTabData() {
68 | viewList = new ArrayList();
69 | //主控件
70 | StickHeaderLayout shl_root = (StickHeaderLayout) findViewById(R.id.shl_root);
71 | //去掉顶部布局的滚动条
72 | shl_root.getHeaderScrollView().setVerticalScrollBarEnabled(false);
73 | //viewPager
74 | // 设置缓存页面,当前页面的相邻N各页面都会被缓存
75 | mViewPager.setOffscreenPageLimit(3);
76 | // 可滑动
77 | // mViewPager.setHeaderView(shl_root.getStickHeaderView());
78 | // 关键类
79 | manager = new StickHeaderViewPagerManager(shl_root, mViewPager);
80 |
81 | //图文详情
82 | photoDetailWebView = new GoodsDetailWebListView(this, "description");
83 | viewList.add(new ViewPagerBean(photoDetailWebView, photoDetailWebView.getPlaceHoderHeaderLayout()));
84 | //产品参数
85 | productParameterWebView = new GoodsDetailWebListView(this, "specs");
86 | viewList.add(new ViewPagerBean(productParameterWebView, productParameterWebView.getPlaceHoderHeaderLayout()));
87 | //用户评价
88 | evaluateListView = new GoodsDetailEvaluListView(this);
89 | viewList.add(new ViewPagerBean(evaluateListView, evaluateListView.getPlaceHoderHeaderLayout()));
90 |
91 | mViewPager.setAdapter(pagerAdapter);
92 | initTabBar();
93 | initStickHeaderInfo(shl_root);
94 | }
95 |
96 | class ViewPagerBean {
97 | // root是FrameLayout
98 | View root;
99 | PlaceHoderHeaderLayout placeHoderHeaderLayout;
100 |
101 | public ViewPagerBean(View root, PlaceHoderHeaderLayout placeHoderHeaderLayout) {
102 | this.root = root;
103 | this.placeHoderHeaderLayout = placeHoderHeaderLayout;
104 | }
105 | }
106 |
107 | PagerAdapter pagerAdapter = new PagerAdapter() {
108 |
109 | @Override
110 | public boolean isViewFromObject(View arg0, Object arg1) {
111 | return arg0 == arg1;
112 | }
113 |
114 | @Override
115 | public int getCount() {
116 | return viewList.size();
117 | }
118 |
119 | @Override
120 | public Object instantiateItem(ViewGroup container, int position) {
121 | // root: new 的 FrameLayout();
122 | container.addView(viewList.get(position).root);
123 | // placeHoderHeaderLayout里面fragment的填充布局
124 | manager.addPlaceHoderHeaderLayout(position, viewList.get(position).placeHoderHeaderLayout);
125 | return viewList.get(position).root;
126 | }
127 |
128 | @Override
129 | public void destroyItem(ViewGroup container, int position, Object object) {
130 | container.removeView((View) object);//(FrameLayout)
131 | }
132 | };
133 |
134 |
135 | private void initTabBar() {
136 | final List llList = new ArrayList<>();
137 |
138 | llDetailTab = (LinearLayout) findViewById(R.id.ll_detail_tab);
139 | for (int i = 0; i < llDetailTab.getChildCount(); i++) {
140 | LinearLayout tab = (LinearLayout) llDetailTab.getChildAt(i);
141 | final int finalI = i;
142 | tab.setOnClickListener(new View.OnClickListener() {
143 | @Override
144 | public void onClick(View v) {
145 | if (mViewPager != null && mViewPager.getAdapter().getCount() > finalI) {
146 | if (mViewPager.getCurrentItem() == finalI) {
147 | return;
148 | }
149 | mViewPager.setCurrentItem(finalI);
150 | }
151 | }
152 | });
153 | llList.add(tab);
154 | }
155 | mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
156 | @Override
157 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
158 | }
159 |
160 | @Override
161 | public void onPageSelected(int position) {
162 | for (LinearLayout linearLayout : llList) {
163 | //文字
164 | TextView textView = (TextView) linearLayout.getChildAt(0);
165 | textView.setTextColor(Color.parseColor("#333333"));
166 | //线条
167 | View view = (View) linearLayout.getChildAt(1);
168 | view.setBackgroundColor(Color.parseColor("#ffffffff"));
169 | }
170 | //文字
171 | TextView textView = (TextView) llList.get(position).getChildAt(0);
172 | textView.setTextColor(Color.parseColor("#f23030"));
173 | //线条
174 | View view = (View) llList.get(position).getChildAt(1);
175 | view.setBackgroundColor(Color.parseColor("#f23030"));
176 | }
177 |
178 | @Override
179 | public void onPageScrollStateChanged(int state) {
180 | }
181 | });
182 | }
183 |
184 | /**
185 | * 初始化刷新控件
186 | */
187 | private void initStickHeaderInfo(StickHeaderLayout shl_root) {
188 | final PtrClassicFrameLayout rotate_header_list_view_frame = (PtrClassicFrameLayout) findViewById(R.id.rotate_header_list_view_frame);
189 | rotate_header_list_view_frame.setEnabledNextPtrAtOnce(true);
190 | rotate_header_list_view_frame.setPtrHandler(new PtrHandler() {
191 | @Override
192 | public void onRefreshBegin(final PtrFrameLayout frame) {
193 | frame.postDelayed(new Runnable() {
194 | @Override
195 | public void run() {
196 | /** 刷新处理*/
197 | // loadData();
198 | tv_refresh_data.setText("刷新了数据");
199 |
200 | rotate_header_list_view_frame.refreshComplete();
201 | }
202 | }, 100);
203 | }
204 |
205 | @Override
206 | public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
207 | if (manager.isCanPullToRefresh()) {
208 | return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header);
209 | }
210 | return false;
211 | }
212 | });
213 |
214 | /** 显示top的情况*/
215 | shl_root.addOnPlaceHoderListener(new StickHeaderLayout.OnPlaceHoderListener() {
216 | @Override
217 | public void onSizeChanged(int headerHeight, int stickHeight) {
218 | // iv_detail_top.setVisibility(View.INVISIBLE);
219 | }
220 |
221 | @Override
222 | public void onScrollChanged(int stickHeight) {
223 | }
224 |
225 | @Override
226 | public void onHeaderTranslationY(float translationY) {
227 | /** 显示top的情况*/
228 | int width, height;
229 | Point p = new Point();
230 | getWindowManager().getDefaultDisplay().getSize(p);
231 | width = p.x;
232 | height = p.y;
233 | Rect rect = new Rect(0, 0, width, height);
234 | // if (llDetailTab.getLocalVisibleRect(rect)) {
235 | // iv_detail_top.setVisibility(View.VISIBLE);
236 | // } else {
237 | // iv_detail_top.setVisibility(View.INVISIBLE);
238 | // }
239 | }
240 | });
241 | }
242 |
243 | }
244 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/childpager/GoodsDetailEvaluListView.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.childpager;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.FrameLayout;
9 | import android.widget.ListView;
10 | import android.widget.TextView;
11 |
12 | import com.example.jingbin.app.R;
13 | import com.example.jingbin.app.stickview.PlaceHoderHeaderLayout;
14 |
15 | import java.util.ArrayList;
16 |
17 |
18 | /**
19 | * Created by jingbin on 16/4/27.
20 | * 商品详情评价栏
21 | */
22 | public class GoodsDetailEvaluListView extends FrameLayout {
23 | public ListView listView;
24 | private PlaceHoderHeaderLayout placeHoderHeaderLayout;
25 | private Context mContext;
26 | private View mFooterView;
27 |
28 | public GoodsDetailEvaluListView(Context context) {
29 | super(context);
30 | mContext = context;
31 | initView();
32 | // initData();
33 | loadData();
34 | }
35 |
36 | private void initData() {
37 | listView.setAdapter(new EvaluAdapter());
38 | }
39 |
40 | public PlaceHoderHeaderLayout getPlaceHoderHeaderLayout() {
41 | return placeHoderHeaderLayout;
42 | }
43 |
44 | private void initView() {
45 | LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
46 | View view = inflater.inflate(R.layout.layout_goods_detail_listview, this);
47 | placeHoderHeaderLayout = (PlaceHoderHeaderLayout) view.findViewById(R.id.placehoder_goods_detail);
48 | listView = (ListView) view.findViewById(R.id.lv_goods_detail_scroll);
49 | }
50 |
51 | private void loadData() {
52 | /**
53 | * viewpager :
54 | * setOffscreenPageLimit,预加载机制偶尔会失效,导致评价没有数据时会回到顶部并不能滑动
55 | * 如果注释 mViewPager.setOffscreenPageLimit(3);,则点击评论时会回到顶部,但可以下滑查看其数据
56 | * 加上此行代码,初始化listview的数据,预加载机制失效也会有adapter填充,不会出现下滑不动的情况。
57 | * 实际上加上以后,一切正常,也不会回到顶部了。
58 | * */
59 | listView.setAdapter(new EvaluAdapter());
60 |
61 | }
62 |
63 | class EvaluAdapter extends BaseAdapter {
64 |
65 | ArrayList listDatas ;
66 | public EvaluAdapter() {
67 | listDatas = new ArrayList<>();
68 | for (int i=0;i<100;i++) {
69 | listDatas.add("第"+i+"条评价");
70 | }
71 | }
72 |
73 | @Override
74 | public int getCount() {
75 | return listDatas.size();
76 | }
77 |
78 | @Override
79 | public Object getItem(int i) {
80 | return null;
81 | }
82 |
83 | @Override
84 | public long getItemId(int i) {
85 | return i;
86 | }
87 |
88 | @Override
89 | public View getView(int i, View view, ViewGroup viewGroup) {
90 | view = LayoutInflater.from(mContext).inflate(R.layout.item_goods_evaluation_empty, null);
91 | TextView tv_evalu = (TextView) view.findViewById(R.id.tv_evalu);
92 | tv_evalu.setText(listDatas.get(i));
93 | return view;
94 | }
95 | }
96 |
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/childpager/GoodsDetailWebListView.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.childpager;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.net.http.SslError;
7 | import android.util.Log;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.webkit.SslErrorHandler;
12 | import android.webkit.WebSettings;
13 | import android.webkit.WebView;
14 | import android.webkit.WebViewClient;
15 | import android.widget.BaseAdapter;
16 | import android.widget.FrameLayout;
17 | import android.widget.ListView;
18 |
19 | import com.example.jingbin.app.R;
20 | import com.example.jingbin.app.stickview.PlaceHoderHeaderLayout;
21 |
22 | /**
23 | * Created by jingbin on 16/4/27.
24 | * 包含商品详情页里:
25 | * 图文详情、产品参数
26 | */
27 | public class GoodsDetailWebListView extends FrameLayout {
28 |
29 | private PlaceHoderHeaderLayout placeHoderHeaderLayout;
30 | public ListView listView;
31 | private Context mContext;
32 | private WebView webview_list;
33 | // web显示部分(借用ListView)
34 | private View headerView;
35 | /**
36 | * 是哪个页面:
37 | * 图文详情:description
38 | * 产品参数:specs
39 | */
40 | private String whichOne;
41 |
42 | public GoodsDetailWebListView(Context context) {
43 | super(context);
44 | }
45 |
46 | public GoodsDetailWebListView(Context context, String whichOne) {
47 | super(context);
48 | mContext = context;
49 | this.whichOne = whichOne;
50 | initView();
51 | loadData();
52 | }
53 |
54 | public PlaceHoderHeaderLayout getPlaceHoderHeaderLayout() {
55 | return placeHoderHeaderLayout;
56 | }
57 |
58 | public void initView() {
59 | LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
60 | View view = inflater.inflate(R.layout.layout_goods_detail_listview, this);
61 | placeHoderHeaderLayout = (PlaceHoderHeaderLayout) view.findViewById(R.id.placehoder_goods_detail);
62 | listView = (ListView) view.findViewById(R.id.lv_goods_detail_scroll);
63 | }
64 |
65 | private void loadData() {
66 | /** 网页显示部分*/
67 | if (headerView == null) {
68 | headerView = LayoutInflater.from(getContext()).inflate(R.layout.item_goods_detail_web, null);
69 | webview_list = (WebView) headerView.findViewById(R.id.webview_list);
70 | initWebView();
71 |
72 | String url;
73 | if ("description".equals(whichOne)) {
74 | url = "https://github.com/youlookwhat";
75 | } else {
76 | url = "https://github.com/youlookwhat/StickViewLayout";
77 | }
78 | webview_list.loadUrl(url);
79 | Log.e("------url:", url);
80 |
81 | }
82 | listView.addHeaderView(headerView);
83 | listView.setAdapter(new EmptyAdapter());
84 | }
85 |
86 | class EmptyAdapter extends BaseAdapter {
87 |
88 | public EmptyAdapter() {
89 | }
90 |
91 | @Override
92 | public int getCount() {
93 | return 1;
94 | }
95 |
96 | @Override
97 | public Object getItem(int i) {
98 | return null;
99 | }
100 |
101 | @Override
102 | public long getItemId(int i) {
103 | return i;
104 | }
105 |
106 | @Override
107 | public View getView(int i, View view, ViewGroup viewGroup) {
108 | view = LayoutInflater.from(mContext).inflate(R.layout.item_goods_detail_empty, null);
109 | return view;
110 | }
111 | }
112 |
113 |
114 | private void initWebView() {
115 | WebSettings ws = webview_list.getSettings();
116 | ws.setLoadWithOverviewMode(true);// setUseWideViewPort方法设置webview推荐使用的窗口。setLoadWithOverviewMode方法是设置webview加载的页面的模式。
117 | // ws.setUseWideViewPort(true); /**合作商页面适应屏幕*/
118 | ws.setSavePassword(true);
119 | ws.setSaveFormData(true);// 保存表单数据
120 | // ws.setBuiltInZoomControls(false);// 隐藏缩放按钮
121 | //----------缩放---------
122 | // webview_list.setOnTouchListener(this);//监听触摸事件
123 | ws.setSupportZoom(true);
124 | // 双击缩放
125 | ws.setBuiltInZoomControls(true);
126 | ws.setDisplayZoomControls(false);
127 | // webview_list.setFocusable(true);
128 | // webview_list.requestFocus();
129 | // webview_list.setClickable(true);
130 | // webview_list.setLongClickable(true);
131 | //设置加载进来的页面自适应手机屏幕
132 | // ws.setLoadWithOverviewMode(true);
133 |
134 | //设置缓存模式
135 | ws.setAppCacheEnabled(true);
136 | ws.setCacheMode(WebSettings.LOAD_DEFAULT);//设置缓存模式
137 | // 设置此属性,可任意比例缩放。
138 | ws.setUseWideViewPort(true);
139 | //缩放比例 1
140 | webview_list.setInitialScale(1);
141 | // ws.setDefaultZoom(WebSettings.ZoomDensity.MEDIUM);
142 | //---------------------
143 | ws.setJavaScriptEnabled(true);
144 | ws.setDomStorageEnabled(true);
145 | ws.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);// 排版适应屏幕
146 | // 缩放排版:
147 | // ws.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
148 | // 适应屏幕
149 | // ws.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
150 |
151 | ws.setSupportMultipleWindows(true);// 新加
152 | // ws.setUseWideViewPort(true); /**合作商页面适应屏幕*/
153 | webview_list.setWebViewClient(new MyWebViewClient());
154 | }
155 |
156 |
157 | public class MyWebViewClient extends WebViewClient {
158 | @Override
159 | public boolean shouldOverrideUrlLoading(WebView view, String url) {
160 | if (url.startsWith("http://v.youku.com/")) {
161 | Intent intent = new Intent();
162 | intent.setAction("android.intent.action.VIEW");
163 | intent.addCategory("android.intent.category.DEFAULT");
164 | intent.addCategory("android.intent.category.BROWSABLE");
165 | Uri content_url = Uri.parse(url);
166 | intent.setData(content_url);
167 | mContext.startActivity(intent);
168 | return true;
169 | } else {
170 | view.loadUrl(url);
171 | }
172 | return false;
173 | }
174 |
175 |
176 | @Override
177 | public void onPageFinished(WebView view, String url) {
178 | // html加载完成之后,添加监听图片的点击js函数
179 | // stopProgressDialog();
180 | // mProgressBar.setVisibility(View.GONE);
181 | // addImageClickListener();
182 | // view.loadUrl("javascript:window.android.test();");
183 | super.onPageFinished(view, url);
184 | }
185 |
186 | // webView默认是不处理https请求的,页面显示空白,需要进行如下设置:
187 | // onReceivedSslError为webView处理ssl证书设置
188 | @Override
189 | public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
190 | handler.proceed();
191 | super.onReceivedSslError(view, handler, error);
192 | }
193 | }
194 |
195 |
196 | }
197 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/HeaderLinearLayout.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview;
2 |
3 | import android.content.Context;
4 | import android.widget.LinearLayout;
5 |
6 | public class HeaderLinearLayout extends LinearLayout {
7 |
8 | public HeaderLinearLayout(Context context) {
9 | super(context);
10 | }
11 |
12 | @Override
13 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
14 | super.onSizeChanged(w, h, oldw, oldh);
15 | if(mOnSizeChangedListener != null){
16 | mOnSizeChangedListener.onHeaderSizeChanged(w,h,oldw,oldh);
17 | }
18 | }
19 |
20 | private OnSizeChangedListener mOnSizeChangedListener;
21 |
22 | public interface OnSizeChangedListener {
23 | void onHeaderSizeChanged(int w, int h, int oldw, int oldh);
24 | }
25 |
26 | public void setOnSizeChangedListener(OnSizeChangedListener listener) {
27 | mOnSizeChangedListener = listener;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/HeaderScrollView.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview;
2 |
3 | import android.content.Context;
4 | import android.view.MotionEvent;
5 | import android.widget.ScrollView;
6 |
7 | /**
8 | * Created by sj on 15/11/22.
9 | */
10 | public class HeaderScrollView extends ScrollView {
11 | public HeaderScrollView(Context context) {
12 | super(context);
13 | }
14 |
15 | @Override
16 | public boolean onTouchEvent(MotionEvent ev) {
17 | return false;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/NestingWebViewScrollView.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.GestureDetector;
6 | import android.view.MotionEvent;
7 |
8 | /**
9 | * Created by sj on 15/11/22.
10 | */
11 | public class NestingWebViewScrollView extends NotifyingListenerScrollView {
12 | private GestureDetector mGestureDetector;
13 |
14 | public NestingWebViewScrollView(Context context) {
15 | this(context,null);
16 | }
17 |
18 | public NestingWebViewScrollView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | mGestureDetector = new GestureDetector(context, new YScrollDetector());
21 | setFadingEdgeLength(0);
22 | }
23 |
24 | @Override
25 | public boolean onInterceptTouchEvent(MotionEvent ev) {
26 | return super.onInterceptTouchEvent(ev) || mGestureDetector.onTouchEvent(ev);
27 | }
28 |
29 | class YScrollDetector extends GestureDetector.SimpleOnGestureListener {
30 | @Override
31 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
32 | if (Math.abs(distanceY) > Math.abs(distanceX)) {
33 | return true;
34 | }
35 | return false;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/NotifyingListenerScrollView.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.ScrollView;
6 |
7 | /**
8 | * Created by sj on 15/11/20.
9 | */
10 | public class NotifyingListenerScrollView extends ScrollView {
11 |
12 | public NotifyingListenerScrollView(Context context) {
13 | super(context);
14 | }
15 |
16 | public NotifyingListenerScrollView(Context context, AttributeSet attrs) {
17 | super(context, attrs);
18 | }
19 |
20 | public NotifyingListenerScrollView(Context context, AttributeSet attrs, int defStyle) {
21 | super(context, attrs, defStyle);
22 | }
23 |
24 | public interface OnScrollChangedListener {
25 | void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt);
26 | }
27 |
28 | private OnScrollChangedListener mOnScrollChangedListener;
29 |
30 | /**
31 | * This is called in response to an internal scroll in this view (i.e., the
32 | * view scrolled its own contents). This is typically as a result of
33 | * {@link #scrollBy(int, int)} or {@link #scrollTo(int, int)} having been
34 | * called.
35 | *
36 | * @param l Current horizontal scroll origin.
37 | * @param t Current vertical scroll origin.
38 | * @param oldl Previous horizontal scroll origin.
39 | * @param oldt Previous vertical scroll origin.
40 | */
41 | @Override
42 | protected void onScrollChanged(int l, int t, int oldl, int oldt) {
43 | super.onScrollChanged(l, t, oldl, oldt);
44 | if (mOnScrollChangedListener != null) {
45 | mOnScrollChangedListener.onScrollChanged(this, l, t, oldl, oldt);
46 | }
47 | }
48 |
49 | public void setOnScrollChangedListener(OnScrollChangedListener listener) {
50 | mOnScrollChangedListener = listener;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/PlaceHoderHeaderLayout.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.util.AttributeSet;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.webkit.WebView;
9 | import android.widget.AbsListView;
10 | import android.widget.FrameLayout;
11 | import android.widget.LinearLayout;
12 | import android.widget.ListView;
13 | import android.widget.ScrollView;
14 |
15 | import com.example.jingbin.app.R;
16 |
17 |
18 | /**
19 | * Created by jingbin on 16/08/03.
20 | */
21 | public class PlaceHoderHeaderLayout extends FrameLayout {
22 |
23 | private View placeHolderView;
24 | private View mScrollItemView;
25 |
26 | public View getmScrollItemView (){
27 | return mScrollItemView;
28 | }
29 | private int mScrollViewId;
30 | private boolean mIsRegisterScrollListener;
31 |
32 | private StickHeaderViewPagerManager mStickHeaderViewPagerManager;
33 | private int mPosition;
34 |
35 | public PlaceHoderHeaderLayout(Context context, AttributeSet attrs) {
36 | super(context, attrs);
37 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StickHeaderLayout);
38 | if (typedArray != null) {
39 | mScrollViewId = typedArray.getResourceId(R.styleable.StickHeaderLayout_scrollViewId, mScrollViewId);
40 | typedArray.recycle();
41 | }
42 | }
43 |
44 | @Override
45 | public final void addView(View child, int index, ViewGroup.LayoutParams params) {
46 | if (getChildCount() > 0) {
47 | throw new IllegalStateException("only can host 1 elements");
48 | }
49 | super.addView(child, index, params);
50 | }
51 |
52 | @Override
53 | protected void onAttachedToWindow() {
54 | super.onAttachedToWindow();
55 | if (onAttachedToWindowListener != null) {
56 | onAttachedToWindowListener.onAttachedToWindow(this);
57 | }
58 | }
59 |
60 | @Override
61 | protected void onFinishInflate() {
62 | super.onFinishInflate();
63 |
64 | mScrollItemView = mScrollViewId != 0 ? findViewById(mScrollViewId) : getChildAt(0);
65 |
66 | if (mScrollItemView == null) {
67 | return;
68 | }
69 |
70 | if (mScrollItemView instanceof ScrollView) {
71 | ScrollView scrollView = (ScrollView) mScrollItemView;
72 | View contentView = scrollView.getChildAt(0);
73 | scrollView.removeView(contentView);
74 | LinearLayout childLayout = new LinearLayout(getContext());
75 | childLayout.setOrientation(LinearLayout.VERTICAL);
76 | placeHolderView = new View(getContext());
77 | childLayout.addView(placeHolderView, ViewGroup.LayoutParams.MATCH_PARENT, 0);
78 | childLayout.addView(contentView);
79 | scrollView.addView(childLayout);
80 | } else if (mScrollItemView instanceof ListView) {
81 | ListView listView = (ListView) mScrollItemView;
82 | placeHolderView = new View(getContext());
83 | listView.addHeaderView(placeHolderView);
84 | } else if (mScrollItemView instanceof WebView) {
85 | removeView(mScrollItemView);
86 | NestingWebViewScrollView scrollView = new NestingWebViewScrollView(getContext());
87 | LinearLayout childLayout = new LinearLayout(getContext());
88 | childLayout.setOrientation(LinearLayout.VERTICAL);
89 | placeHolderView = new View(getContext());
90 | childLayout.addView(placeHolderView, ViewGroup.LayoutParams.MATCH_PARENT, 0);
91 | childLayout.addView(mScrollItemView);
92 | scrollView.addView(childLayout);
93 | mScrollItemView = scrollView;
94 | addView(scrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
95 | }
96 | }
97 |
98 | public void updatePlaceHeight(final int placeHoderHeight, final StickHeaderViewPagerManager stickHeaderViewPagerManager, final int position) {
99 |
100 | this.mStickHeaderViewPagerManager = stickHeaderViewPagerManager;
101 | this.mPosition = position;
102 |
103 |
104 | if (placeHolderView != null && placeHoderHeight != 0) {
105 | placeHolderView.post(new Runnable() {
106 | @Override
107 | public void run() {
108 | ViewGroup.LayoutParams params = placeHolderView.getLayoutParams();
109 | if (params != null) {
110 | params.height = placeHoderHeight;
111 | placeHolderView.setLayoutParams(params);
112 | }
113 | }
114 | });
115 |
116 | if (!mIsRegisterScrollListener) {
117 | if (mScrollItemView instanceof NotifyingListenerScrollView) {
118 | ((NotifyingListenerScrollView) mScrollItemView).setOnScrollChangedListener(new NotifyingListenerScrollView.OnScrollChangedListener() {
119 | @Override
120 | public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) {
121 | stickHeaderViewPagerManager.onScrollViewScroll(who, l, t, oldl, oldt, position);
122 | }
123 | });
124 | } else if (mScrollItemView instanceof ListView) {
125 | ((ListView) mScrollItemView).setOnScrollListener(new AbsListView.OnScrollListener() {
126 | @Override
127 | public void onScrollStateChanged(AbsListView view, int scrollState) { }
128 |
129 | @Override
130 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
131 | stickHeaderViewPagerManager.onListViewScroll(view, firstVisibleItem, visibleItemCount, totalItemCount, position);
132 | }
133 | });
134 | } else if (mScrollItemView instanceof NestingWebViewScrollView) {
135 | ((NestingWebViewScrollView) mScrollItemView).setOnScrollChangedListener(new NotifyingListenerScrollView.OnScrollChangedListener() {
136 | @Override
137 | public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) {
138 | stickHeaderViewPagerManager.onScrollViewScroll(who, l, t, oldl, oldt, position);
139 | }
140 | });
141 | }
142 | mIsRegisterScrollListener = true;
143 | }
144 | }
145 | }
146 |
147 |
148 | int mScrollHeight;
149 | int mHeaderHeight;
150 |
151 | public void adjustScroll(int scrollHeight, int headerHeight, boolean isPageSelected) {
152 | this.mScrollHeight = scrollHeight;
153 | this.mHeaderHeight = headerHeight;
154 | if (mScrollItemView == null) return;
155 |
156 | if (mScrollItemView instanceof ListView) {
157 | if (scrollHeight == 0 && ((ListView) mScrollItemView).getFirstVisiblePosition() >= 1) {
158 | return;
159 | }
160 | ((ListView) mScrollItemView).setSelectionFromTop(1, scrollHeight);
161 | } else if (mScrollItemView instanceof ScrollView) {
162 | ((ScrollView) mScrollItemView).scrollTo(0, headerHeight - scrollHeight);
163 | }
164 | }
165 |
166 | OnAttachedToWindowListener onAttachedToWindowListener;
167 |
168 | public void setOnAttachedToWindowListener(OnAttachedToWindowListener onAttachedToWindowListener) {
169 | this.onAttachedToWindowListener = onAttachedToWindowListener;
170 | }
171 |
172 | public interface OnAttachedToWindowListener {
173 | void onAttachedToWindow(PlaceHoderHeaderLayout placeHoderHeaderLayout);
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/ScrollHolder.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview;
2 |
3 | import android.widget.AbsListView;
4 | import android.widget.ScrollView;
5 |
6 | /**
7 | * Created by sj on 15/11/22.
8 | */
9 | public interface ScrollHolder {
10 |
11 | void onListViewScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount, int pagePosition);
12 |
13 | void onScrollViewScroll(ScrollView view, int x, int y, int oldX, int oldY, int pagePosition);
14 |
15 | //void onRecyclerViewScroll(RecyclerView view, int scrollY, int pagePosition, boolean isScrollToTop);
16 | }
17 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/StickHeaderLayout.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.util.AttributeSet;
6 | import android.view.MotionEvent;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.webkit.WebView;
10 | import android.widget.AbsListView;
11 | import android.widget.FrameLayout;
12 | import android.widget.LinearLayout;
13 | import android.widget.ListView;
14 | import android.widget.RelativeLayout;
15 | import android.widget.ScrollView;
16 |
17 | import com.example.jingbin.app.R;
18 |
19 | import java.util.ArrayList;
20 |
21 | /**
22 | * Any problem about the library. Contact me
23 | *
24 | * https://github.com/w446108264/StickHeaderLayout
25 | * shengjun8486@gmail.com
26 | *
27 | * Created by sj on 15/11/22.
28 | */
29 | public class StickHeaderLayout extends RelativeLayout implements ScrollHolder, HeaderLinearLayout.OnSizeChangedListener {
30 |
31 | private int mScrollMinY = 10;
32 |
33 | private FrameLayout rootFrameLayout;
34 | private HeaderScrollView headerScrollView;
35 | private HeaderLinearLayout mStickheader;
36 | private View placeHolderView;
37 |
38 | private View mScrollItemView;
39 | private int mScrollViewId;
40 |
41 | private int mStickHeaderHeight;
42 | private int mStickViewHeight;
43 | private int mMinHeaderTranslation;
44 | private boolean mIsHorizontalScrolling;
45 |
46 | public View getStickHeaderView() {
47 | return mStickheader;
48 | }
49 |
50 | public HeaderScrollView getHeaderScrollView(){
51 | return headerScrollView;
52 | }
53 | public FrameLayout getRootFrameLayout(){
54 | return rootFrameLayout;
55 | }
56 |
57 | public StickHeaderLayout(Context context, AttributeSet attrs) {
58 | super(context, attrs);
59 |
60 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StickHeaderLayout);
61 | if (typedArray != null) {
62 | mScrollViewId = typedArray.getResourceId(R.styleable.StickHeaderLayout_scrollViewId, mScrollViewId);
63 | typedArray.recycle();
64 | }
65 |
66 | // add root
67 | rootFrameLayout = new FrameLayout(context);
68 | addView(rootFrameLayout, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
69 |
70 | // add header
71 | headerScrollView = new HeaderScrollView(context);
72 | headerScrollView.setFillViewport(true);
73 | mStickheader = new HeaderLinearLayout(context);
74 | mStickheader.setOrientation(LinearLayout.VERTICAL);
75 | headerScrollView.addView(mStickheader, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
76 | addView(headerScrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
77 | }
78 |
79 | @Override
80 | public final void addView(View child, int index, ViewGroup.LayoutParams params) {
81 | if (getChildCount() < 2) {
82 | super.addView(child, index, params);
83 | } else {
84 | if (mStickheader.getChildCount() < 2) {
85 | mStickheader.addView(child, params);
86 | return;
87 | }
88 | if (rootFrameLayout.getChildCount() > 1) {
89 | throw new IllegalStateException("only can host 3 elements");
90 | }
91 | rootFrameLayout.addView(child, params);
92 | }
93 | }
94 |
95 | @Override
96 | protected void onFinishInflate() {
97 | super.onFinishInflate();
98 |
99 | mScrollItemView = mScrollViewId != 0 ? findViewById(mScrollViewId) : rootFrameLayout.getChildAt(0);
100 |
101 | if (mScrollItemView instanceof ScrollView) {
102 | ScrollView scrollView = (ScrollView) mScrollItemView;
103 |
104 | View contentView = scrollView.getChildAt(0);
105 | scrollView.removeView(contentView);
106 |
107 | LinearLayout childLayout = new LinearLayout(getContext());
108 | childLayout.setOrientation(LinearLayout.VERTICAL);
109 |
110 | placeHolderView = new View(getContext());
111 | childLayout.addView(placeHolderView, ViewGroup.LayoutParams.MATCH_PARENT, 0);
112 | childLayout.addView(contentView);
113 | scrollView.addView(childLayout);
114 |
115 | if (scrollView instanceof NotifyingListenerScrollView) {
116 | ((NotifyingListenerScrollView) scrollView).setOnScrollChangedListener(new NotifyingListenerScrollView.OnScrollChangedListener() {
117 | @Override
118 | public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) {
119 | onScrollViewScroll(who, l, t, oldl, oldt, 0);
120 | }
121 | });
122 | }
123 | } else if (mScrollItemView instanceof ListView) {
124 | ListView listView = (ListView) mScrollItemView;
125 |
126 | placeHolderView = new View(getContext());
127 | listView.addHeaderView(placeHolderView);
128 |
129 | listView.setOnScrollListener(new AbsListView.OnScrollListener() {
130 | @Override
131 | public void onScrollStateChanged(AbsListView view, int scrollState) {
132 | }
133 |
134 | @Override
135 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
136 | onListViewScroll(view, firstVisibleItem, visibleItemCount, totalItemCount, 0);
137 | }
138 | });
139 | } else if (mScrollItemView instanceof WebView) {
140 | rootFrameLayout.removeView(mScrollItemView);
141 | NestingWebViewScrollView scrollView = new NestingWebViewScrollView(getContext());
142 | LinearLayout childLayout = new LinearLayout(getContext());
143 | childLayout.setOrientation(LinearLayout.VERTICAL);
144 | placeHolderView = new View(getContext());
145 | childLayout.addView(placeHolderView, ViewGroup.LayoutParams.MATCH_PARENT, 0);
146 | childLayout.addView(mScrollItemView);
147 | scrollView.addView(childLayout);
148 | rootFrameLayout.addView(scrollView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
149 |
150 | scrollView.setOnScrollChangedListener(new NotifyingListenerScrollView.OnScrollChangedListener() {
151 | @Override
152 | public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) {
153 | onScrollViewScroll(who, l, t, oldl, oldt, 0);
154 | }
155 | });
156 | }
157 |
158 | mStickheader.setOnSizeChangedListener(this);
159 | }
160 |
161 | private void updatePlaceHeight() {
162 | if (mStickHeaderHeight != 0 && mStickViewHeight != 0) {
163 | mMinHeaderTranslation = -mStickHeaderHeight + mStickViewHeight;
164 |
165 |
166 | if (placeHolderView != null) {
167 | ViewGroup.LayoutParams params = placeHolderView.getLayoutParams();
168 | if (params != null) {
169 | params.height = mStickHeaderHeight;
170 | placeHolderView.setLayoutParams(params);
171 | }
172 | }
173 |
174 | if (onPlaceHoderListenerListeners != null) {
175 | for(OnPlaceHoderListener listener : onPlaceHoderListenerListeners){
176 | listener.onSizeChanged(mStickHeaderHeight, mMinHeaderTranslation);
177 | }
178 | }
179 | }
180 | }
181 |
182 | @Override
183 | public void onListViewScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount, int pagePosition) {
184 | scrollHeader(getListScrollY(view));
185 | }
186 |
187 | @Override
188 | public void onScrollViewScroll(ScrollView view, int x, int y, int oldX, int oldY, int pagePosition) {
189 | scrollHeader(view.getScrollY());
190 | }
191 |
192 |
193 | public void scrollHeader(int scrollY) {
194 | if (onPlaceHoderListenerListeners != null) {
195 | for(OnPlaceHoderListener listener : onPlaceHoderListenerListeners){
196 | listener.onScrollChanged(scrollY);
197 | }
198 | }
199 | float translationY = Math.max(-scrollY, mMinHeaderTranslation);
200 | headerTranslationY(translationY);
201 | }
202 |
203 | public void headerTranslationY(float translationY){
204 | mStickheader.setTranslationY(translationY);
205 | if (onPlaceHoderListenerListeners != null) {
206 | for(OnPlaceHoderListener listener : onPlaceHoderListenerListeners){
207 | listener.onHeaderTranslationY(translationY);
208 | }
209 | }
210 | }
211 |
212 | private int getListScrollY(AbsListView view) {
213 | View child = view.getChildAt(0);
214 | if (child == null) {
215 | return 0;
216 | }
217 |
218 | int firstVisiblePosition = view.getFirstVisiblePosition();
219 | int top = child.getTop();
220 |
221 | int headerHeight = 0;
222 | if (firstVisiblePosition >= 1) {
223 | headerHeight = mStickHeaderHeight;
224 | }
225 |
226 | return -top + firstVisiblePosition * child.getHeight() + headerHeight;
227 | }
228 |
229 | float x_down;
230 | float y_down;
231 | float x_move;
232 | float y_move;
233 | float moveDistanceX;
234 | float moveDistanceY;
235 |
236 | @Override
237 | public boolean dispatchTouchEvent(MotionEvent ev) {
238 | switch (ev.getAction()) {
239 | case MotionEvent.ACTION_DOWN:
240 | mIsHorizontalScrolling = false;
241 | x_down = ev.getRawX();
242 | y_down = ev.getRawY();
243 | break;
244 | case MotionEvent.ACTION_MOVE:
245 | x_move = ev.getRawX();
246 | y_move = ev.getRawY();
247 | moveDistanceX = (int) (x_move - x_down);
248 | moveDistanceY = (int) (y_move - y_down);
249 | if (Math.abs(moveDistanceY) > mScrollMinY && (Math.abs(moveDistanceY) * 0.1 > Math.abs(moveDistanceX))) {
250 | mIsHorizontalScrolling = false;
251 | } else {
252 | mIsHorizontalScrolling = true;
253 | }
254 | break;
255 | default:
256 | break;
257 | }
258 | rootFrameLayout.dispatchTouchEvent(ev);
259 | headerScrollView.dispatchTouchEvent(ev);
260 | return true;
261 | }
262 |
263 |
264 | public void setScrollMinY(int y) {
265 | mScrollMinY = y;
266 | }
267 |
268 | public boolean isHorizontalScrolling() {
269 | return mIsHorizontalScrolling;
270 | }
271 |
272 | ArrayList onPlaceHoderListenerListeners = new ArrayList<>();
273 |
274 | public void addOnPlaceHoderListener(OnPlaceHoderListener onPlaceHoderListener) {
275 | if(onPlaceHoderListener != null){
276 | onPlaceHoderListenerListeners.add(onPlaceHoderListener);
277 | }
278 | }
279 |
280 | public interface OnPlaceHoderListener {
281 | void onSizeChanged(int headerHeight, int stickHeight);
282 |
283 | void onScrollChanged(int height);
284 |
285 | void onHeaderTranslationY(float translationY);
286 | }
287 |
288 | @Override
289 | public void onHeaderSizeChanged(int w, int h, int oldw, int oldh) {
290 | mStickHeaderHeight = mStickheader.getMeasuredHeight();
291 | mStickViewHeight = mStickheader.getChildAt(1).getMeasuredHeight();
292 | updatePlaceHeight();
293 | }
294 | }
295 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/StickHeaderViewPagerManager.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview;
2 |
3 | import android.support.v4.util.SparseArrayCompat;
4 | import android.support.v4.view.ViewPager;
5 | import android.view.View;
6 | import android.widget.AbsListView;
7 | import android.widget.ScrollView;
8 |
9 | import java.util.HashSet;
10 |
11 | /**
12 | * Any problem about the library. Contact me
13 | *
14 | * https://github.com/w446108264/StickHeaderLayout
15 | * shengjun8486@gmail.com
16 | *
17 | * Created by sj on 15/11/22.
18 | */
19 | public class StickHeaderViewPagerManager implements ViewPager.OnPageChangeListener, StickHeaderLayout.OnPlaceHoderListener, ScrollHolder {
20 |
21 | private SparseArrayCompat placeHoderHeaderLayoutList = new SparseArrayCompat<>();
22 | private HashSet canPullToRefreshPosiTionSet = new HashSet<>();
23 | private ViewPager mViewPager;
24 | private StickHeaderLayout mStickHeaderLayout;
25 | private int placeHoderHeight;
26 | private int mStickHeaderTranslationY;
27 |
28 | public StickHeaderViewPagerManager(StickHeaderLayout stickHeaderLayout, ViewPager viewPager) {
29 | this.mViewPager = viewPager;
30 | this.mStickHeaderLayout = stickHeaderLayout;
31 | mViewPager.addOnPageChangeListener(this);
32 | mStickHeaderLayout.addOnPlaceHoderListener(this);
33 | }
34 |
35 | public void addPlaceHoderHeaderLayout(final int position, final PlaceHoderHeaderLayout layout) {
36 | addPlaceHoderHeaderLayout(position, layout, true);
37 | }
38 |
39 | public void addPlaceHoderHeaderLayout(final int position, final PlaceHoderHeaderLayout layout, boolean isCanPullToRefresh) {
40 | if (mStickHeaderLayout == null) {
41 | throw new IllegalStateException("StickHeaderLayout can not be null");
42 | }
43 | if (layout == null) {
44 | return;
45 | }
46 | if (isCanPullToRefresh) {
47 | canPullToRefreshPosiTionSet.add(position);
48 | }
49 | placeHoderHeaderLayoutList.put(position, layout);
50 | layout.updatePlaceHeight(placeHoderHeight, StickHeaderViewPagerManager.this, position);
51 | layout.setOnAttachedToWindowListener(new PlaceHoderHeaderLayout.OnAttachedToWindowListener() {
52 | @Override
53 | public void onAttachedToWindow(PlaceHoderHeaderLayout placeHoderHeaderLayout) {
54 | layout.updatePlaceHeight(placeHoderHeight, StickHeaderViewPagerManager.this, position);
55 | }
56 | });
57 | }
58 |
59 | @Override
60 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
61 | int currentItem = mViewPager.getCurrentItem();
62 | if (positionOffsetPixels > 0) {
63 | PlaceHoderHeaderLayout placeHoderHeaderLayout;
64 | if (position < currentItem) {
65 | placeHoderHeaderLayout = placeHoderHeaderLayoutList.valueAt(position);
66 | } else {
67 | placeHoderHeaderLayout = placeHoderHeaderLayoutList.valueAt(position + 1);
68 | }
69 | View mStickheader = mStickHeaderLayout.getStickHeaderView();
70 | if (placeHoderHeaderLayout != null) {
71 | placeHoderHeaderLayout.adjustScroll((int) (mStickheader.getHeight() + mStickheader.getTranslationY()), mStickheader.getHeight(), false);
72 | }
73 | }
74 | }
75 |
76 | @Override
77 | public void onPageSelected(int position) {
78 | if (placeHoderHeaderLayoutList == null) {
79 | return;
80 | }
81 | PlaceHoderHeaderLayout placeHoderHeaderLayout = placeHoderHeaderLayoutList.valueAt(position);
82 | View mStickheader = mStickHeaderLayout.getStickHeaderView();
83 | if (placeHoderHeaderLayout != null) {
84 | placeHoderHeaderLayout.adjustScroll((int) (mStickheader.getHeight() + mStickheader.getTranslationY()), mStickheader.getHeight(), true);
85 | }
86 | }
87 |
88 | @Override
89 | public void onPageScrollStateChanged(int state) {
90 | }
91 |
92 | @Override
93 | public void onSizeChanged(int placeHoderHeight, int stickHeight) {
94 | this.placeHoderHeight = placeHoderHeight;
95 | for (int i = 0; i < placeHoderHeaderLayoutList.size(); i++) {
96 | if (placeHoderHeaderLayoutList.get(i) != null) {
97 | placeHoderHeaderLayoutList.get(i).updatePlaceHeight(placeHoderHeight, this, placeHoderHeaderLayoutList.indexOfValue(placeHoderHeaderLayoutList.get(i)));
98 | }
99 | }
100 | }
101 |
102 | @Override
103 | public void onScrollChanged(int height) {
104 | mStickHeaderTranslationY = height;
105 | if(onHeaderScrollListener != null){
106 | onHeaderScrollListener.onScrollChanged(height);
107 | }
108 | }
109 |
110 | @Override
111 | public void onHeaderTranslationY(float translationY) {
112 |
113 | }
114 |
115 | @Override
116 | public void onListViewScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount, int pagePosition) {
117 | if (mViewPager.getCurrentItem() == pagePosition) {
118 | mStickHeaderLayout.onListViewScroll(view, firstVisibleItem, visibleItemCount, totalItemCount, pagePosition);
119 | }
120 | }
121 |
122 | @Override
123 | public void onScrollViewScroll(ScrollView view, int x, int y, int oldX, int oldY, int pagePosition) {
124 | if (mViewPager.getCurrentItem() == pagePosition) {
125 | mStickHeaderLayout.onScrollViewScroll(view, x, y, oldX, oldY, pagePosition);
126 | }
127 | }
128 |
129 | public boolean isCanPullToRefresh() {
130 | int currentItem = mViewPager.getCurrentItem();
131 | if (mStickHeaderTranslationY <= 0 && canPullToRefreshPosiTionSet.contains(currentItem)) {
132 | return mStickHeaderLayout == null ? true : !mStickHeaderLayout.isHorizontalScrolling();
133 | }
134 | return false;
135 | }
136 |
137 | public int getStickHeaderTranslationY() {
138 | return mStickHeaderTranslationY;
139 | }
140 |
141 | OnHeaderScrollListener onHeaderScrollListener;
142 |
143 | public void setOnPlaceHoderListener(OnHeaderScrollListener onHeaderScrollListener) {
144 | this.onHeaderScrollListener = onHeaderScrollListener;
145 | }
146 |
147 | public interface OnHeaderScrollListener {
148 | void onScrollChanged(int height);
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/ptr/PtrClassicDefaultHeader.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview.ptr;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.content.res.TypedArray;
6 | import android.graphics.drawable.AnimationDrawable;
7 | import android.text.TextUtils;
8 | import android.util.AttributeSet;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.widget.FrameLayout;
12 | import android.widget.ImageView;
13 | import android.widget.TextView;
14 |
15 | import com.example.jingbin.app.R;
16 |
17 | import java.util.Date;
18 |
19 | public class PtrClassicDefaultHeader extends FrameLayout implements PtrUIHandler {
20 |
21 | private final static String KEY_SharedPreferences = "cube_ptr_classic_last_update";
22 | private int mRotateAniTime = 150;
23 | private TextView mTitleTextView;
24 | private ImageView mRotateView;
25 | private View mProgressBar;
26 | private AnimationDrawable animationDrawable;
27 | private String mLastUpdateTimeKey;
28 |
29 |
30 | public PtrClassicDefaultHeader(Context context) {
31 | super(context);
32 | initViews(null);
33 | }
34 |
35 | public PtrClassicDefaultHeader(Context context, AttributeSet attrs) {
36 | super(context, attrs);
37 | initViews(attrs);
38 | }
39 |
40 | public PtrClassicDefaultHeader(Context context, AttributeSet attrs, int defStyle) {
41 | super(context, attrs, defStyle);
42 | initViews(attrs);
43 | }
44 |
45 | protected void initViews(AttributeSet attrs) {
46 | TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.PtrClassicHeader, 0, 0);
47 | if (arr != null) {
48 | mRotateAniTime = arr.getInt(R.styleable.PtrClassicHeader_ptr_rotate_ani_time, mRotateAniTime);
49 | }
50 | View header = LayoutInflater.from(getContext()).inflate(R.layout.cube_ptr_classic_default_header, this);
51 |
52 | mRotateView = (ImageView)header.findViewById(R.id.ptr_classic_header_rotate_view);
53 | mRotateView.setImageResource(R.drawable.kaws_anim);
54 | //小鹿的动画
55 | animationDrawable = (AnimationDrawable) mRotateView.getDrawable();
56 |
57 | mTitleTextView = (TextView) header.findViewById(R.id.ptr_classic_header_rotate_view_header_title);
58 | mProgressBar = header.findViewById(R.id.ptr_classic_header_rotate_view_progressbar);
59 |
60 | resetView();
61 | }
62 |
63 |
64 | /**
65 | * Specify the last update time by this key string
66 | *
67 | * @param key
68 | */
69 | public void setLastUpdateTimeKey(String key) {
70 | if (TextUtils.isEmpty(key)) {
71 | return;
72 | }
73 | mLastUpdateTimeKey = key;
74 | }
75 |
76 | /**
77 | * Using an object to specify the last update time.
78 | *
79 | * @param object
80 | */
81 | public void setLastUpdateTimeRelateObject(Object object) {
82 | setLastUpdateTimeKey(object.getClass().getName());
83 | }
84 |
85 |
86 | private void resetView() {
87 | hideRotateView();
88 | mProgressBar.setVisibility(INVISIBLE);
89 | }
90 |
91 | private void hideRotateView() {
92 | mRotateView.clearAnimation();
93 | if (animationDrawable.isRunning()) {
94 | animationDrawable.stop();
95 | }
96 | mRotateView.setVisibility(INVISIBLE);
97 | }
98 |
99 | @Override
100 | public void onUIReset(PtrFrameLayout frame) {
101 | resetView();
102 | }
103 |
104 | @Override
105 | public void onUIRefreshPrepare(PtrFrameLayout frame) {
106 |
107 | mProgressBar.setVisibility(INVISIBLE);
108 |
109 | mRotateView.setVisibility(VISIBLE);
110 | mTitleTextView.setVisibility(VISIBLE);
111 | if (frame.isPullToRefresh()) {
112 | mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down_to_refresh));
113 | } else {
114 | mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down));
115 | }
116 | }
117 |
118 | @Override
119 | public void onUIRefreshBegin(PtrFrameLayout frame) {
120 | // hideRotateView();
121 | mProgressBar.setVisibility(INVISIBLE);
122 | mTitleTextView.setVisibility(VISIBLE);
123 | mTitleTextView.setText(R.string.cube_ptr_refreshing);
124 | animationDrawable.start();
125 | }
126 |
127 | @Override
128 | public void onUIRefreshComplete(PtrFrameLayout frame) {
129 |
130 | // hideRotateView();
131 | mProgressBar.setVisibility(INVISIBLE);
132 |
133 | mTitleTextView.setVisibility(VISIBLE);
134 | mTitleTextView.setText(getResources().getString(R.string.cube_ptr_refresh_complete));
135 |
136 | // update last update time
137 | SharedPreferences sharedPreferences = getContext().getSharedPreferences(KEY_SharedPreferences, 0);
138 | if (!TextUtils.isEmpty(mLastUpdateTimeKey)) {
139 | long mLastUpdateTime = new Date().getTime();
140 | sharedPreferences.edit().putLong(mLastUpdateTimeKey, mLastUpdateTime).commit();
141 | }
142 | }
143 |
144 |
145 |
146 | @Override
147 | public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {
148 |
149 | final int mOffsetToRefresh = frame.getOffsetToRefresh();
150 | final int currentPos = ptrIndicator.getCurrentPosY();
151 | final int lastPos = ptrIndicator.getLastPosY();
152 |
153 | if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) {
154 | if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
155 | crossRotateLineFromBottomUnderTouch(frame);
156 | if (mRotateView != null) {
157 | if (animationDrawable.isRunning()) {
158 | animationDrawable.stop();
159 | }
160 | mRotateView.clearAnimation();
161 | }
162 | }
163 | } else if (currentPos > mOffsetToRefresh && lastPos <= mOffsetToRefresh) {
164 | if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
165 | crossRotateLineFromTopUnderTouch(frame);
166 | if (mRotateView != null) {
167 | if (animationDrawable.isRunning()) {
168 | animationDrawable.stop();
169 | }
170 | mRotateView.clearAnimation();
171 | }
172 | }
173 | }
174 | }
175 |
176 | private void crossRotateLineFromTopUnderTouch(PtrFrameLayout frame) {
177 | if (!frame.isPullToRefresh()) {
178 | mTitleTextView.setVisibility(VISIBLE);
179 | animationDrawable.start();
180 | mRotateView.setVisibility(VISIBLE);
181 | mTitleTextView.setText(R.string.cube_ptr_release_to_refresh);
182 | }
183 | }
184 |
185 | private void crossRotateLineFromBottomUnderTouch(PtrFrameLayout frame) {
186 | mTitleTextView.setVisibility(VISIBLE);
187 | if (frame.isPullToRefresh()) {
188 | mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down_to_refresh));
189 | } else {
190 | mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down));
191 | }
192 | }
193 |
194 | }
195 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/ptr/PtrClassicFrameLayout.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview.ptr;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 |
6 | /**
7 | * Created by jingbin on 16/4/28.
8 | */
9 | public class PtrClassicFrameLayout extends PtrFrameLayout{
10 | private PtrClassicDefaultHeader mPtrClassicHeader;
11 |
12 | public PtrClassicFrameLayout(Context context) {
13 | super(context);
14 | initViews();
15 | }
16 |
17 | public PtrClassicFrameLayout(Context context, AttributeSet attrs) {
18 | super(context, attrs);
19 | initViews();
20 | }
21 |
22 | public PtrClassicFrameLayout(Context context, AttributeSet attrs, int defStyle) {
23 | super(context, attrs, defStyle);
24 | initViews();
25 | }
26 |
27 | private void initViews() {
28 | mPtrClassicHeader = new PtrClassicDefaultHeader(getContext());
29 | setHeaderView(mPtrClassicHeader);
30 | addPtrUIHandler(mPtrClassicHeader);
31 | }
32 |
33 | public PtrClassicDefaultHeader getHeader() {
34 | return mPtrClassicHeader;
35 | }
36 |
37 | /**
38 | * Specify the last update time by this key string
39 | *
40 | * @param key
41 | */
42 | public void setLastUpdateTimeKey(String key) {
43 | if (mPtrClassicHeader != null) {
44 | mPtrClassicHeader.setLastUpdateTimeKey(key);
45 | }
46 | }
47 |
48 | /**
49 | * Using an object to specify the last update time.
50 | *
51 | * @param object
52 | */
53 | public void setLastUpdateTimeRelateObject(Object object) {
54 | if (mPtrClassicHeader != null) {
55 | mPtrClassicHeader.setLastUpdateTimeRelateObject(object);
56 | }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/ptr/PtrDefaultHandler.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview.ptr;
2 |
3 | import android.view.View;
4 | import android.widget.AbsListView;
5 |
6 | public abstract class PtrDefaultHandler implements PtrHandler {
7 |
8 | public static boolean canChildScrollUp(View view) {
9 | if (android.os.Build.VERSION.SDK_INT < 14) {
10 | if (view instanceof AbsListView) {
11 | final AbsListView absListView = (AbsListView) view;
12 | return absListView.getChildCount() > 0
13 | && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
14 | .getTop() < absListView.getPaddingTop());
15 | } else {
16 | return view.getScrollY() > 0;
17 | }
18 | } else {
19 | return view.canScrollVertically(-1);
20 | }
21 | }
22 |
23 | /**
24 | * Default implement for check can perform pull to refresh
25 | *
26 | * @param frame
27 | * @param content
28 | * @param header
29 | * @return
30 | */
31 | public static boolean checkContentCanBePulledDown(PtrFrameLayout frame, View content, View header) {
32 | return !canChildScrollUp(content);
33 | }
34 |
35 | @Override
36 | public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
37 | return checkContentCanBePulledDown(frame, content, header);
38 | }
39 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/ptr/PtrFrameLayout.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview.ptr;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.util.AttributeSet;
6 | import android.view.Gravity;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 | import android.view.ViewConfiguration;
10 | import android.view.ViewGroup;
11 | import android.widget.Scroller;
12 | import android.widget.TextView;
13 |
14 | import com.example.jingbin.app.R;
15 |
16 |
17 | /**
18 | * This layout view for "Pull to Refresh(Ptr)" support all of the view, you can contain everything you want.
19 | * support: pull to refresh / release to refresh / auto refresh / keep header view while refreshing / hide header view while refreshing
20 | * It defines {@link }, which allows you customize the UI easily.
21 | */
22 | public class PtrFrameLayout extends ViewGroup {
23 |
24 | // status enum
25 | public final static byte PTR_STATUS_INIT = 1;
26 | private byte mStatus = PTR_STATUS_INIT;
27 | public final static byte PTR_STATUS_PREPARE = 2;
28 | public final static byte PTR_STATUS_LOADING = 3;
29 | public final static byte PTR_STATUS_COMPLETE = 4;
30 | private static final boolean DEBUG_LAYOUT = true;
31 | public static boolean DEBUG = false;
32 | private static int ID = 1;
33 | protected final String LOG_TAG = "ptr-frame-" + ++ID;
34 | // auto refresh status
35 | private static byte FLAG_AUTO_REFRESH_AT_ONCE = 0x01;
36 | private static byte FLAG_AUTO_REFRESH_BUT_LATER = 0x01 << 1;
37 | private static byte FLAG_ENABLE_NEXT_PTR_AT_ONCE = 0x01 << 2;
38 | private static byte FLAG_PIN_CONTENT = 0x01 << 3;
39 | private static byte MASK_AUTO_REFRESH = 0x03;
40 | protected View mContent;
41 | // optional config for define header and content in xml file
42 | private int mHeaderId = 0;
43 | private int mContainerId = 0;
44 | // config
45 | private int mDurationToClose = 200;
46 | private int mDurationToCloseHeader = 1000;
47 | private boolean mKeepHeaderWhenRefresh = true;
48 | private boolean mPullToRefresh = false;
49 | private View mHeaderView;
50 | private PtrUIHandlerHolder mPtrUIHandlerHolder = PtrUIHandlerHolder.create();
51 | private PtrHandler mPtrHandler;
52 | // working parameters
53 | private ScrollChecker mScrollChecker;
54 | private int mPagingTouchSlop;
55 | private int mHeaderHeight;
56 | private boolean mDisableWhenHorizontalMove = false;
57 | private int mFlag = 0x00;
58 |
59 | // disable when detect moving horizontally
60 | private boolean mPreventForHorizontal = false;
61 |
62 | private MotionEvent mLastMoveEvent;
63 |
64 | private PtrUIHandlerHook mRefreshCompleteHook;
65 |
66 | private int mLoadingMinTime = 500;
67 | private long mLoadingStartTime = 0;
68 | private PtrIndicator mPtrIndicator;
69 | private boolean mHasSendCancelEvent = false;
70 | private Runnable mPerformRefreshCompleteDelay = new Runnable() {
71 | @Override
72 | public void run() {
73 | performRefreshComplete();
74 | }
75 | };
76 |
77 | public PtrFrameLayout(Context context) {
78 | this(context, null);
79 | }
80 |
81 | public PtrFrameLayout(Context context, AttributeSet attrs) {
82 | this(context, attrs, 0);
83 | }
84 |
85 | public PtrFrameLayout(Context context, AttributeSet attrs, int defStyle) {
86 | super(context, attrs, defStyle);
87 |
88 | mPtrIndicator = new PtrIndicator();
89 |
90 | TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.PtrFrameLayout, 0, 0);
91 | if (arr != null) {
92 |
93 | mHeaderId = arr.getResourceId(R.styleable.PtrFrameLayout_ptr_header, mHeaderId);
94 | mContainerId = arr.getResourceId(R.styleable.PtrFrameLayout_ptr_content, mContainerId);
95 |
96 | mPtrIndicator.setResistance(
97 | arr.getFloat(R.styleable.PtrFrameLayout_ptr_resistance, mPtrIndicator.getResistance()));
98 |
99 | mDurationToClose = arr.getInt(R.styleable.PtrFrameLayout_ptr_duration_to_close, mDurationToClose);
100 | mDurationToCloseHeader = arr.getInt(R.styleable.PtrFrameLayout_ptr_duration_to_close_header, mDurationToCloseHeader);
101 |
102 | float ratio = mPtrIndicator.getRatioOfHeaderToHeightRefresh();
103 | ratio = arr.getFloat(R.styleable.PtrFrameLayout_ptr_ratio_of_header_height_to_refresh, ratio);
104 | mPtrIndicator.setRatioOfHeaderHeightToRefresh(ratio);
105 |
106 | mKeepHeaderWhenRefresh = arr.getBoolean(R.styleable.PtrFrameLayout_ptr_keep_header_when_refresh, mKeepHeaderWhenRefresh);
107 |
108 | mPullToRefresh = arr.getBoolean(R.styleable.PtrFrameLayout_ptr_pull_to_fresh, mPullToRefresh);
109 | arr.recycle();
110 | }
111 |
112 | mScrollChecker = new ScrollChecker();
113 |
114 | final ViewConfiguration conf = ViewConfiguration.get(getContext());
115 | mPagingTouchSlop = conf.getScaledTouchSlop() * 2;
116 | }
117 |
118 | @Override
119 | protected void onFinishInflate() {
120 | final int childCount = getChildCount();
121 | if (childCount > 2) {
122 | throw new IllegalStateException("PtrFrameLayout only can host 2 elements");
123 | } else if (childCount == 2) {
124 | if (mHeaderId != 0 && mHeaderView == null) {
125 | mHeaderView = findViewById(mHeaderId);
126 | }
127 | if (mContainerId != 0 && mContent == null) {
128 | mContent = findViewById(mContainerId);
129 | }
130 |
131 | // not specify header or content
132 | if (mContent == null || mHeaderView == null) {
133 |
134 | View child1 = getChildAt(0);
135 | View child2 = getChildAt(1);
136 | if (child1 instanceof PtrUIHandler) {
137 | mHeaderView = child1;
138 | mContent = child2;
139 | } else if (child2 instanceof PtrUIHandler) {
140 | mHeaderView = child2;
141 | mContent = child1;
142 | } else {
143 | // both are not specified
144 | if (mContent == null && mHeaderView == null) {
145 | mHeaderView = child1;
146 | mContent = child2;
147 | }
148 | // only one is specified
149 | else {
150 | if (mHeaderView == null) {
151 | mHeaderView = mContent == child1 ? child2 : child1;
152 | } else {
153 | mContent = mHeaderView == child1 ? child2 : child1;
154 | }
155 | }
156 | }
157 | }
158 | } else if (childCount == 1) {
159 | mContent = getChildAt(0);
160 | } else {
161 | TextView errorView = new TextView(getContext());
162 | errorView.setClickable(true);
163 | errorView.setTextColor(0xffff6600);
164 | errorView.setGravity(Gravity.CENTER);
165 | errorView.setTextSize(20);
166 | errorView.setText("The content view in PtrFrameLayout is empty. Do you forget to specify its id in xml layout file?");
167 | mContent = errorView;
168 | addView(mContent);
169 | }
170 | if (mHeaderView != null) {
171 | mHeaderView.bringToFront();
172 | }
173 | super.onFinishInflate();
174 | }
175 |
176 | @Override
177 | protected void onDetachedFromWindow() {
178 | super.onDetachedFromWindow();
179 | if (mScrollChecker != null) {
180 | mScrollChecker.destroy();
181 | }
182 |
183 | if (mPerformRefreshCompleteDelay != null) {
184 | removeCallbacks(mPerformRefreshCompleteDelay);
185 | }
186 | }
187 |
188 | @Override
189 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
190 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
191 |
192 | if (DEBUG && DEBUG_LAYOUT) {
193 | // PtrCLog.d(LOG_TAG, "onMeasure frame: width: %s, height: %s, padding: %s %s %s %s",
194 | // getMeasuredHeight(), getMeasuredWidth(),
195 | // getPaddingLeft(), getPaddingRight(), getPaddingTop(), getPaddingBottom());
196 |
197 | }
198 |
199 | if (mHeaderView != null) {
200 | measureChildWithMargins(mHeaderView, widthMeasureSpec, 0, heightMeasureSpec, 0);
201 | MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
202 | mHeaderHeight = mHeaderView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
203 | mPtrIndicator.setHeaderHeight(mHeaderHeight);
204 | }
205 |
206 | if (mContent != null) {
207 | measureContentView(mContent, widthMeasureSpec, heightMeasureSpec);
208 | if (DEBUG && DEBUG_LAYOUT) {
209 | // ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams();
210 | // PtrCLog.d(LOG_TAG, "onMeasure content, width: %s, height: %s, margin: %s %s %s %s",
211 | // getMeasuredWidth(), getMeasuredHeight(),
212 | // lp.leftMargin, lp.topMargin, lp.rightMargin, lp.bottomMargin);
213 | // PtrCLog.d(LOG_TAG, "onMeasure, currentPos: %s, lastPos: %s, top: %s",
214 | // mPtrIndicator.getCurrentPosY(), mPtrIndicator.getLastPosY(), mContent.getTop());
215 | }
216 | }
217 | }
218 |
219 | private void measureContentView(View child,
220 | int parentWidthMeasureSpec,
221 | int parentHeightMeasureSpec) {
222 | final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
223 |
224 | final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
225 | getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, lp.width);
226 | final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
227 | getPaddingTop() + getPaddingBottom() + lp.topMargin, lp.height);
228 |
229 | child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
230 | }
231 |
232 | @Override
233 | protected void onLayout(boolean flag, int i, int j, int k, int l) {
234 | layoutChildren();
235 | }
236 |
237 | private void layoutChildren() {
238 | int offsetX = mPtrIndicator.getCurrentPosY();
239 | int paddingLeft = getPaddingLeft();
240 | int paddingTop = getPaddingTop();
241 |
242 | if (mHeaderView != null) {
243 | MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
244 | final int left = paddingLeft + lp.leftMargin;
245 | final int top = paddingTop + lp.topMargin + offsetX - mHeaderHeight;
246 | final int right = left + mHeaderView.getMeasuredWidth();
247 | final int bottom = top + mHeaderView.getMeasuredHeight();
248 | mHeaderView.layout(left, top, right, bottom);
249 | }
250 | if (mContent != null) {
251 | if (isPinContent()) {
252 | offsetX = 0;
253 | }
254 | MarginLayoutParams lp = (MarginLayoutParams) mContent.getLayoutParams();
255 | final int left = paddingLeft + lp.leftMargin;
256 | final int top = paddingTop + lp.topMargin + offsetX;
257 | final int right = left + mContent.getMeasuredWidth();
258 | final int bottom = top + mContent.getMeasuredHeight();
259 | mContent.layout(left, top, right, bottom);
260 | }
261 | }
262 |
263 | public boolean dispatchTouchEventSupper(MotionEvent e) {
264 | return super.dispatchTouchEvent(e);
265 | }
266 |
267 | @Override
268 | public boolean dispatchTouchEvent(MotionEvent e) {
269 | if (!isEnabled() || mContent == null || mHeaderView == null) {
270 | return dispatchTouchEventSupper(e);
271 | }
272 | int action = e.getAction();
273 | switch (action) {
274 | case MotionEvent.ACTION_UP:
275 | case MotionEvent.ACTION_CANCEL:
276 | mPtrIndicator.onRelease();
277 | if (mPtrIndicator.hasLeftStartPosition()) {
278 | onRelease(false);
279 | if (mPtrIndicator.hasMovedAfterPressedDown()) {
280 | sendCancelEvent();
281 | return true;
282 | }
283 | return dispatchTouchEventSupper(e);
284 | } else {
285 | return dispatchTouchEventSupper(e);
286 | }
287 |
288 | case MotionEvent.ACTION_DOWN:
289 | mHasSendCancelEvent = false;
290 | mPtrIndicator.onPressDown(e.getX(), e.getY());
291 |
292 | mScrollChecker.abortIfWorking();
293 |
294 | mPreventForHorizontal = false;
295 | // The cancel event will be sent once the position is moved.
296 | // So let the event pass to children.
297 | // fix #93, #102
298 | dispatchTouchEventSupper(e);
299 | return true;
300 |
301 | case MotionEvent.ACTION_MOVE:
302 | mLastMoveEvent = e;
303 | mPtrIndicator.onMove(e.getX(), e.getY());
304 | float offsetX = mPtrIndicator.getOffsetX();
305 | float offsetY = mPtrIndicator.getOffsetY();
306 |
307 | if (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) {
308 | if (mPtrIndicator.isInStartPosition()) {
309 | mPreventForHorizontal = true;
310 | }
311 | }
312 | if (mPreventForHorizontal) {
313 | return dispatchTouchEventSupper(e);
314 | }
315 |
316 | boolean moveDown = offsetY > 0;
317 | boolean moveUp = !moveDown;
318 | boolean canMoveUp = mPtrIndicator.hasLeftStartPosition();
319 |
320 | if (DEBUG) {
321 | boolean canMoveDown = mPtrHandler != null && mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView);
322 | // PtrCLog.v(LOG_TAG, "ACTION_MOVE: offsetY:%s, currentPos: %s, moveUp: %s, canMoveUp: %s, moveDown: %s: canMoveDown: %s", offsetY, mPtrIndicator.getCurrentPosY(), moveUp, canMoveUp, moveDown, canMoveDown);
323 | }
324 |
325 | // disable move when header not reach top
326 | if (moveDown && mPtrHandler != null && !mPtrHandler.checkCanDoRefresh(this, mContent, mHeaderView)) {
327 | return dispatchTouchEventSupper(e);
328 | }
329 |
330 | if ((moveUp && canMoveUp) || moveDown) {
331 | movePos(offsetY/2);//下拉header布局的高度
332 | return true;
333 | }
334 | }
335 | return dispatchTouchEventSupper(e);
336 | }
337 |
338 | /**
339 | * if deltaY > 0, move the content down
340 | *
341 | * @param deltaY
342 | */
343 | private void movePos(float deltaY) {
344 | // has reached the top
345 | if ((deltaY < 0 && mPtrIndicator.isInStartPosition())) {
346 | if (DEBUG) {
347 | // PtrCLog.e(LOG_TAG, String.format("has reached the top"));
348 | }
349 | return;
350 | }
351 |
352 | int to = mPtrIndicator.getCurrentPosY() + (int) deltaY;
353 |
354 | // over top
355 | if (mPtrIndicator.willOverTop(to)) {
356 | to = PtrIndicator.POS_START;
357 | }
358 |
359 | mPtrIndicator.setCurrentPos(to);
360 | int change = to - mPtrIndicator.getLastPosY();
361 | updatePos(change);
362 | }
363 |
364 | private void updatePos(int change) {
365 | if (change == 0) {
366 | return;
367 | }
368 |
369 | boolean isUnderTouch = mPtrIndicator.isUnderTouch();
370 |
371 | // once moved, cancel event will be sent to child
372 | if (isUnderTouch && !mHasSendCancelEvent && mPtrIndicator.hasMovedAfterPressedDown()) {
373 | mHasSendCancelEvent = true;
374 | sendCancelEvent();
375 | }
376 |
377 | // leave initiated position or just refresh complete
378 | if ((mPtrIndicator.hasJustLeftStartPosition() && mStatus == PTR_STATUS_INIT) ||
379 | (mPtrIndicator.goDownCrossFinishPosition() && mStatus == PTR_STATUS_COMPLETE && isEnabledNextPtrAtOnce())) {
380 |
381 | mStatus = PTR_STATUS_PREPARE;
382 | mPtrUIHandlerHolder.onUIRefreshPrepare(this);
383 | }
384 |
385 | // back to initiated position
386 | if (mPtrIndicator.hasJustBackToStartPosition()) {
387 | tryToNotifyReset();
388 |
389 | // recover event to children
390 | if (isUnderTouch) {
391 | sendDownEvent();
392 | }
393 | }
394 |
395 | // Pull to Refresh
396 | if (mStatus == PTR_STATUS_PREPARE) {
397 | // reach fresh height while moving from top to bottom
398 | if (isUnderTouch && !isAutoRefresh() && mPullToRefresh
399 | && mPtrIndicator.crossRefreshLineFromTopToBottom()) {
400 | tryToPerformRefresh();
401 | }
402 | // reach header height while auto refresh
403 | if (performAutoRefreshButLater() && mPtrIndicator.hasJustReachedHeaderHeightFromTopToBottom()) {
404 | tryToPerformRefresh();
405 | }
406 | }
407 |
408 | mHeaderView.offsetTopAndBottom(change);
409 | if (!isPinContent()) {
410 | mContent.offsetTopAndBottom(change);
411 | }
412 | invalidate();
413 |
414 | if (mPtrUIHandlerHolder.hasHandler()) {
415 | mPtrUIHandlerHolder.onUIPositionChange(this, isUnderTouch, mStatus, mPtrIndicator);
416 | }
417 | onPositionChange(isUnderTouch, mStatus, mPtrIndicator);
418 | }
419 |
420 | protected void onPositionChange(boolean isInTouching, byte status, PtrIndicator mPtrIndicator) {
421 | }
422 |
423 | @SuppressWarnings("unused")
424 | public int getHeaderHeight() {
425 | return mHeaderHeight;
426 | }
427 |
428 | private void onRelease(boolean stayForLoading) {
429 |
430 | tryToPerformRefresh();
431 |
432 | if (mStatus == PTR_STATUS_LOADING) {
433 | // keep header for fresh
434 | if (mKeepHeaderWhenRefresh) {
435 | // scroll header back
436 | if (mPtrIndicator.isOverOffsetToKeepHeaderWhileLoading() && !stayForLoading) {
437 | mScrollChecker.tryToScrollTo(mPtrIndicator.getOffsetToKeepHeaderWhileLoading(), mDurationToClose);
438 | } else {
439 | // do nothing
440 | }
441 | } else {
442 | tryScrollBackToTopWhileLoading();
443 | }
444 | } else {
445 | if (mStatus == PTR_STATUS_COMPLETE) {
446 | notifyUIRefreshComplete(false);
447 | } else {
448 | tryScrollBackToTopAbortRefresh();
449 | }
450 | }
451 | }
452 |
453 | /**
454 | * please DO REMEMBER resume the hook
455 | *
456 | * @param hook
457 | */
458 |
459 | public void setRefreshCompleteHook(PtrUIHandlerHook hook) {
460 | mRefreshCompleteHook = hook;
461 | hook.setResumeAction(new Runnable() {
462 | @Override
463 | public void run() {
464 | if (DEBUG) {
465 | // PtrCLog.d(LOG_TAG, "mRefreshCompleteHook resume.");
466 | }
467 | notifyUIRefreshComplete(true);
468 | }
469 | });
470 | }
471 |
472 | /**
473 | * Scroll back to to if is not under touch
474 | */
475 | private void tryScrollBackToTop() {
476 | if (!mPtrIndicator.isUnderTouch()) {
477 | mScrollChecker.tryToScrollTo(PtrIndicator.POS_START, mDurationToCloseHeader);
478 | }
479 | }
480 |
481 | /**
482 | * just make easier to understand
483 | */
484 | private void tryScrollBackToTopWhileLoading() {
485 | tryScrollBackToTop();
486 | }
487 |
488 | /**
489 | * just make easier to understand
490 | */
491 | private void tryScrollBackToTopAfterComplete() {
492 | tryScrollBackToTop();
493 | }
494 |
495 | /**
496 | * just make easier to understand
497 | */
498 | private void tryScrollBackToTopAbortRefresh() {
499 | tryScrollBackToTop();
500 | }
501 |
502 | private boolean tryToPerformRefresh() {
503 | if (mStatus != PTR_STATUS_PREPARE) {
504 | return false;
505 | }
506 |
507 | //
508 | if ((mPtrIndicator.isOverOffsetToKeepHeaderWhileLoading() && isAutoRefresh()) || mPtrIndicator.isOverOffsetToRefresh()) {
509 | mStatus = PTR_STATUS_LOADING;
510 | performRefresh();
511 | }
512 | return false;
513 | }
514 |
515 | private void performRefresh() {
516 | mLoadingStartTime = System.currentTimeMillis();
517 | if (mPtrUIHandlerHolder.hasHandler()) {
518 | mPtrUIHandlerHolder.onUIRefreshBegin(this);
519 | if (DEBUG) {
520 | // PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshBegin");
521 | }
522 | }
523 | if (mPtrHandler != null) {
524 | mPtrHandler.onRefreshBegin(this);
525 | }
526 | }
527 |
528 | /**
529 | * If at the top and not in loading, reset
530 | */
531 | private boolean tryToNotifyReset() {
532 | if ((mStatus == PTR_STATUS_COMPLETE || mStatus == PTR_STATUS_PREPARE) && mPtrIndicator.isInStartPosition()) {
533 | if (mPtrUIHandlerHolder.hasHandler()) {
534 | mPtrUIHandlerHolder.onUIReset(this);
535 | if (DEBUG) {
536 | // PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIReset");
537 | }
538 | }
539 | mStatus = PTR_STATUS_INIT;
540 | clearFlag();
541 | return true;
542 | }
543 | return false;
544 | }
545 |
546 | protected void onPtrScrollAbort() {
547 | if (mPtrIndicator.hasLeftStartPosition() && isAutoRefresh()) {
548 | onRelease(true);
549 | }
550 | }
551 |
552 | protected void onPtrScrollFinish() {
553 | if (mPtrIndicator.hasLeftStartPosition() && isAutoRefresh()) {
554 | onRelease(true);
555 | }
556 | }
557 |
558 | /**
559 | * Detect whether is refreshing.
560 | *
561 | * @return
562 | */
563 | public boolean isRefreshing() {
564 | return mStatus == PTR_STATUS_LOADING;
565 | }
566 |
567 | /**
568 | * Call this when data is loaded.
569 | * The UI will perform complete at once or after a delay, depends on the time elapsed is greater then {@link #mLoadingMinTime} or not.
570 | */
571 | final public void refreshComplete() {
572 |
573 | if (mRefreshCompleteHook != null) {
574 | mRefreshCompleteHook.reset();
575 | }
576 |
577 | int delay = (int) (mLoadingMinTime - (System.currentTimeMillis() - mLoadingStartTime));
578 | if (delay <= 0) {
579 | performRefreshComplete();
580 | } else {
581 | postDelayed(mPerformRefreshCompleteDelay, delay);
582 | }
583 | }
584 |
585 | /**
586 | * Do refresh complete work when time elapsed is greater than {@link #mLoadingMinTime}
587 | */
588 | private void performRefreshComplete() {
589 | mStatus = PTR_STATUS_COMPLETE;
590 |
591 | // if is auto refresh do nothing, wait scroller stop
592 | if (mScrollChecker.mIsRunning && isAutoRefresh()) {
593 | // do nothing
594 | if (DEBUG) {
595 | // PtrCLog.d(LOG_TAG, "performRefreshComplete do nothing, scrolling: %s, auto refresh: %s",
596 | // mScrollChecker.mIsRunning, mFlag);
597 | }
598 | return;
599 | }
600 |
601 | notifyUIRefreshComplete(false);
602 | }
603 |
604 | /**
605 | * Do real refresh work. If there is a hook, execute the hook first.
606 | *
607 | * @param ignoreHook
608 | */
609 | private void notifyUIRefreshComplete(boolean ignoreHook) {
610 | /**
611 | * After hook operation is done, {@link #notifyUIRefreshComplete} will be call in resume action to ignore hook.
612 | */
613 | if (mPtrIndicator.hasLeftStartPosition() && !ignoreHook && mRefreshCompleteHook != null) {
614 |
615 | mRefreshCompleteHook.takeOver();
616 | return;
617 | }
618 | if (mPtrUIHandlerHolder.hasHandler()) {
619 | mPtrUIHandlerHolder.onUIRefreshComplete(this);
620 | }
621 | mPtrIndicator.onUIRefreshComplete();
622 | tryScrollBackToTopAfterComplete();
623 | tryToNotifyReset();
624 | }
625 |
626 | public void autoRefresh() {
627 | autoRefresh(true, mDurationToCloseHeader);
628 | }
629 |
630 | public void autoRefresh(boolean atOnce) {
631 | autoRefresh(atOnce, mDurationToCloseHeader);
632 | }
633 |
634 | private void clearFlag() {
635 | // remove auto fresh flag
636 | mFlag = mFlag & ~MASK_AUTO_REFRESH;
637 | }
638 |
639 | public void autoRefresh(boolean atOnce, int duration) {
640 |
641 | if (mStatus != PTR_STATUS_INIT) {
642 | return;
643 | }
644 |
645 | mFlag |= atOnce ? FLAG_AUTO_REFRESH_AT_ONCE : FLAG_AUTO_REFRESH_BUT_LATER;
646 |
647 | mStatus = PTR_STATUS_PREPARE;
648 | if (mPtrUIHandlerHolder.hasHandler()) {
649 | mPtrUIHandlerHolder.onUIRefreshPrepare(this);
650 | if (DEBUG) {
651 | // PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshPrepare, mFlag %s", mFlag);
652 | }
653 | }
654 | mScrollChecker.tryToScrollTo(mPtrIndicator.getOffsetToRefresh(), duration);
655 | if (atOnce) {
656 | mStatus = PTR_STATUS_LOADING;
657 | performRefresh();
658 | }
659 | }
660 |
661 | public boolean isAutoRefresh() {
662 | return (mFlag & MASK_AUTO_REFRESH) > 0;
663 | }
664 |
665 | private boolean performAutoRefreshButLater() {
666 | return (mFlag & MASK_AUTO_REFRESH) == FLAG_AUTO_REFRESH_BUT_LATER;
667 | }
668 |
669 | public boolean isEnabledNextPtrAtOnce() {
670 | return (mFlag & FLAG_ENABLE_NEXT_PTR_AT_ONCE) > 0;
671 | }
672 |
673 | /**
674 | * If @param enable has been set to true. The user can perform next PTR at once.
675 | *
676 | * @param enable
677 | */
678 | public void setEnabledNextPtrAtOnce(boolean enable) {
679 | if (enable) {
680 | mFlag = mFlag | FLAG_ENABLE_NEXT_PTR_AT_ONCE;
681 | } else {
682 | mFlag = mFlag & ~FLAG_ENABLE_NEXT_PTR_AT_ONCE;
683 | }
684 | }
685 |
686 | public boolean isPinContent() {
687 | return (mFlag & FLAG_PIN_CONTENT) > 0;
688 | }
689 |
690 | /**
691 | * The content view will now move when {@param pinContent} set to true.
692 | *
693 | * @param pinContent
694 | */
695 | public void setPinContent(boolean pinContent) {
696 | if (pinContent) {
697 | mFlag = mFlag | FLAG_PIN_CONTENT;
698 | } else {
699 | mFlag = mFlag & ~FLAG_PIN_CONTENT;
700 | }
701 | }
702 |
703 | /**
704 | * It's useful when working with viewpager.
705 | *
706 | * @param disable
707 | */
708 | public void disableWhenHorizontalMove(boolean disable) {
709 | mDisableWhenHorizontalMove = disable;
710 | }
711 |
712 | /**
713 | * loading will last at least for so long
714 | *
715 | * @param time
716 | */
717 | public void setLoadingMinTime(int time) {
718 | mLoadingMinTime = time;
719 | }
720 |
721 | /**
722 | * Not necessary any longer. Once moved, cancel event will be sent to child.
723 | *
724 | * @param yes
725 | */
726 | @Deprecated
727 | public void setInterceptEventWhileWorking(boolean yes) {
728 | }
729 |
730 | @SuppressWarnings({"unused"})
731 | public View getContentView() {
732 | return mContent;
733 | }
734 |
735 | public void setPtrHandler(PtrHandler ptrHandler) {
736 | mPtrHandler = ptrHandler;
737 | }
738 |
739 | public void addPtrUIHandler(PtrUIHandler ptrUIHandler) {
740 | PtrUIHandlerHolder.addHandler(mPtrUIHandlerHolder, ptrUIHandler);
741 | }
742 |
743 | @SuppressWarnings({"unused"})
744 | public void removePtrUIHandler(PtrUIHandler ptrUIHandler) {
745 | mPtrUIHandlerHolder = PtrUIHandlerHolder.removeHandler(mPtrUIHandlerHolder, ptrUIHandler);
746 | }
747 |
748 | public void setPtrIndicator(PtrIndicator slider) {
749 | if (mPtrIndicator != null && mPtrIndicator != slider) {
750 | slider.convertFrom(mPtrIndicator);
751 | }
752 | mPtrIndicator = slider;
753 | }
754 |
755 | @SuppressWarnings({"unused"})
756 | public float getResistance() {
757 | return mPtrIndicator.getResistance();
758 | }
759 |
760 | public void setResistance(float resistance) {
761 | mPtrIndicator.setResistance(resistance);
762 | }
763 |
764 | @SuppressWarnings({"unused"})
765 | public float getDurationToClose() {
766 | return mDurationToClose;
767 | }
768 |
769 | /**
770 | * The duration to return back to the refresh position
771 | *
772 | * @param duration
773 | */
774 | public void setDurationToClose(int duration) {
775 | mDurationToClose = duration;
776 | }
777 |
778 | @SuppressWarnings({"unused"})
779 | public long getDurationToCloseHeader() {
780 | return mDurationToCloseHeader;
781 | }
782 |
783 | /**
784 | * The duration to close time
785 | *
786 | * @param duration
787 | */
788 | public void setDurationToCloseHeader(int duration) {
789 | mDurationToCloseHeader = duration;
790 | }
791 |
792 | public void setRatioOfHeaderHeightToRefresh(float ratio) {
793 | mPtrIndicator.setRatioOfHeaderHeightToRefresh(ratio);
794 | }
795 |
796 | public int getOffsetToRefresh() {
797 | return mPtrIndicator.getOffsetToRefresh();
798 | }
799 |
800 | @SuppressWarnings({"unused"})
801 | public void setOffsetToRefresh(int offset) {
802 | mPtrIndicator.setOffsetToRefresh(offset);
803 | }
804 |
805 | @SuppressWarnings({"unused"})
806 | public float getRatioOfHeaderToHeightRefresh() {
807 | return mPtrIndicator.getRatioOfHeaderToHeightRefresh();
808 | }
809 |
810 | @SuppressWarnings({"unused"})
811 | public int getOffsetToKeepHeaderWhileLoading() {
812 | return mPtrIndicator.getOffsetToKeepHeaderWhileLoading();
813 | }
814 |
815 | @SuppressWarnings({"unused"})
816 | public void setOffsetToKeepHeaderWhileLoading(int offset) {
817 | mPtrIndicator.setOffsetToKeepHeaderWhileLoading(offset);
818 | }
819 |
820 | @SuppressWarnings({"unused"})
821 | public boolean isKeepHeaderWhenRefresh() {
822 | return mKeepHeaderWhenRefresh;
823 | }
824 |
825 | public void setKeepHeaderWhenRefresh(boolean keepOrNot) {
826 | mKeepHeaderWhenRefresh = keepOrNot;
827 | }
828 |
829 | public boolean isPullToRefresh() {
830 | return mPullToRefresh;
831 | }
832 |
833 | public void setPullToRefresh(boolean pullToRefresh) {
834 | mPullToRefresh = pullToRefresh;
835 | }
836 |
837 | @SuppressWarnings({"unused"})
838 | public View getHeaderView() {
839 | return mHeaderView;
840 | }
841 |
842 | public void setHeaderView(View header) {
843 | if (mHeaderView != null && header != null && mHeaderView != header) {
844 | removeView(mHeaderView);
845 | }
846 | ViewGroup.LayoutParams lp = header.getLayoutParams();
847 | if (lp == null) {
848 | lp = new LayoutParams(-1, -2);
849 | header.setLayoutParams(lp);
850 | }
851 | mHeaderView = header;
852 | addView(header);
853 | }
854 |
855 | @Override
856 | protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
857 | return p instanceof LayoutParams;
858 | }
859 |
860 | @Override
861 | protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
862 | return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
863 | }
864 |
865 | @Override
866 | protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
867 | return new LayoutParams(p);
868 | }
869 |
870 | @Override
871 | public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
872 | return new LayoutParams(getContext(), attrs);
873 | }
874 |
875 | private void sendCancelEvent() {
876 | if (DEBUG) {
877 | // PtrCLog.d(LOG_TAG, "send cancel event");
878 | }
879 | // The ScrollChecker will update position and lead to send cancel event when mLastMoveEvent is null.
880 | // fix #104, #80, #92
881 | if (mLastMoveEvent == null) {
882 | return;
883 | }
884 | MotionEvent last = mLastMoveEvent;
885 | MotionEvent e = MotionEvent.obtain(last.getDownTime(), last.getEventTime() + ViewConfiguration.getLongPressTimeout(), MotionEvent.ACTION_CANCEL, last.getX(), last.getY(), last.getMetaState());
886 | dispatchTouchEventSupper(e);
887 | }
888 |
889 | private void sendDownEvent() {
890 | if (DEBUG) {
891 | // PtrCLog.d(LOG_TAG, "send down event");
892 | }
893 | final MotionEvent last = mLastMoveEvent;
894 | MotionEvent e = MotionEvent.obtain(last.getDownTime(), last.getEventTime(), MotionEvent.ACTION_DOWN, last.getX(), last.getY(), last.getMetaState());
895 | dispatchTouchEventSupper(e);
896 | }
897 |
898 | public static class LayoutParams extends MarginLayoutParams {
899 |
900 | public LayoutParams(Context c, AttributeSet attrs) {
901 | super(c, attrs);
902 | }
903 |
904 | public LayoutParams(int width, int height) {
905 | super(width, height);
906 | }
907 |
908 | @SuppressWarnings({"unused"})
909 | public LayoutParams(MarginLayoutParams source) {
910 | super(source);
911 | }
912 |
913 | public LayoutParams(ViewGroup.LayoutParams source) {
914 | super(source);
915 | }
916 | }
917 |
918 | class ScrollChecker implements Runnable {
919 |
920 | private int mLastFlingY;
921 | private Scroller mScroller;
922 | private boolean mIsRunning = false;
923 | private int mStart;
924 | private int mTo;
925 |
926 | public ScrollChecker() {
927 | mScroller = new Scroller(getContext());
928 | }
929 |
930 | public void run() {
931 | boolean finish = !mScroller.computeScrollOffset() || mScroller.isFinished();
932 | int curY = mScroller.getCurrY();
933 | int deltaY = curY - mLastFlingY;
934 | if (DEBUG) {
935 | if (deltaY != 0) {
936 | // PtrCLog.v(LOG_TAG,
937 | // "scroll: %s, start: %s, to: %s, currentPos: %s, current :%s, last: %s, delta: %s",
938 | // finish, mStart, mTo, mPtrIndicator.getCurrentPosY(), curY, mLastFlingY, deltaY);
939 | }
940 | }
941 | if (!finish) {
942 | mLastFlingY = curY;
943 | movePos(deltaY);
944 | post(this);
945 | } else {
946 | finish();
947 | }
948 | }
949 |
950 | private void finish() {
951 | if (DEBUG) {
952 | // PtrCLog.v(LOG_TAG, "finish, currentPos:%s", mPtrIndicator.getCurrentPosY());
953 | }
954 | reset();
955 | onPtrScrollFinish();
956 | }
957 |
958 | private void reset() {
959 | mIsRunning = false;
960 | mLastFlingY = 0;
961 | removeCallbacks(this);
962 | }
963 |
964 | private void destroy() {
965 | reset();
966 | if (!mScroller.isFinished()) {
967 | mScroller.forceFinished(true);
968 | }
969 | }
970 |
971 | public void abortIfWorking() {
972 | if (mIsRunning) {
973 | if (!mScroller.isFinished()) {
974 | mScroller.forceFinished(true);
975 | }
976 | onPtrScrollAbort();
977 | reset();
978 | }
979 | }
980 |
981 | public void tryToScrollTo(int to, int duration) {
982 | if (mPtrIndicator.isAlreadyHere(to)) {
983 | return;
984 | }
985 | mStart = mPtrIndicator.getCurrentPosY();
986 | mTo = to;
987 | int distance = to - mStart;
988 | if (DEBUG) {
989 | // PtrCLog.d(LOG_TAG, "tryToScrollTo: start: %s, distance:%s, to:%s", mStart, distance, to);
990 | }
991 | removeCallbacks(this);
992 |
993 | mLastFlingY = 0;
994 |
995 | // fix #47: Scroller should be reused, https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/issues/47
996 | if (!mScroller.isFinished()) {
997 | mScroller.forceFinished(true);
998 | }
999 | mScroller.startScroll(0, 0, 0, distance, duration);
1000 | post(this);
1001 | mIsRunning = true;
1002 | }
1003 | }
1004 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/ptr/PtrHandler.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview.ptr;
2 |
3 | import android.view.View;
4 |
5 | public interface PtrHandler {
6 |
7 | /**
8 | * Check can do refresh or not. For example the content is empty or the first child is in view.
9 | *
10 | */
11 | public boolean checkCanDoRefresh(final PtrFrameLayout frame, final View content, final View header);
12 |
13 | /**
14 | * When refresh begin
15 | *
16 | * @param frame
17 | */
18 | public void onRefreshBegin(final PtrFrameLayout frame);
19 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/ptr/PtrIndicator.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview.ptr;
2 |
3 | import android.graphics.PointF;
4 |
5 | public class PtrIndicator {
6 |
7 | public final static int POS_START = 0;
8 | protected int mOffsetToRefresh = 0;
9 | private PointF mPtLastMove = new PointF();
10 | private float mOffsetX;
11 | private float mOffsetY;
12 | private int mCurrentPos = 0;
13 | private int mLastPos = 0;
14 | private int mHeaderHeight;
15 | private int mPressedPos = 0;
16 |
17 | private float mRatioOfHeaderHeightToRefresh = 1.2f;
18 | private float mResistance = 1.7f;
19 | private boolean mIsUnderTouch = false;
20 | private int mOffsetToKeepHeaderWhileLoading = -1;
21 | // record the refresh complete position
22 | private int mRefreshCompleteY = 0;
23 |
24 | public boolean isUnderTouch() {
25 | return mIsUnderTouch;
26 | }
27 |
28 | public float getResistance() {
29 | return mResistance;
30 | }
31 |
32 | public void setResistance(float resistance) {
33 | mResistance = resistance;
34 | }
35 |
36 | public void onRelease() {
37 | mIsUnderTouch = false;
38 | }
39 |
40 | public void onUIRefreshComplete() {
41 | mRefreshCompleteY = mCurrentPos;
42 | }
43 |
44 | public boolean goDownCrossFinishPosition() {
45 | return mCurrentPos >= mRefreshCompleteY;
46 | }
47 |
48 | protected void processOnMove(float currentX, float currentY, float offsetX, float offsetY) {
49 | setOffset(offsetX, offsetY / mResistance);
50 | }
51 |
52 | public void setRatioOfHeaderHeightToRefresh(float ratio) {
53 | mRatioOfHeaderHeightToRefresh = ratio;
54 | mOffsetToRefresh = (int) (mHeaderHeight * ratio);
55 | }
56 |
57 | public float getRatioOfHeaderToHeightRefresh() {
58 | return mRatioOfHeaderHeightToRefresh;
59 | }
60 |
61 | public int getOffsetToRefresh() {
62 | return mOffsetToRefresh;
63 | }
64 |
65 | public void setOffsetToRefresh(int offset) {
66 | mRatioOfHeaderHeightToRefresh = mHeaderHeight * 1f / offset;
67 | mOffsetToRefresh = offset;
68 | }
69 |
70 | public void onPressDown(float x, float y) {
71 | mIsUnderTouch = true;
72 | mPressedPos = mCurrentPos;
73 | mPtLastMove.set(x, y);
74 | }
75 |
76 | public final void onMove(float x, float y) {
77 | float offsetX = x - mPtLastMove.x;
78 | float offsetY = (y - mPtLastMove.y);
79 | processOnMove(x, y, offsetX, offsetY);
80 | mPtLastMove.set(x, y);
81 | }
82 |
83 | protected void setOffset(float x, float y) {
84 | mOffsetX = x;
85 | mOffsetY = y;
86 | }
87 |
88 | public float getOffsetX() {
89 | return mOffsetX;
90 | }
91 |
92 | public float getOffsetY() {
93 | return mOffsetY;
94 | }
95 |
96 | public int getLastPosY() {
97 | return mLastPos;
98 | }
99 |
100 | public int getCurrentPosY() {
101 | return mCurrentPos;
102 | }
103 |
104 | /**
105 | * Update current position before update the UI
106 | */
107 | public final void setCurrentPos(int current) {
108 | mLastPos = mCurrentPos;
109 | mCurrentPos = current;
110 | onUpdatePos(current, mLastPos);
111 | }
112 |
113 | protected void onUpdatePos(int current, int last) {
114 |
115 | }
116 |
117 | public int getHeaderHeight() {
118 | return mHeaderHeight;
119 | }
120 |
121 | public void setHeaderHeight(int height) {
122 | mHeaderHeight = height;
123 | updateHeight();
124 | }
125 |
126 | protected void updateHeight() {
127 | mOffsetToRefresh = (int) (mRatioOfHeaderHeightToRefresh * mHeaderHeight);
128 | }
129 |
130 | public void convertFrom(PtrIndicator ptrSlider) {
131 | mCurrentPos = ptrSlider.mCurrentPos;
132 | mLastPos = ptrSlider.mLastPos;
133 | mHeaderHeight = ptrSlider.mHeaderHeight;
134 | }
135 |
136 | public boolean hasLeftStartPosition() {
137 | return mCurrentPos > POS_START;
138 | }
139 |
140 | public boolean hasJustLeftStartPosition() {
141 | return mLastPos == POS_START && hasLeftStartPosition();
142 | }
143 |
144 | public boolean hasJustBackToStartPosition() {
145 | return mLastPos != POS_START && isInStartPosition();
146 | }
147 |
148 | public boolean isOverOffsetToRefresh() {
149 | return mCurrentPos >= getOffsetToRefresh();
150 | }
151 |
152 | public boolean hasMovedAfterPressedDown() {
153 | return mCurrentPos != mPressedPos;
154 | }
155 |
156 | public boolean isInStartPosition() {
157 | return mCurrentPos == POS_START;
158 | }
159 |
160 | public boolean crossRefreshLineFromTopToBottom() {
161 | return mLastPos < getOffsetToRefresh() && mCurrentPos >= getOffsetToRefresh();
162 | }
163 |
164 | public boolean hasJustReachedHeaderHeightFromTopToBottom() {
165 | return mLastPos < mHeaderHeight && mCurrentPos >= mHeaderHeight;
166 | }
167 |
168 | public boolean isOverOffsetToKeepHeaderWhileLoading() {
169 | return mCurrentPos > getOffsetToKeepHeaderWhileLoading();
170 | }
171 |
172 | public void setOffsetToKeepHeaderWhileLoading(int offset) {
173 | mOffsetToKeepHeaderWhileLoading = offset;
174 | }
175 |
176 | public int getOffsetToKeepHeaderWhileLoading() {
177 | return mOffsetToKeepHeaderWhileLoading >= 0 ? mOffsetToKeepHeaderWhileLoading : mHeaderHeight;
178 | }
179 |
180 | public boolean isAlreadyHere(int to) {
181 | return mCurrentPos == to;
182 | }
183 |
184 | public float getLastPercent() {
185 | final float oldPercent = mHeaderHeight == 0 ? 0 : mLastPos * 1f / mHeaderHeight;
186 | return oldPercent;
187 | }
188 |
189 | public float getCurrentPercent() {
190 | final float currentPercent = mHeaderHeight == 0 ? 0 : mCurrentPos * 1f / mHeaderHeight;
191 | return currentPercent;
192 | }
193 |
194 | public boolean willOverTop(int to) {
195 | return to < POS_START;
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/ptr/PtrUIHandler.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview.ptr;
2 |
3 | /**
4 | * Created by jingbin on 16/4/28.
5 | */
6 | public interface PtrUIHandler {
7 |
8 | /**
9 | * When the content view has reached top and refresh has been completed, view will be reset.
10 | *
11 | * @param frame
12 | */
13 | public void onUIReset(PtrFrameLayout frame);
14 |
15 | /**
16 | * prepare for loading
17 | *
18 | * @param frame
19 | */
20 | public void onUIRefreshPrepare(PtrFrameLayout frame);
21 |
22 | /**
23 | * perform refreshing UI
24 | */
25 | public void onUIRefreshBegin(PtrFrameLayout frame);
26 |
27 | /**
28 | * perform UI after refresh
29 | */
30 | public void onUIRefreshComplete(PtrFrameLayout frame);
31 |
32 | public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator);
33 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/ptr/PtrUIHandlerHolder.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview.ptr;
2 |
3 | /**
4 | * A single linked list to wrap PtrUIHandler
5 | */
6 | class PtrUIHandlerHolder implements PtrUIHandler {
7 |
8 | private PtrUIHandler mHandler;
9 | private PtrUIHandlerHolder mNext;
10 |
11 | private boolean contains(PtrUIHandler handler) {
12 | return mHandler != null && mHandler == handler;
13 | }
14 |
15 | private PtrUIHandlerHolder() {
16 |
17 | }
18 |
19 | public boolean hasHandler() {
20 | return mHandler != null;
21 | }
22 |
23 | private PtrUIHandler getHandler() {
24 | return mHandler;
25 | }
26 |
27 | public static void addHandler(PtrUIHandlerHolder head, PtrUIHandler handler) {
28 |
29 | if (null == handler) {
30 | return;
31 | }
32 | if (head == null) {
33 | return;
34 | }
35 | if (null == head.mHandler) {
36 | head.mHandler = handler;
37 | return;
38 | }
39 |
40 | PtrUIHandlerHolder current = head;
41 | for (; ; current = current.mNext) {
42 |
43 | // duplicated
44 | if (current.contains(handler)) {
45 | return;
46 | }
47 | if (current.mNext == null) {
48 | break;
49 | }
50 | }
51 |
52 | PtrUIHandlerHolder newHolder = new PtrUIHandlerHolder();
53 | newHolder.mHandler = handler;
54 | current.mNext = newHolder;
55 | }
56 |
57 | public static PtrUIHandlerHolder create() {
58 | return new PtrUIHandlerHolder();
59 | }
60 |
61 | public static PtrUIHandlerHolder removeHandler(PtrUIHandlerHolder head, PtrUIHandler handler) {
62 | if (head == null || handler == null || null == head.mHandler) {
63 | return head;
64 | }
65 |
66 | PtrUIHandlerHolder current = head;
67 | PtrUIHandlerHolder pre = null;
68 | do {
69 |
70 | // delete current: link pre to next, unlink next from current;
71 | // pre will no change, current move to next element;
72 | if (current.contains(handler)) {
73 |
74 | // current is head
75 | if (pre == null) {
76 |
77 | head = current.mNext;
78 | current.mNext = null;
79 |
80 | current = head;
81 | } else {
82 |
83 | pre.mNext = current.mNext;
84 | current.mNext = null;
85 | current = pre.mNext;
86 | }
87 | } else {
88 | pre = current;
89 | current = current.mNext;
90 | }
91 |
92 | } while (current != null);
93 |
94 | if (head == null) {
95 | head = new PtrUIHandlerHolder();
96 | }
97 | return head;
98 | }
99 |
100 | @Override
101 | public void onUIReset(PtrFrameLayout frame) {
102 | PtrUIHandlerHolder current = this;
103 | do {
104 | final PtrUIHandler handler = current.getHandler();
105 | if (null != handler) {
106 | handler.onUIReset(frame);
107 | }
108 | } while ((current = current.mNext) != null);
109 | }
110 |
111 | @Override
112 | public void onUIRefreshPrepare(PtrFrameLayout frame) {
113 | if (!hasHandler()) {
114 | return;
115 | }
116 | PtrUIHandlerHolder current = this;
117 | do {
118 | final PtrUIHandler handler = current.getHandler();
119 | if (null != handler) {
120 | handler.onUIRefreshPrepare(frame);
121 | }
122 | } while ((current = current.mNext) != null);
123 | }
124 |
125 | @Override
126 | public void onUIRefreshBegin(PtrFrameLayout frame) {
127 | PtrUIHandlerHolder current = this;
128 | do {
129 | final PtrUIHandler handler = current.getHandler();
130 | if (null != handler) {
131 | handler.onUIRefreshBegin(frame);
132 | }
133 | } while ((current = current.mNext) != null);
134 | }
135 |
136 | @Override
137 | public void onUIRefreshComplete(PtrFrameLayout frame) {
138 | PtrUIHandlerHolder current = this;
139 | do {
140 | final PtrUIHandler handler = current.getHandler();
141 | if (null != handler) {
142 | handler.onUIRefreshComplete(frame);
143 | }
144 | } while ((current = current.mNext) != null);
145 | }
146 |
147 | @Override
148 | public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {
149 | PtrUIHandlerHolder current = this;
150 | do {
151 | final PtrUIHandler handler = current.getHandler();
152 | if (null != handler) {
153 | handler.onUIPositionChange(frame, isUnderTouch, status, ptrIndicator);
154 | }
155 | } while ((current = current.mNext) != null);
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/ptr/PtrUIHandlerHook.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview.ptr;
2 |
3 | /**
4 | * Run a hook runnable, the runnable will run only once.
5 | * After the runnable is done, call resume to resume.
6 | * Once run, call takeover will directory call the resume action
7 | */
8 | public abstract class PtrUIHandlerHook implements Runnable {
9 |
10 | private Runnable mResumeAction;
11 | private static final byte STATUS_PREPARE = 0;
12 | private static final byte STATUS_IN_HOOK = 1;
13 | private static final byte STATUS_RESUMED = 2;
14 | private byte mStatus = STATUS_PREPARE;
15 |
16 | public void takeOver() {
17 | takeOver(null);
18 | }
19 |
20 | public void takeOver(Runnable resumeAction) {
21 | if (resumeAction != null) {
22 | mResumeAction = resumeAction;
23 | }
24 | switch (mStatus) {
25 | case STATUS_PREPARE:
26 | mStatus = STATUS_IN_HOOK;
27 | run();
28 | break;
29 | case STATUS_IN_HOOK:
30 | break;
31 | case STATUS_RESUMED:
32 | resume();
33 | break;
34 | }
35 | }
36 |
37 | public void reset() {
38 | mStatus = STATUS_PREPARE;
39 | }
40 |
41 | public void resume() {
42 | if (mResumeAction != null) {
43 | mResumeAction.run();
44 | }
45 | mStatus = STATUS_RESUMED;
46 | }
47 |
48 | /**
49 | * Hook should always have a resume action, which is hooked by this hook.
50 | *
51 | * @param runnable
52 | */
53 | public void setResumeAction(Runnable runnable) {
54 | mResumeAction = runnable;
55 | }
56 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/com/example/jingbin/app/stickview/view/NoScrollViewPager.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app.stickview.view;
2 |
3 |
4 | import android.content.Context;
5 | import android.support.v4.view.ViewPager;
6 | import android.util.AttributeSet;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 |
10 | public class NoScrollViewPager extends ViewPager {
11 |
12 | private boolean mNoScroll = false;
13 | // private boolean mNoScroll = true;
14 | private View mHeaderView;
15 |
16 | public NoScrollViewPager(Context context, AttributeSet attrs) {
17 | super(context, attrs);
18 | }
19 |
20 | public NoScrollViewPager(Context context) {
21 | super(context);
22 | }
23 |
24 | public void setNoScroll(boolean noScroll) {
25 | this.mNoScroll = noScroll;
26 | }
27 |
28 | public View getHeaderView() {
29 | return mHeaderView;
30 | }
31 |
32 | public void setHeaderView(View mHeaderView) {
33 | this.mHeaderView = mHeaderView;
34 | }
35 |
36 | @Override
37 | public boolean onTouchEvent(MotionEvent arg0) {
38 | if (mNoScroll) {
39 | return false;
40 | } else {
41 | return super.onTouchEvent(arg0);
42 | }
43 |
44 | }
45 |
46 | @Override
47 | public boolean onInterceptTouchEvent(MotionEvent arg0) {
48 | if (mHeaderView != null) {
49 | if (mHeaderView.getTop() < arg0.getY() && arg0.getY() < mHeaderView.getBottom() + mHeaderView.getTranslationY()) {
50 | mNoScroll = true;
51 | } else {
52 | mNoScroll = false;
53 | }
54 | }
55 | if (mNoScroll) {
56 | return false;
57 | } else {
58 | return super.onInterceptTouchEvent(arg0);
59 | }
60 | }
61 |
62 |
63 | }
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable-hdpi/app_loading0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/drawable-hdpi/app_loading0.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable-hdpi/app_loading1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/drawable-hdpi/app_loading1.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable-hdpi/app_loading2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/drawable-hdpi/app_loading2.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable-hdpi/app_loading3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/drawable-hdpi/app_loading3.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable-xhdpi/app_loading0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/drawable-xhdpi/app_loading0.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable-xhdpi/app_loading1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/drawable-xhdpi/app_loading1.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable-xhdpi/app_loading2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/drawable-xhdpi/app_loading2.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable-xhdpi/app_loading3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/drawable-xhdpi/app_loading3.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable/kaws_anim.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
11 |
14 |
17 |
18 |
--------------------------------------------------------------------------------
/App/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
34 |
35 |
39 |
40 |
41 |
50 |
51 |
52 |
53 |
54 |
60 |
61 |
62 |
68 |
69 |
76 |
77 |
84 |
85 |
89 |
90 |
91 |
92 |
98 |
99 |
106 |
107 |
114 |
115 |
119 |
120 |
121 |
122 |
128 |
129 |
136 |
137 |
144 |
145 |
149 |
150 |
151 |
152 |
153 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/App/app/src/main/res/layout/cube_ptr_classic_default_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
21 |
22 |
30 |
31 |
32 |
38 |
39 |
47 |
48 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/App/app/src/main/res/layout/item_goods_detail_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/App/app/src/main/res/layout/item_goods_detail_web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/App/app/src/main/res/layout/item_goods_evaluation_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/App/app/src/main/res/layout/layout_goods_detail_listview.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/App/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/App/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/App/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #f2f2f1
8 | #E0E0E0
9 |
10 |
--------------------------------------------------------------------------------
/App/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/App/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | StickViewLayout
3 |
4 |
5 | 下拉刷新...
6 | 下拉刷新...
7 | 放开以刷新...
8 | 正在载入...
9 | 刷新完成...
10 |
11 |
--------------------------------------------------------------------------------
/App/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/App/app/src/test/java/com/example/jingbin/app/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.example.jingbin.app;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/App/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 | maven {
7 | url 'https://maven.google.com/'
8 | name 'Google'
9 | }
10 | }
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:3.1.3'
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | jcenter()
22 | maven {
23 | url 'https://maven.google.com/'
24 | name 'Google'
25 | }
26 | }
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/App/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/App/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/App/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Nov 12 17:30:26 CST 2019
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-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/App/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 |
--------------------------------------------------------------------------------
/App/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 |
--------------------------------------------------------------------------------
/App/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # StickViewLayout
2 | 仿原京东商品详情页,上拉Tab置顶,可查看图文详情,参数详情,商品评论。
3 |
4 | ## 示例图
5 | 
6 |
7 | > 参考:https://github.com/w446108264/StickyHeaderViewPager
--------------------------------------------------------------------------------
/sample.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/youlookwhat/StickViewLayout/fc41a18290ff0e82bc713d44fa32f0ca4b5b8998/sample.gif
--------------------------------------------------------------------------------