├── .gitignore
├── AndroidManifest.xml
├── README.md
├── ant.properties
├── build.xml
├── libs
├── android-support-v4.jar
└── picasso-2.0.0.jar
├── proguard-project.txt
├── project.properties
├── res
├── anim
│ ├── slide_in_from_bottom.xml
│ ├── slide_in_from_top.xml
│ ├── slide_out_to_bottom.xml
│ └── slide_out_to_top.xml
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-ldpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ ├── default_ptr_flip.png
│ ├── default_ptr_rotate.png
│ ├── ic_launcher.png
│ └── indicator_arrow.png
├── drawable
│ ├── indicator_bg_bottom.xml
│ └── indicator_bg_top.xml
├── layout
│ ├── ac_main.xml
│ ├── ac_stgv.xml
│ ├── ac_stgv_with_ptr.xml
│ ├── cell_stgv.xml
│ ├── layout_loading_footer.xml
│ ├── pull_to_refresh_header_horizontal.xml
│ └── pull_to_refresh_header_vertical.xml
└── values
│ ├── ptr_attrs.xml
│ ├── ptr_dimens.xml
│ ├── ptr_ids.xml
│ ├── ptr_strings.xml
│ ├── stgv_attrs.xml
│ └── strings.xml
├── snapshot
└── snap.png
└── src
└── com
├── bulletnoid
└── android
│ └── widget
│ ├── StaggeredGridView
│ ├── HeaderFooterListAdapter.java
│ ├── ScrollerCompat.java
│ ├── ScrollerCompatIcs.java
│ └── StaggeredGridView.java
│ └── StaggeredGridViewDemo
│ ├── DataSet.java
│ ├── Item.java
│ ├── MainActivity.java
│ ├── STGVActivity.java
│ ├── STGVAdapter.java
│ ├── STGVImageView.java
│ └── STGVWithPTRActivity.java
└── handmark
└── pulltorefresh
└── library
├── ILoadingLayout.java
├── IPullToRefresh.java
├── LoadingLayoutProxy.java
├── OverscrollHelper.java
├── PullToRefreshAdapterViewBase.java
├── PullToRefreshBase.java
├── PullToRefreshExpandableListView.java
├── PullToRefreshGridView.java
├── PullToRefreshHorizontalScrollView.java
├── PullToRefreshListView.java
├── PullToRefreshScrollView.java
├── PullToRefreshStaggeredGridView.java
├── PullToRefreshWebView.java
├── extras
├── PullToRefreshWebView2.java
└── SoundPullEventListener.java
└── internal
├── EmptyViewMethodAccessor.java
├── FlipLoadingLayout.java
├── IndicatorLayout.java
├── LoadingLayout.java
├── RotateLoadingLayout.java
├── Utils.java
└── ViewCompat.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Eclipse project files
19 | .classpath
20 | .project
21 |
22 | # IntelliJ project files
23 | .idea/
24 | *.iml
25 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
18 |
19 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
31 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | StaggeredGridView
2 | =================
3 |
4 | ## A sweeter StaggeredGridView
5 |
6 | This widget is based on [maurycyw/StaggeredGridView][1], which is a modification of Android's experimental StaggeredGridView: [com.android.ex.widget.StaggeredGridView][2]
7 |
8 | This widget has fixed some of the major bugs and has some new features that you may be interested.
9 |
10 | 
11 |
12 | ## Suggestion
13 |
14 | It is very helpful for you to use this widget in your app and understand the restriction in it if you had knowledge of how to create Android custom views;
15 | http://developer.android.com/guide/topics/ui/custom-components.html has good information about that;
16 |
17 | ## Features
18 |
19 | * Stability and high performance
20 |
21 | This widget fix some bugs of [maurycyw/StaggeredGridView][1]. Such as when fling the view, the scroll sometimes slow down and speed up later.
22 |
23 | Notice that the image loading also has a contribution to the performance. I use [square/Picasso][3], it provides the best performance I've ever seen.
24 | * Header and Footer View and an Adapter to wrap all child views, just like android.widget.ListView
25 |
26 | Header and footer views can cross columns, but the widget currently only support no more than one header and no more than one footer.
27 | * Load more when get to the bottom
28 |
29 | You may find the footer view useful here.
30 | * Work with PullToRefresh
31 |
32 | A compatible part enable this widget to be pulled to refresh.
33 |
34 | ## Restriction
35 |
36 | * You have to determine the dimension of each child view in the widget before the parent the child.measure()
37 |
38 | This is because the after the child is first time added to the parent widget, its size should not be changed, otherwise it may cause gird misalign as you may have seen in [maurycyw/StaggeredGridView][1].
39 |
40 | eg. You want to display pictures in the widget, and the pictures are loaded from network.
41 | The height of each grid is decided by the size of the picture in them.
42 | Say, if you set your picture container to WRAP_CONTENT, the size of the pic container may change when the picture is loaded, and then the size of the grid change.
43 | This can cause gird misalign.
44 | Unfortunately, the current methodology has nothing to do to fix this.
45 | Instead, you can let this widget know the size of each child before the picture is actually downloaded.
46 | You can do this by overwrite the onMeasure() method of the container.
47 |
48 | * Load more is lazy
49 |
50 | When load more, the widget only add new items to the old ones, the old ones is not reloaded.
51 |
52 | * Screen rotation
53 |
54 | The widget may have problem holding the state when it is destroyed and restored the them in a different screen orientation.
55 | Besides, you may want to change the column number when the screen orientation changed, you'd better rebuild the whole content from start.
56 |
57 | ## Project structure
58 |
59 | Project contains StaggeredGridView library, StaggeredGridView demo, modified PullToRefresh library to work with StaggeredGridView.
60 | In order to avoid some dependency problems, I add the libs into one project, but it is easy to retrieve the libs.
61 | * StaggeredGridView lib
62 |
63 | code: src/com.bulletoid.android.widget.StaggeredGridView
64 |
65 | res: res/stgv_*.xml
66 | * PullToRefresh lib
67 |
68 | code: src/com.handmark.pulltorefresh.library
69 |
70 | res: res/ptr_*.xml
71 |
72 | ## Usage
73 |
74 | Please refer to the Demo of how to use the widget and use it with PullToRefresh.
75 |
76 | ## Pictures in the demo are from Pinterest
77 |
78 | License
79 | --------
80 |
81 |
82 | Licensed under the Apache License, Version 2.0 (the "License");
83 | you may not use this file except in compliance with the License.
84 | You may obtain a copy of the License at
85 |
86 | http://www.apache.org/licenses/LICENSE-2.0
87 |
88 | Unless required by applicable law or agreed to in writing, software
89 | distributed under the License is distributed on an "AS IS" BASIS,
90 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
91 | See the License for the specific language governing permissions and
92 | limitations under the License.
93 |
94 |
95 | [1]: https://github.com/maurycyw/StaggeredGridView
96 | [2]: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/com/android/ex/widget/StaggeredGridView.java?av=f
97 | [3]: https://github.com/square/picasso
98 |
--------------------------------------------------------------------------------
/ant.properties:
--------------------------------------------------------------------------------
1 | # This file is used to override default values used by the Ant build system.
2 | #
3 | # This file must be checked into Version Control Systems, as it is
4 | # integral to the build system of your project.
5 |
6 | # This file is only used by the Ant script.
7 |
8 | # You can use this to override default values such as
9 | # 'source.dir' for the location of your java source folder and
10 | # 'out.dir' for the location of your output folder.
11 |
12 | # You can also use it define how the release builds are signed by declaring
13 | # the following properties:
14 | # 'key.store' for the location of your keystore and
15 | # 'key.alias' for the name of the key to use.
16 | # The password will be asked during the build when you use the 'release' target.
17 |
18 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
29 |
30 |
31 |
35 |
36 |
37 |
38 |
39 |
40 |
49 |
50 |
51 |
52 |
56 |
57 |
69 |
70 |
71 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bulletnoid/StaggeredGridView/77b7d52aa707fd10c74b98e540b966929bfc66c6/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/libs/picasso-2.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bulletnoid/StaggeredGridView/77b7d52aa707fd10c74b98e540b966929bfc66c6/libs/picasso-2.0.0.jar
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-18
15 |
16 |
--------------------------------------------------------------------------------
/res/anim/slide_in_from_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
22 |
--------------------------------------------------------------------------------
/res/anim/slide_in_from_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
22 |
--------------------------------------------------------------------------------
/res/anim/slide_out_to_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
22 |
--------------------------------------------------------------------------------
/res/anim/slide_out_to_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
22 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bulletnoid/StaggeredGridView/77b7d52aa707fd10c74b98e540b966929bfc66c6/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bulletnoid/StaggeredGridView/77b7d52aa707fd10c74b98e540b966929bfc66c6/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bulletnoid/StaggeredGridView/77b7d52aa707fd10c74b98e540b966929bfc66c6/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/default_ptr_flip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bulletnoid/StaggeredGridView/77b7d52aa707fd10c74b98e540b966929bfc66c6/res/drawable-xhdpi/default_ptr_flip.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/default_ptr_rotate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bulletnoid/StaggeredGridView/77b7d52aa707fd10c74b98e540b966929bfc66c6/res/drawable-xhdpi/default_ptr_rotate.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bulletnoid/StaggeredGridView/77b7d52aa707fd10c74b98e540b966929bfc66c6/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/indicator_arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bulletnoid/StaggeredGridView/77b7d52aa707fd10c74b98e540b966929bfc66c6/res/drawable-xhdpi/indicator_arrow.png
--------------------------------------------------------------------------------
/res/drawable/indicator_bg_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/res/drawable/indicator_bg_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/res/layout/ac_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/res/layout/ac_stgv.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
15 |
16 |
--------------------------------------------------------------------------------
/res/layout/ac_stgv_with_ptr.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/res/layout/cell_stgv.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
14 |
15 |
22 |
23 |
--------------------------------------------------------------------------------
/res/layout/layout_loading_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/res/layout/pull_to_refresh_header_horizontal.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
18 |
19 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/res/layout/pull_to_refresh_header_vertical.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
17 |
18 |
23 |
24 |
32 |
33 |
34 |
40 |
41 |
48 |
49 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/res/values/ptr_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 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
66 |
67 |
68 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/res/values/ptr_dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 8dp
5 | 10dp
6 | 12dp
7 | 4dp
8 | 24dp
9 | 12dp
10 |
11 |
--------------------------------------------------------------------------------
/res/values/ptr_ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/values/ptr_strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Pull to refresh…
5 | Release to refresh…
6 | Loading…
7 |
8 |
9 | @string/pull_to_refresh_pull_label
10 | @string/pull_to_refresh_release_label
11 | @string/pull_to_refresh_refreshing_label
12 |
13 |
--------------------------------------------------------------------------------
/res/values/stgv_attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | StaggeredGridViewDemo
4 |
5 |
--------------------------------------------------------------------------------
/snapshot/snap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bulletnoid/StaggeredGridView/77b7d52aa707fd10c74b98e540b966929bfc66c6/snapshot/snap.png
--------------------------------------------------------------------------------
/src/com/bulletnoid/android/widget/StaggeredGridView/HeaderFooterListAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.bulletnoid.android.widget.StaggeredGridView;
18 |
19 | import android.database.DataSetObserver;
20 | import android.view.View;
21 | import android.view.ViewGroup;
22 | import android.widget.ListAdapter;
23 | import android.widget.WrapperListAdapter;
24 |
25 | public class HeaderFooterListAdapter implements WrapperListAdapter {
26 | private ListAdapter mAdapter;
27 | View mHeaderView;
28 | View mFooterView;
29 | boolean mAreAllFixedViewsSelectable;
30 | private int headerSize = -1;
31 | private int footerSize = -1;
32 |
33 | public static int TYPE_COUNT_WITHOUT_REFRESHABLE_LIST = 2;
34 | public static int HEADER_TYPE = 0;
35 | public static int FOOTER_TYPE = HEADER_TYPE + 1;
36 | public static int REFRESHABLE_LIST_TYPE = FOOTER_TYPE + 1;
37 |
38 | public HeaderFooterListAdapter(View headerView, View footerView, ListAdapter adapter) {
39 | mAdapter = adapter;
40 |
41 | if (headerView == null) {
42 | headerSize = 0;
43 | } else {
44 | headerSize = 1;
45 | mHeaderView = headerView;
46 | }
47 |
48 | if (footerView == null) {
49 | footerSize = 0;
50 | } else {
51 | footerSize = 1;
52 | mFooterView = footerView;
53 | }
54 |
55 | mAreAllFixedViewsSelectable = true;
56 | }
57 |
58 | @Override
59 | public ListAdapter getWrappedAdapter() {
60 | return mAdapter;
61 | }
62 |
63 | @Override
64 | public boolean areAllItemsEnabled() {
65 | if (mAdapter != null) {
66 | return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
67 | } else {
68 | return true;
69 | }
70 | }
71 |
72 | @Override
73 | public boolean isEnabled(int position) {
74 | if (position < headerSize) {
75 | return true;
76 | }
77 |
78 | int adjPosition = position - headerSize;
79 | if (mAdapter != null) {
80 | if (adjPosition < mAdapter.getCount()) {
81 | return mAdapter.isEnabled(adjPosition);
82 | }
83 | }
84 |
85 | return false;
86 | }
87 |
88 | @Override
89 | public void registerDataSetObserver(DataSetObserver observer) {
90 | if (mAdapter != null) {
91 | mAdapter.registerDataSetObserver(observer);
92 | }
93 | }
94 |
95 | @Override
96 | public void unregisterDataSetObserver(DataSetObserver observer) {
97 | if (mAdapter != null) {
98 | mAdapter.unregisterDataSetObserver(observer);
99 | }
100 | }
101 |
102 | @Override
103 | public int getCount() {
104 | if (mAdapter != null) {
105 | return headerSize + footerSize + mAdapter.getCount();
106 | } else {
107 | return headerSize + footerSize;
108 | }
109 | }
110 |
111 | @Override
112 | public Object getItem(int position) {
113 | if (position < headerSize) {
114 | return mHeaderView;
115 | }
116 |
117 | final int adjPosition = position - headerSize;
118 | int adapterCount = 0;
119 | if (mAdapter != null) {
120 | adapterCount = mAdapter.getCount();
121 | if (adjPosition < adapterCount) {
122 | return mAdapter.getItem(adjPosition);
123 | }
124 | }
125 |
126 | return mFooterView;
127 | }
128 |
129 | @Override
130 | public long getItemId(int position) {
131 | if (mAdapter != null && position >= headerSize) {
132 | int adjPosition = position - headerSize;
133 | int adapterCount = mAdapter.getCount();
134 | if (adjPosition < adapterCount) {
135 | return mAdapter.getItemId(adjPosition);
136 | }
137 | }
138 | return -1;
139 | }
140 |
141 | @Override
142 | public boolean hasStableIds() {
143 | if (mAdapter != null) {
144 | return mAdapter.hasStableIds();
145 | }
146 | return false;
147 | }
148 |
149 | @Override
150 | public View getView(int position, View convertView, ViewGroup parent) {
151 | if (position < headerSize) {
152 | return mHeaderView;
153 | }
154 |
155 | final int adjPosition = position - headerSize;
156 | int adapterCount = 0;
157 | if (mAdapter != null) {
158 | adapterCount = mAdapter.getCount();
159 | if (adjPosition < adapterCount) {
160 | return mAdapter.getView(adjPosition, convertView, parent);
161 | }
162 | }
163 |
164 | return mFooterView;
165 | }
166 |
167 | @Override
168 | public int getItemViewType(int position) {
169 | if (position < headerSize) {
170 | return HEADER_TYPE;
171 | }
172 |
173 | if (mAdapter != null && position >= headerSize) {
174 | int adjPosition = position - headerSize;
175 | int adapterCount = mAdapter.getCount();
176 | if (adjPosition < adapterCount) {
177 | return mAdapter.getItemViewType(adjPosition) + REFRESHABLE_LIST_TYPE;
178 | }
179 | }
180 |
181 | return FOOTER_TYPE;
182 | }
183 |
184 | @Override
185 | public int getViewTypeCount() {
186 | if (mAdapter != null) {
187 | return mAdapter.getViewTypeCount() + TYPE_COUNT_WITHOUT_REFRESHABLE_LIST;
188 | }
189 | return 1;
190 | }
191 |
192 | @Override
193 | public boolean isEmpty() {
194 | return mAdapter == null || mAdapter.isEmpty();
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/com/bulletnoid/android/widget/StaggeredGridView/ScrollerCompat.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.bulletnoid.android.widget.StaggeredGridView;
18 |
19 | import android.content.Context;
20 | import android.widget.Scroller;
21 |
22 | /**
23 | * Provides access to new {@link android.widget.Scroller Scroller} APIs when available.
24 | *
25 | *
This class provides a platform version-independent mechanism for obeying the
26 | * current device's preferred scroll physics and fling behavior. It offers a subset of
27 | * the APIs from Scroller or OverScroller.
28 | */
29 | class ScrollerCompat {
30 | Scroller mScroller;
31 |
32 | static class ScrollerCompatImplIcs extends ScrollerCompat {
33 | public ScrollerCompatImplIcs(Context context) {
34 | super(context);
35 | }
36 |
37 | @Override
38 | public float getCurrVelocity() {
39 | return ScrollerCompatIcs.getCurrVelocity(mScroller);
40 | }
41 | }
42 |
43 | public static ScrollerCompat from(Context context) {
44 | if (android.os.Build.VERSION.SDK_INT >= 14) {
45 | return new ScrollerCompatImplIcs(context);
46 | }
47 | return new ScrollerCompat(context);
48 | }
49 |
50 | ScrollerCompat(Context context) {
51 | mScroller = new Scroller(context);
52 | }
53 |
54 | /**
55 | * Returns whether the scroller has finished scrolling.
56 | *
57 | * @return True if the scroller has finished scrolling, false otherwise.
58 | */
59 | public boolean isFinished() {
60 | return mScroller.isFinished();
61 | }
62 |
63 | /**
64 | * Returns how long the scroll event will take, in milliseconds.
65 | *
66 | * @return The duration of the scroll in milliseconds.
67 | */
68 | public int getDuration() {
69 | return mScroller.getDuration();
70 | }
71 |
72 | /**
73 | * Returns the current X offset in the scroll.
74 | *
75 | * @return The new X offset as an absolute distance from the origin.
76 | */
77 | public int getCurrX() {
78 | return mScroller.getCurrX();
79 | }
80 |
81 | /**
82 | * Returns the current Y offset in the scroll.
83 | *
84 | * @return The new Y offset as an absolute distance from the origin.
85 | */
86 | public int getCurrY() {
87 | return mScroller.getCurrY();
88 | }
89 |
90 | /**
91 | * Returns the current velocity.
92 | *
93 | * TODO: Approximate a sane result for older platform versions. Right now
94 | * this will return 0 for platforms earlier than ICS. This is acceptable
95 | * at the moment only since it is only used for EdgeEffect, which is also only
96 | * present in ICS+, and ScrollerCompat is not public.
97 | *
98 | * @return The original velocity less the deceleration. Result may be
99 | * negative.
100 | */
101 | public float getCurrVelocity() {
102 | return 0;
103 | }
104 |
105 | /**
106 | * Call this when you want to know the new location. If it returns true,
107 | * the animation is not yet finished. loc will be altered to provide the
108 | * new location.
109 | */
110 | public boolean computeScrollOffset() {
111 | return mScroller.computeScrollOffset();
112 | }
113 |
114 | /**
115 | * Start scrolling by providing a starting point and the distance to travel.
116 | * The scroll will use the default value of 250 milliseconds for the
117 | * duration.
118 | *
119 | * @param startX Starting horizontal scroll offset in pixels. Positive
120 | * numbers will scroll the content to the left.
121 | * @param startY Starting vertical scroll offset in pixels. Positive numbers
122 | * will scroll the content up.
123 | * @param dx Horizontal distance to travel. Positive numbers will scroll the
124 | * content to the left.
125 | * @param dy Vertical distance to travel. Positive numbers will scroll the
126 | * content up.
127 | */
128 | public void startScroll(int startX, int startY, int dx, int dy) {
129 | mScroller.startScroll(startX, startY, dx, dy);
130 | }
131 |
132 | /**
133 | * Start scrolling by providing a starting point and the distance to travel.
134 | *
135 | * @param startX Starting horizontal scroll offset in pixels. Positive
136 | * numbers will scroll the content to the left.
137 | * @param startY Starting vertical scroll offset in pixels. Positive numbers
138 | * will scroll the content up.
139 | * @param dx Horizontal distance to travel. Positive numbers will scroll the
140 | * content to the left.
141 | * @param dy Vertical distance to travel. Positive numbers will scroll the
142 | * content up.
143 | * @param duration Duration of the scroll in milliseconds.
144 | */
145 | public void startScroll(int startX, int startY, int dx, int dy, int duration) {
146 | mScroller.startScroll(startX, startY, dx, dy, duration);
147 | }
148 |
149 | /**
150 | * Start scrolling based on a fling gesture. The distance travelled will
151 | * depend on the initial velocity of the fling.
152 | *
153 | * @param startX Starting point of the scroll (X)
154 | * @param startY Starting point of the scroll (Y)
155 | * @param velocityX Initial velocity of the fling (X) measured in pixels per
156 | * second.
157 | * @param velocityY Initial velocity of the fling (Y) measured in pixels per
158 | * second
159 | * @param minX Minimum X value. The scroller will not scroll past this
160 | * point.
161 | * @param maxX Maximum X value. The scroller will not scroll past this
162 | * point.
163 | * @param minY Minimum Y value. The scroller will not scroll past this
164 | * point.
165 | * @param maxY Maximum Y value. The scroller will not scroll past this
166 | * point.
167 | */
168 | public void fling(int startX, int startY, int velocityX, int velocityY,
169 | int minX, int maxX, int minY, int maxY) {
170 | mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
171 | }
172 |
173 | /**
174 | * Stops the animation. Contrary to {@link #forceFinished(boolean)},
175 | * aborting the animating cause the scroller to move to the final x and y
176 | * position
177 | */
178 | public void abortAnimation() {
179 | mScroller.abortAnimation();
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/src/com/bulletnoid/android/widget/StaggeredGridView/ScrollerCompatIcs.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.bulletnoid.android.widget.StaggeredGridView;
18 |
19 | import android.annotation.TargetApi;
20 | import android.os.Build;
21 | import android.widget.Scroller;
22 |
23 | /**
24 | * ICS API access for Scroller
25 | */
26 | class ScrollerCompatIcs {
27 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
28 | public static float getCurrVelocity(Scroller scroller) {
29 | return scroller.getCurrVelocity();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/com/bulletnoid/android/widget/StaggeredGridViewDemo/DataSet.java:
--------------------------------------------------------------------------------
1 | package com.bulletnoid.android.widget.StaggeredGridViewDemo;
2 |
3 | public class DataSet {
4 | public String url[] = {
5 | "http://media-cache-ak0.pinimg.com/736x/92/c3/92/92c39256180c5833348bf13f484c7118.jpg",
6 | "http://media-cache-ak0.pinimg.com/736x/9c/ce/ef/9cceef143ae517e7841669a930afcce0.jpg",
7 | "http://media-cache-ak0.pinimg.com/736x/f4/0e/ea/f40eeadee9be166a7765a2fa8dc329ee.jpg",
8 | "http://media-cache-ak0.pinimg.com/736x/61/c2/7b/61c27ba27d8e4d1be5a4e9885e41437a.jpg",
9 | "http://media-cache-ec0.pinimg.com/736x/7c/66/c3/7c66c3ed5840b7dca55e57380d3d6bac.jpg",
10 | "http://media-cache-ak0.pinimg.com/736x/2f/0c/15/2f0c1576c034ff4979c0ebdad42c646d.jpg",
11 | "http://media-cache-ak0.pinimg.com/736x/fe/c4/e9/fec4e938f2e6912c97405627bb902527.jpg",
12 | "http://media-cache-ak0.pinimg.com/736x/d6/ce/f5/d6cef528704c4b6bdf4fac2f48805b31.jpg",
13 | "http://media-cache-ec0.pinimg.com/736x/c4/2f/10/c42f10f8aa4803985f0ec7bde6d1ee7c.jpg",
14 | "http://media-cache-ak0.pinimg.com/736x/fe/2d/ef/fe2def2d9b5383cb886e62ea12ae5e4e.jpg",
15 | "http://media-cache-ec0.pinimg.com/736x/2b/38/f8/2b38f835e98b8c09d7f99f7791539901.jpg",
16 | "http://media-cache-ec0.pinimg.com/736x/9b/98/16/9b9816242b6433c6844bbf70cddbbe1a.jpg",
17 | "http://media-cache-ec0.pinimg.com/736x/2d/f0/92/2df0925b13d7bc782512b41d373cd941.jpg",
18 | "http://media-cache-ec0.pinimg.com/736x/98/1e/98/981e98dca0a0968b36007ec2ba45d973.jpg",
19 | "http://media-cache-ak0.pinimg.com/736x/8f/4e/f3/8f4ef30d53b3137034f539510062cd47.jpg",
20 | "http://media-cache-ec0.pinimg.com/736x/3e/16/5f/3e165fce7cb73211be5b5f78a426f0f7.jpg",
21 | "http://media-cache-ak0.pinimg.com/736x/90/fb/ab/90fbab0ef514bd47ea9bd81b78e43252.jpg",
22 | "http://media-cache-ak0.pinimg.com/736x/d7/39/fa/d739fa2fe6ac409f50738767f83d1192.jpg",
23 | "http://media-cache-ec0.pinimg.com/736x/37/28/85/37288502b44b06902708ceb6927cde42.jpg",
24 | "http://media-cache-ak0.pinimg.com/736x/dd/d7/81/ddd7813ce56b431083e7ca006a759ad1.jpg"
25 | };
26 | public int width[] = {
27 | 468,
28 | 664,
29 | 736,
30 | 450,
31 | 736,
32 | 498,
33 | 468,
34 | 736,
35 | 666,
36 | 710,
37 | 640,
38 | 398,
39 | 610,
40 | 468,
41 | 486,
42 | 497,
43 | 600,
44 | 600,
45 | 420,
46 | 323
47 | };
48 | public int height[] = {
49 | 735,
50 | 1000,
51 | 551,
52 | 619,
53 | 553,
54 | 750,
55 | 624,
56 | 1045,
57 | 1000,
58 | 475,
59 | 427,
60 | 900,
61 | 800,
62 | 334,
63 | 658,
64 | 750,
65 | 800,
66 | 450,
67 | 630,
68 | 400
69 | };
70 | }
71 |
--------------------------------------------------------------------------------
/src/com/bulletnoid/android/widget/StaggeredGridViewDemo/Item.java:
--------------------------------------------------------------------------------
1 | package com.bulletnoid.android.widget.StaggeredGridViewDemo;
2 |
3 | public class Item {
4 | public String url;
5 | public int width;
6 | public int height;
7 | }
8 |
--------------------------------------------------------------------------------
/src/com/bulletnoid/android/widget/StaggeredGridViewDemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.bulletnoid.android.widget.StaggeredGridViewDemo;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.Button;
8 |
9 | public class MainActivity extends Activity {
10 | Button btn_stgv;
11 | Button btn_stgv_ptr;
12 |
13 | public void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.ac_main);
16 |
17 | btn_stgv = (Button) findViewById(R.id.btn_stgv);
18 | btn_stgv_ptr = (Button) findViewById(R.id.btn_stgv_ptr);
19 |
20 | btn_stgv.setOnClickListener(new View.OnClickListener() {
21 | @Override
22 | public void onClick(View v) {
23 | Intent i = new Intent(MainActivity.this, STGVActivity.class);
24 | startActivity(i);
25 | }
26 | });
27 |
28 | btn_stgv_ptr.setOnClickListener(new View.OnClickListener() {
29 | @Override
30 | public void onClick(View v) {
31 | Intent i = new Intent(MainActivity.this, STGVWithPTRActivity.class);
32 | startActivity(i);
33 | }
34 | });
35 | }
36 | }
--------------------------------------------------------------------------------
/src/com/bulletnoid/android/widget/StaggeredGridViewDemo/STGVActivity.java:
--------------------------------------------------------------------------------
1 | package com.bulletnoid.android.widget.StaggeredGridViewDemo;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.os.AsyncTask;
6 | import android.os.Bundle;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.widget.Button;
10 | import com.bulletnoid.android.widget.StaggeredGridView.StaggeredGridView;
11 |
12 | public class STGVActivity extends Activity {
13 | StaggeredGridView stgv;
14 | STGVAdapter mAdapter;
15 |
16 | public void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.ac_stgv);
19 |
20 | stgv = (StaggeredGridView) findViewById(R.id.stgv);
21 |
22 | int margin = getResources().getDimensionPixelSize(R.dimen.stgv_margin);
23 |
24 | stgv.setItemMargin(margin);
25 | stgv.setPadding(margin, 0, margin, 0);
26 |
27 | stgv.setHeaderView(new Button(this));
28 | View footerView;
29 | LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
30 | footerView = inflater.inflate(R.layout.layout_loading_footer, null);
31 | stgv.setFooterView(footerView);
32 |
33 | mAdapter = new STGVAdapter(this, getApplication());
34 | stgv.setAdapter(mAdapter);
35 | mAdapter.notifyDataSetChanged();
36 |
37 | stgv.setOnLoadmoreListener(new StaggeredGridView.OnLoadmoreListener() {
38 | @Override
39 | public void onLoadmore() {
40 | new LoadMoreTask().execute();
41 | }
42 | });
43 | }
44 |
45 | public class LoadMoreTask extends AsyncTask {
46 |
47 | @Override
48 | protected void onPreExecute() {
49 | super.onPreExecute();
50 | }
51 |
52 | @Override
53 | protected Void doInBackground(Void... params) {
54 | try {
55 | Thread.sleep(1000);
56 | } catch (InterruptedException e) {
57 | }
58 | return null;
59 | }
60 |
61 | @Override
62 | protected void onPostExecute(Void result) {
63 | mAdapter.getMoreItem();
64 | mAdapter.notifyDataSetChanged();
65 | super.onPostExecute(result);
66 | }
67 |
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/src/com/bulletnoid/android/widget/StaggeredGridViewDemo/STGVAdapter.java:
--------------------------------------------------------------------------------
1 | package com.bulletnoid.android.widget.StaggeredGridViewDemo;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 | import com.squareup.picasso.Picasso;
11 |
12 | import java.util.ArrayList;
13 |
14 | public class STGVAdapter extends BaseAdapter {
15 | private Context mContext;
16 | private Application mAppContext;
17 | private DataSet mData = new DataSet();
18 | private ArrayList mItems = new ArrayList();
19 |
20 | private int newPos = 19;
21 |
22 | public STGVAdapter(Context context, Application app) {
23 | mContext = context;
24 | mAppContext = app;
25 | getMoreItem();
26 | }
27 |
28 | public void getMoreItem() {
29 | for (int i = 0; i < 20; i++) {
30 | Item item = new Item();
31 | item.url = mData.url[i];
32 | item.width = mData.width[i];
33 | item.height = mData.height[i];
34 | mItems.add(item);
35 | }
36 | }
37 |
38 | public void getNewItem() {
39 | Item item = new Item();
40 | item.url = mData.url[newPos];
41 | item.width = mData.width[newPos];
42 | item.height = mData.height[newPos];
43 | mItems.add(0, item);
44 | newPos = (newPos - 1) % 19;
45 | }
46 |
47 | @Override
48 | public int getCount() {
49 | return mItems == null ? 0 : mItems.size();
50 | }
51 |
52 | @Override
53 | public Object getItem(int position) {
54 | return null;
55 | }
56 |
57 | @Override
58 | public long getItemId(int position) {
59 | return 0;
60 | }
61 |
62 | @Override
63 | public View getView(int position, View convertView, ViewGroup parent) {
64 | View view = null;
65 | final Item item = mItems.get(position);
66 |
67 | String url = item.url;
68 |
69 | if (convertView == null) {
70 | Holder holder = new Holder();
71 | view = View.inflate(mContext, R.layout.cell_stgv, null);
72 | holder.img_content = (STGVImageView) view.findViewById(R.id.img_content);
73 | holder.tv_info = (TextView) view.findViewById(R.id.tv_info);
74 |
75 | view.setTag(holder);
76 | } else {
77 | view = convertView;
78 | }
79 |
80 | Holder holder = (Holder) view.getTag();
81 |
82 | /**
83 | * StaggeredGridView has bugs dealing with child TouchEvent
84 | * You must deal TouchEvent in the child view itself
85 | **/
86 | holder.img_content.setOnClickListener(new View.OnClickListener() {
87 | @Override
88 | public void onClick(View v) {
89 | }
90 | });
91 |
92 | holder.tv_info.setOnClickListener(new View.OnClickListener() {
93 | @Override
94 | public void onClick(View v) {
95 | }
96 | });
97 |
98 | holder.tv_info.setText(position + " : " + item.width + "/" + item.height);
99 |
100 | holder.img_content.mHeight = item.height;
101 | holder.img_content.mWidth = item.width;
102 |
103 | Picasso.with(mAppContext).load(url).into(holder.img_content);
104 | return view;
105 | }
106 |
107 | class Holder {
108 | STGVImageView img_content;
109 | TextView tv_info;
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/src/com/bulletnoid/android/widget/StaggeredGridViewDemo/STGVImageView.java:
--------------------------------------------------------------------------------
1 | package com.bulletnoid.android.widget.StaggeredGridViewDemo;
2 |
3 | import android.content.Context;
4 | import android.graphics.ColorMatrixColorFilter;
5 | import android.util.AttributeSet;
6 | import android.view.MotionEvent;
7 | import android.widget.ImageView;
8 |
9 | public class STGVImageView extends ImageView {
10 |
11 | public int mWidth = 0;
12 | public int mHeight = 0;
13 |
14 | private static final float Trans = -25f;
15 |
16 | private final static float[] BT_SELECTED = new float[]{
17 | 1, 0, 0, 0, Trans,
18 | 0, 1, 0, 0, Trans,
19 | 0, 0, 1, 0, Trans,
20 | 0, 0, 0, 1, 0};
21 |
22 | private final static float[] BT_NOT_SELECTED = new float[]{
23 | 1, 0, 0, 0, 0,
24 | 0, 1, 0, 0, 0,
25 | 0, 0, 1, 0, 0,
26 | 0, 0, 0, 1, 0};
27 |
28 | private ColorMatrixColorFilter mPressFilter;
29 | private ColorMatrixColorFilter mNormalFilter;
30 |
31 | public STGVImageView(Context context) {
32 | super(context);
33 | }
34 |
35 | public STGVImageView(Context context, AttributeSet attrs) {
36 | super(context, attrs);
37 | }
38 |
39 | public STGVImageView(Context context, AttributeSet attrs, int defStyle) {
40 | super(context, attrs, defStyle);
41 | }
42 |
43 | @Override
44 | public boolean onTouchEvent(MotionEvent event) {
45 | switch (event.getAction()) {
46 | case MotionEvent.ACTION_DOWN:
47 | if (getDrawable() != null) {
48 | if (mPressFilter == null) {
49 | mPressFilter = new ColorMatrixColorFilter(BT_SELECTED);
50 | }
51 | getDrawable().setColorFilter(mPressFilter);
52 | }
53 | break;
54 | case MotionEvent.ACTION_CANCEL:
55 | case MotionEvent.ACTION_UP:
56 | if (getDrawable() != null) {
57 | if (mNormalFilter == null) {
58 | mNormalFilter = new ColorMatrixColorFilter(BT_NOT_SELECTED);
59 | }
60 | getDrawable().setColorFilter(mNormalFilter);
61 | }
62 | break;
63 | default:
64 | break;
65 | }
66 | return super.onTouchEvent(event);
67 | }
68 |
69 | @Override
70 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
71 |
72 | // int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);
73 | // int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);
74 | int width = MeasureSpec.getSize(widthMeasureSpec);
75 | int height = MeasureSpec.getSize(heightMeasureSpec);
76 |
77 | int heightC = width * mHeight / mWidth;
78 |
79 | setMeasuredDimension(width, heightC);
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/src/com/bulletnoid/android/widget/StaggeredGridViewDemo/STGVWithPTRActivity.java:
--------------------------------------------------------------------------------
1 | package com.bulletnoid.android.widget.StaggeredGridViewDemo;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.os.AsyncTask;
6 | import android.os.Bundle;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.widget.Button;
10 | import com.bulletnoid.android.widget.StaggeredGridView.StaggeredGridView;
11 | import com.handmark.pulltorefresh.library.PullToRefreshBase;
12 | import com.handmark.pulltorefresh.library.PullToRefreshStaggeredGridView;
13 |
14 | public class STGVWithPTRActivity extends Activity {
15 | PullToRefreshStaggeredGridView ptrstgv;
16 | STGVAdapter mAdapter;
17 |
18 | public void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.ac_stgv_with_ptr);
21 |
22 | ptrstgv = (PullToRefreshStaggeredGridView) findViewById(R.id.ptrstgv);
23 |
24 | mAdapter = new STGVAdapter(this, getApplication());
25 |
26 | ptrstgv.setMode(PullToRefreshBase.Mode.PULL_FROM_START);
27 | ptrstgv.getRefreshableView().setHeaderView(new Button(this));
28 | View footerView;
29 | LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
30 | footerView = inflater.inflate(R.layout.layout_loading_footer, null);
31 | ptrstgv.getRefreshableView().setFooterView(footerView);
32 | ptrstgv.setAdapter(mAdapter);
33 | mAdapter.notifyDataSetChanged();
34 |
35 | ptrstgv.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener() {
36 | @Override
37 | public void onRefresh(PullToRefreshBase refreshView) {
38 | mAdapter.getNewItem();
39 | mAdapter.notifyDataSetChanged();
40 |
41 | ptrstgv.onRefreshComplete();
42 | }
43 | });
44 |
45 | ptrstgv.setOnLoadmoreListener(new StaggeredGridView.OnLoadmoreListener() {
46 | @Override
47 | public void onLoadmore() {
48 | new LoadMoreTask().execute();
49 | }
50 | });
51 |
52 | }
53 |
54 | public class LoadMoreTask extends AsyncTask {
55 |
56 | @Override
57 | protected void onPreExecute() {
58 | super.onPreExecute();
59 | }
60 |
61 | @Override
62 | protected Void doInBackground(Void... params) {
63 | try {
64 | Thread.sleep(1000);
65 | } catch (InterruptedException e) {
66 | }
67 | return null;
68 | }
69 |
70 | @Override
71 | protected void onPostExecute(Void result) {
72 | mAdapter.getMoreItem();
73 | mAdapter.notifyDataSetChanged();
74 | super.onPostExecute(result);
75 | }
76 |
77 | }
78 | }
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/ILoadingLayout.java:
--------------------------------------------------------------------------------
1 | package com.handmark.pulltorefresh.library;
2 |
3 | import android.graphics.Typeface;
4 | import android.graphics.drawable.Drawable;
5 |
6 | public interface ILoadingLayout {
7 |
8 | /**
9 | * Set the Last Updated Text. This displayed under the ac_main label when
10 | * Pulling
11 | *
12 | * @param label - Label to set
13 | */
14 | public void setLastUpdatedLabel(CharSequence label);
15 |
16 | /**
17 | * Set the drawable used in the loading layout. This is the same as calling
18 | * setLoadingDrawable(drawable, Mode.BOTH)
19 | *
20 | * @param drawable - Drawable to display
21 | */
22 | public void setLoadingDrawable(Drawable drawable);
23 |
24 | /**
25 | * Set Text to show when the Widget is being Pulled
26 | * setPullLabel(releaseLabel, Mode.BOTH)
27 | *
28 | * @param pullLabel - CharSequence to display
29 | */
30 | public void setPullLabel(CharSequence pullLabel);
31 |
32 | /**
33 | * Set Text to show when the Widget is refreshing
34 | * setRefreshingLabel(releaseLabel, Mode.BOTH)
35 | *
36 | * @param refreshingLabel - CharSequence to display
37 | */
38 | public void setRefreshingLabel(CharSequence refreshingLabel);
39 |
40 | /**
41 | * Set Text to show when the Widget is being pulled, and will refresh when
42 | * released. This is the same as calling
43 | * setReleaseLabel(releaseLabel, Mode.BOTH)
44 | *
45 | * @param releaseLabel - CharSequence to display
46 | */
47 | public void setReleaseLabel(CharSequence releaseLabel);
48 |
49 | /**
50 | * Set's the Sets the typeface and style in which the text should be
51 | * displayed. Please see
52 | * {@link android.widget.TextView#setTypeface(android.graphics.Typeface)
53 | * TextView#setTypeface(Typeface)}.
54 | */
55 | public void setTextTypeface(Typeface tf);
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/IPullToRefresh.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library;
17 |
18 | import android.view.View;
19 | import android.view.animation.Interpolator;
20 | import com.handmark.pulltorefresh.library.PullToRefreshBase.*;
21 |
22 | public interface IPullToRefresh {
23 |
24 | /**
25 | * Demos the Pull-to-Refresh functionality to the user so that they are
26 | * aware it is there. This could be useful when the user first opens your
27 | * app, etc. The animation will only happen if the Refresh View (ListView,
28 | * ScrollView, etc) is in a state where a Pull-to-Refresh could occur by a
29 | * user's touch gesture (i.e. scrolled to the top/bottom).
30 | *
31 | * @return true - if the Demo has been started, false if not.
32 | */
33 | public boolean demo();
34 |
35 | /**
36 | * Get the mode that this view is currently in. This is only really useful
37 | * when using Mode.BOTH.
38 | *
39 | * @return Mode that the view is currently in
40 | */
41 | public Mode getCurrentMode();
42 |
43 | /**
44 | * Returns whether the Touch Events are filtered or not. If true is
45 | * returned, then the View will only use touch events where the difference
46 | * in the Y-axis is greater than the difference in the X-axis. This means
47 | * that the View will not interfere when it is used in a horizontal
48 | * scrolling View (such as a ViewPager).
49 | *
50 | * @return boolean - true if the View is filtering Touch Events
51 | */
52 | public boolean getFilterTouchEvents();
53 |
54 | /**
55 | * Returns a proxy object which allows you to call methods on all of the
56 | * LoadingLayouts (the Views which show when Pulling/Refreshing).
57 | *
58 | * You should not keep the result of this method any longer than you need
59 | * it.
60 | *
61 | * @return Object which will proxy any calls you make on it, to all of the
62 | * LoadingLayouts.
63 | */
64 | public ILoadingLayout getLoadingLayoutProxy();
65 |
66 | /**
67 | * Returns a proxy object which allows you to call methods on the
68 | * LoadingLayouts (the Views which show when Pulling/Refreshing). The actual
69 | * LoadingLayout(s) which will be affected, are chosen by the parameters you
70 | * give.
71 | *
72 | * You should not keep the result of this method any longer than you need
73 | * it.
74 | *
75 | * @param includeStart - Whether to include the Start/Header Views
76 | * @param includeEnd - Whether to include the End/Footer Views
77 | * @return Object which will proxy any calls you make on it, to the
78 | * LoadingLayouts included.
79 | */
80 | public ILoadingLayout getLoadingLayoutProxy(boolean includeStart, boolean includeEnd);
81 |
82 | /**
83 | * Get the mode that this view has been set to. If this returns
84 | * Mode.BOTH, you can use getCurrentMode() to
85 | * check which mode the view is currently in
86 | *
87 | * @return Mode that the view has been set to
88 | */
89 | public Mode getMode();
90 |
91 | /**
92 | * Get the Wrapped Refreshable View. Anything returned here has already been
93 | * added to the content view.
94 | *
95 | * @return The View which is currently wrapped
96 | */
97 | public T getRefreshableView();
98 |
99 | /**
100 | * Get whether the 'Refreshing' View should be automatically shown when
101 | * refreshing. Returns true by default.
102 | *
103 | * @return - true if the Refreshing View will be show
104 | */
105 | public boolean getShowViewWhileRefreshing();
106 |
107 | /**
108 | * @return - The state that the View is currently in.
109 | */
110 | public State getState();
111 |
112 | /**
113 | * Whether Pull-to-Refresh is enabled
114 | *
115 | * @return enabled
116 | */
117 | public boolean isPullToRefreshEnabled();
118 |
119 | /**
120 | * Gets whether Overscroll support is enabled. This is different to
121 | * Android's standard Overscroll support (the edge-glow) which is available
122 | * from GINGERBREAD onwards
123 | *
124 | * @return true - if both PullToRefresh-OverScroll and Android's inbuilt
125 | * OverScroll are enabled
126 | */
127 | public boolean isPullToRefreshOverScrollEnabled();
128 |
129 | /**
130 | * Returns whether the Widget is currently in the Refreshing mState
131 | *
132 | * @return true if the Widget is currently refreshing
133 | */
134 | public boolean isRefreshing();
135 |
136 | /**
137 | * Returns whether the widget has enabled scrolling on the Refreshable View
138 | * while refreshing.
139 | *
140 | * @return true if the widget has enabled scrolling while refreshing
141 | */
142 | public boolean isScrollingWhileRefreshingEnabled();
143 |
144 | /**
145 | * Mark the current Refresh as complete. Will Reset the UI and hide the
146 | * Refreshing View
147 | */
148 | public void onRefreshComplete();
149 |
150 | /**
151 | * Set the Touch Events to be filtered or not. If set to true, then the View
152 | * will only use touch events where the difference in the Y-axis is greater
153 | * than the difference in the X-axis. This means that the View will not
154 | * interfere when it is used in a horizontal scrolling View (such as a
155 | * ViewPager), but will restrict which types of finger scrolls will trigger
156 | * the View.
157 | *
158 | * @param filterEvents - true if you want to filter Touch Events. Default is
159 | * true.
160 | */
161 | public void setFilterTouchEvents(boolean filterEvents);
162 |
163 | /**
164 | * Set the mode of Pull-to-Refresh that this view will use.
165 | *
166 | * @param mode - Mode to set the View to
167 | */
168 | public void setMode(Mode mode);
169 |
170 | /**
171 | * Set OnPullEventListener for the Widget
172 | *
173 | * @param listener - Listener to be used when the Widget has a pull event to
174 | * propogate.
175 | */
176 | public void setOnPullEventListener(OnPullEventListener listener);
177 |
178 | /**
179 | * Set OnRefreshListener for the Widget
180 | *
181 | * @param listener - Listener to be used when the Widget is set to Refresh
182 | */
183 | public void setOnRefreshListener(OnRefreshListener listener);
184 |
185 | /**
186 | * Set OnRefreshListener for the Widget
187 | *
188 | * @param listener - Listener to be used when the Widget is set to Refresh
189 | */
190 | public void setOnRefreshListener(OnRefreshListener2 listener);
191 |
192 | /**
193 | * Sets whether Overscroll support is enabled. This is different to
194 | * Android's standard Overscroll support (the edge-glow). This setting only
195 | * takes effect when running on device with Android v2.3 or greater.
196 | *
197 | * @param enabled - true if you want Overscroll enabled
198 | */
199 | public void setPullToRefreshOverScrollEnabled(boolean enabled);
200 |
201 | /**
202 | * Sets the Widget to be in the refresh state. The UI will be updated to
203 | * show the 'Refreshing' view, and be scrolled to show such.
204 | */
205 | public void setRefreshing();
206 |
207 | /**
208 | * Sets the Widget to be in the refresh state. The UI will be updated to
209 | * show the 'Refreshing' view.
210 | *
211 | * @param doScroll - true if you want to force a scroll to the Refreshing
212 | * view.
213 | */
214 | public void setRefreshing(boolean doScroll);
215 |
216 | /**
217 | * Sets the Animation Interpolator that is used for animated scrolling.
218 | * Defaults to a DecelerateInterpolator
219 | *
220 | * @param interpolator - Interpolator to use
221 | */
222 | public void setScrollAnimationInterpolator(Interpolator interpolator);
223 |
224 | /**
225 | * By default the Widget disables scrolling on the Refreshable View while
226 | * refreshing. This method can change this behaviour.
227 | *
228 | * @param scrollingWhileRefreshingEnabled - true if you want to enable
229 | * scrolling while refreshing
230 | */
231 | public void setScrollingWhileRefreshingEnabled(boolean scrollingWhileRefreshingEnabled);
232 |
233 | /**
234 | * A mutator to enable/disable whether the 'Refreshing' View should be
235 | * automatically shown when refreshing.
236 | *
237 | * @param showView
238 | */
239 | public void setShowViewWhileRefreshing(boolean showView);
240 |
241 | }
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/LoadingLayoutProxy.java:
--------------------------------------------------------------------------------
1 | package com.handmark.pulltorefresh.library;
2 |
3 | import android.graphics.Typeface;
4 | import android.graphics.drawable.Drawable;
5 | import com.handmark.pulltorefresh.library.internal.LoadingLayout;
6 |
7 | import java.util.HashSet;
8 |
9 | public class LoadingLayoutProxy implements ILoadingLayout {
10 |
11 | private final HashSet mLoadingLayouts;
12 |
13 | LoadingLayoutProxy() {
14 | mLoadingLayouts = new HashSet();
15 | }
16 |
17 | /**
18 | * This allows you to add extra LoadingLayout instances to this proxy. This
19 | * is only necessary if you keep your own instances, and want to have them
20 | * included in any
21 | * {@link PullToRefreshBase#createLoadingLayoutProxy(boolean, boolean)
22 | * createLoadingLayoutProxy(...)} calls.
23 | *
24 | * @param layout - LoadingLayout to have included.
25 | */
26 | public void addLayout(LoadingLayout layout) {
27 | if (null != layout) {
28 | mLoadingLayouts.add(layout);
29 | }
30 | }
31 |
32 | @Override
33 | public void setLastUpdatedLabel(CharSequence label) {
34 | for (LoadingLayout layout : mLoadingLayouts) {
35 | layout.setLastUpdatedLabel(label);
36 | }
37 | }
38 |
39 | @Override
40 | public void setLoadingDrawable(Drawable drawable) {
41 | for (LoadingLayout layout : mLoadingLayouts) {
42 | layout.setLoadingDrawable(drawable);
43 | }
44 | }
45 |
46 | @Override
47 | public void setRefreshingLabel(CharSequence refreshingLabel) {
48 | for (LoadingLayout layout : mLoadingLayouts) {
49 | layout.setRefreshingLabel(refreshingLabel);
50 | }
51 | }
52 |
53 | @Override
54 | public void setPullLabel(CharSequence label) {
55 | for (LoadingLayout layout : mLoadingLayouts) {
56 | layout.setPullLabel(label);
57 | }
58 | }
59 |
60 | @Override
61 | public void setReleaseLabel(CharSequence label) {
62 | for (LoadingLayout layout : mLoadingLayouts) {
63 | layout.setReleaseLabel(label);
64 | }
65 | }
66 |
67 | public void setTextTypeface(Typeface tf) {
68 | for (LoadingLayout layout : mLoadingLayouts) {
69 | layout.setTextTypeface(tf);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/OverscrollHelper.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library;
17 |
18 | import android.annotation.TargetApi;
19 | import android.util.Log;
20 | import android.view.View;
21 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode;
22 | import com.handmark.pulltorefresh.library.PullToRefreshBase.State;
23 |
24 | @TargetApi(9)
25 | public final class OverscrollHelper {
26 |
27 | static final String LOG_TAG = "OverscrollHelper";
28 | static final float DEFAULT_OVERSCROLL_SCALE = 1f;
29 |
30 | /**
31 | * Helper method for Overscrolling that encapsulates all of the necessary
32 | * function.
33 | *
34 | * This should only be used on AdapterView's such as ListView as it just
35 | * calls through to overScrollBy() with the scrollRange = 0. AdapterView's
36 | * do not have a scroll range (i.e. getScrollY() doesn't work).
37 | *
38 | * @param view - PullToRefreshView that is calling this.
39 | * @param deltaX - Change in X in pixels, passed through from from
40 | * overScrollBy call
41 | * @param scrollX - Current X scroll value in pixels before applying deltaY,
42 | * passed through from from overScrollBy call
43 | * @param deltaY - Change in Y in pixels, passed through from from
44 | * overScrollBy call
45 | * @param scrollY - Current Y scroll value in pixels before applying deltaY,
46 | * passed through from from overScrollBy call
47 | * @param isTouchEvent - true if this scroll operation is the result of a
48 | * touch event, passed through from from overScrollBy call
49 | */
50 | public static void overScrollBy(final PullToRefreshBase> view, final int deltaX, final int scrollX,
51 | final int deltaY, final int scrollY, final boolean isTouchEvent) {
52 | overScrollBy(view, deltaX, scrollX, deltaY, scrollY, 0, isTouchEvent);
53 | }
54 |
55 | /**
56 | * Helper method for Overscrolling that encapsulates all of the necessary
57 | * function. This version of the call is used for Views that need to specify
58 | * a Scroll Range but scroll back to it's edge correctly.
59 | *
60 | * @param view - PullToRefreshView that is calling this.
61 | * @param deltaX - Change in X in pixels, passed through from from
62 | * overScrollBy call
63 | * @param scrollX - Current X scroll value in pixels before applying deltaY,
64 | * passed through from from overScrollBy call
65 | * @param deltaY - Change in Y in pixels, passed through from from
66 | * overScrollBy call
67 | * @param scrollY - Current Y scroll value in pixels before applying deltaY,
68 | * passed through from from overScrollBy call
69 | * @param scrollRange - Scroll Range of the View, specifically needed for
70 | * ScrollView
71 | * @param isTouchEvent - true if this scroll operation is the result of a
72 | * touch event, passed through from from overScrollBy call
73 | */
74 | public static void overScrollBy(final PullToRefreshBase> view, final int deltaX, final int scrollX,
75 | final int deltaY, final int scrollY, final int scrollRange, final boolean isTouchEvent) {
76 | overScrollBy(view, deltaX, scrollX, deltaY, scrollY, scrollRange, 0, DEFAULT_OVERSCROLL_SCALE, isTouchEvent);
77 | }
78 |
79 | /**
80 | * Helper method for Overscrolling that encapsulates all of the necessary
81 | * function. This is the advanced version of the call.
82 | *
83 | * @param view - PullToRefreshView that is calling this.
84 | * @param deltaX - Change in X in pixels, passed through from from
85 | * overScrollBy call
86 | * @param scrollX - Current X scroll value in pixels before applying deltaY,
87 | * passed through from from overScrollBy call
88 | * @param deltaY - Change in Y in pixels, passed through from from
89 | * overScrollBy call
90 | * @param scrollY - Current Y scroll value in pixels before applying deltaY,
91 | * passed through from from overScrollBy call
92 | * @param scrollRange - Scroll Range of the View, specifically needed for
93 | * ScrollView
94 | * @param fuzzyThreshold - Threshold for which the values how fuzzy we
95 | * should treat the other values. Needed for WebView as it
96 | * doesn't always scroll back to it's edge. 0 = no fuzziness.
97 | * @param scaleFactor - Scale Factor for overscroll amount
98 | * @param isTouchEvent - true if this scroll operation is the result of a
99 | * touch event, passed through from from overScrollBy call
100 | */
101 | public static void overScrollBy(final PullToRefreshBase> view, final int deltaX, final int scrollX,
102 | final int deltaY, final int scrollY, final int scrollRange, final int fuzzyThreshold,
103 | final float scaleFactor, final boolean isTouchEvent) {
104 |
105 | final int deltaValue, currentScrollValue, scrollValue;
106 | switch (view.getPullToRefreshScrollDirection()) {
107 | case HORIZONTAL:
108 | deltaValue = deltaX;
109 | scrollValue = scrollX;
110 | currentScrollValue = view.getScrollX();
111 | break;
112 | case VERTICAL:
113 | default:
114 | deltaValue = deltaY;
115 | scrollValue = scrollY;
116 | currentScrollValue = view.getScrollY();
117 | break;
118 | }
119 |
120 | // Check that OverScroll is enabled and that we're not currently
121 | // refreshing.
122 | if (view.isPullToRefreshOverScrollEnabled() && !view.isRefreshing()) {
123 | final Mode mode = view.getMode();
124 |
125 | // Check that Pull-to-Refresh is enabled, and the event isn't from
126 | // touch
127 | if (mode.permitsPullToRefresh() && !isTouchEvent && deltaValue != 0) {
128 | final int newScrollValue = (deltaValue + scrollValue);
129 |
130 | if (PullToRefreshBase.DEBUG) {
131 | Log.d(LOG_TAG, "OverScroll. DeltaX: " + deltaX + ", ScrollX: " + scrollX + ", DeltaY: " + deltaY
132 | + ", ScrollY: " + scrollY + ", NewY: " + newScrollValue + ", ScrollRange: " + scrollRange
133 | + ", CurrentScroll: " + currentScrollValue);
134 | }
135 |
136 | if (newScrollValue < (0 - fuzzyThreshold)) {
137 | // Check the mode supports the overscroll direction, and
138 | // then move scroll
139 | if (mode.showHeaderLoadingLayout()) {
140 | // If we're currently at zero, we're about to start
141 | // overscrolling, so change the state
142 | if (currentScrollValue == 0) {
143 | view.setState(State.OVERSCROLLING);
144 | }
145 |
146 | view.setHeaderScroll((int) (scaleFactor * (currentScrollValue + newScrollValue)));
147 | }
148 | } else if (newScrollValue > (scrollRange + fuzzyThreshold)) {
149 | // Check the mode supports the overscroll direction, and
150 | // then move scroll
151 | if (mode.showFooterLoadingLayout()) {
152 | // If we're currently at zero, we're about to start
153 | // overscrolling, so change the state
154 | if (currentScrollValue == 0) {
155 | view.setState(State.OVERSCROLLING);
156 | }
157 |
158 | view.setHeaderScroll((int) (scaleFactor * (currentScrollValue + newScrollValue - scrollRange)));
159 | }
160 | } else if (Math.abs(newScrollValue) <= fuzzyThreshold
161 | || Math.abs(newScrollValue - scrollRange) <= fuzzyThreshold) {
162 | // Means we've stopped overscrolling, so scroll back to 0
163 | view.setState(State.RESET);
164 | }
165 | } else if (isTouchEvent && State.OVERSCROLLING == view.getState()) {
166 | // This condition means that we were overscrolling from a fling,
167 | // but the user has touched the View and is now overscrolling
168 | // from touch instead. We need to just reset.
169 | view.setState(State.RESET);
170 | }
171 | }
172 | }
173 |
174 | static boolean isAndroidOverScrollEnabled(View view) {
175 | return view.getOverScrollMode() != View.OVER_SCROLL_NEVER;
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/PullToRefreshAdapterViewBase.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library;
17 |
18 | import android.content.Context;
19 | import android.content.res.TypedArray;
20 | import android.util.AttributeSet;
21 | import android.util.Log;
22 | import android.view.Gravity;
23 | import android.view.View;
24 | import android.view.ViewGroup;
25 | import android.view.ViewParent;
26 | import android.widget.*;
27 | import android.widget.AbsListView.OnScrollListener;
28 | import android.widget.AdapterView.OnItemClickListener;
29 | import com.bulletnoid.android.widget.StaggeredGridViewDemo.R;
30 | import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor;
31 | import com.handmark.pulltorefresh.library.internal.IndicatorLayout;
32 |
33 | public abstract class PullToRefreshAdapterViewBase extends PullToRefreshBase implements
34 | OnScrollListener {
35 |
36 | private static FrameLayout.LayoutParams convertEmptyViewLayoutParams(ViewGroup.LayoutParams lp) {
37 | FrameLayout.LayoutParams newLp = null;
38 |
39 | if (null != lp) {
40 | newLp = new FrameLayout.LayoutParams(lp);
41 |
42 | if (lp instanceof LayoutParams) {
43 | newLp.gravity = ((LayoutParams) lp).gravity;
44 | } else {
45 | newLp.gravity = Gravity.CENTER;
46 | }
47 | }
48 |
49 | return newLp;
50 | }
51 |
52 | private boolean mLastItemVisible;
53 | private OnScrollListener mOnScrollListener;
54 | private OnLastItemVisibleListener mOnLastItemVisibleListener;
55 | private View mEmptyView;
56 |
57 | private IndicatorLayout mIndicatorIvTop;
58 | private IndicatorLayout mIndicatorIvBottom;
59 |
60 | private boolean mShowIndicator;
61 | private boolean mScrollEmptyView = true;
62 |
63 | public PullToRefreshAdapterViewBase(Context context) {
64 | super(context);
65 | mRefreshableView.setOnScrollListener(this);
66 | }
67 |
68 | public PullToRefreshAdapterViewBase(Context context, AttributeSet attrs) {
69 | super(context, attrs);
70 | mRefreshableView.setOnScrollListener(this);
71 | }
72 |
73 | public PullToRefreshAdapterViewBase(Context context, Mode mode) {
74 | super(context, mode);
75 | mRefreshableView.setOnScrollListener(this);
76 | }
77 |
78 | public PullToRefreshAdapterViewBase(Context context, Mode mode, AnimationStyle animStyle) {
79 | super(context, mode, animStyle);
80 | mRefreshableView.setOnScrollListener(this);
81 | }
82 |
83 | /**
84 | * Gets whether an indicator graphic should be displayed when the View is in
85 | * a state where a Pull-to-Refresh can happen. An example of this state is
86 | * when the Adapter View is scrolled to the top and the mode is set to
87 | * {@link PullToRefreshBase.Mode#PULL_FROM_START}. The default value is true if
88 | * {@link PullToRefreshBase#isPullToRefreshOverScrollEnabled()
89 | * isPullToRefreshOverScrollEnabled()} returns false.
90 | *
91 | * @return true if the indicators will be shown
92 | */
93 | public boolean getShowIndicator() {
94 | return mShowIndicator;
95 | }
96 |
97 | public final void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
98 | final int totalItemCount) {
99 |
100 | if (DEBUG) {
101 | Log.d(LOG_TAG, "First Visible: " + firstVisibleItem + ". Visible Count: " + visibleItemCount
102 | + ". Total Items:" + totalItemCount);
103 | }
104 |
105 | /**
106 | * Set whether the Last Item is Visible. lastVisibleItemIndex is a
107 | * zero-based index, so we minus one totalItemCount to check
108 | */
109 | if (null != mOnLastItemVisibleListener) {
110 | mLastItemVisible = (totalItemCount > 0) && (firstVisibleItem + visibleItemCount >= totalItemCount - 1);
111 | }
112 |
113 | // If we're showing the indicator, check positions...
114 | if (getShowIndicatorInternal()) {
115 | updateIndicatorViewsVisibility();
116 | }
117 |
118 | // Finally call OnScrollListener if we have one
119 | if (null != mOnScrollListener) {
120 | mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
121 | }
122 | }
123 |
124 | public final void onScrollStateChanged(final AbsListView view, final int state) {
125 | /**
126 | * Check that the scrolling has stopped, and that the last item is
127 | * visible.
128 | */
129 | if (state == OnScrollListener.SCROLL_STATE_IDLE && null != mOnLastItemVisibleListener && mLastItemVisible) {
130 | mOnLastItemVisibleListener.onLastItemVisible();
131 | }
132 |
133 | if (null != mOnScrollListener) {
134 | mOnScrollListener.onScrollStateChanged(view, state);
135 | }
136 | }
137 |
138 | /**
139 | * Pass-through method for {@link PullToRefreshBase#getRefreshableView()
140 | * getRefreshableView()}.
141 | * {@link android.widget.AdapterView#setAdapter(android.widget.Adapter)}
142 | * setAdapter(adapter)}. This is just for convenience!
143 | *
144 | * @param adapter - Adapter to set
145 | */
146 | public void setAdapter(ListAdapter adapter) {
147 | ((AdapterView) mRefreshableView).setAdapter(adapter);
148 | }
149 |
150 | /**
151 | * Sets the Empty View to be used by the Adapter View.
152 | *
153 | * We need it handle it ourselves so that we can Pull-to-Refresh when the
154 | * Empty View is shown.
155 | *
156 | * Please note, you do not usually need to call this method
157 | * yourself. Calling setEmptyView on the AdapterView will automatically call
158 | * this method and set everything up. This includes when the Android
159 | * Framework automatically sets the Empty View based on it's ID.
160 | *
161 | * @param newEmptyView - Empty View to be used
162 | */
163 | public final void setEmptyView(View newEmptyView) {
164 | FrameLayout refreshableViewWrapper = getRefreshableViewWrapper();
165 |
166 | if (null != newEmptyView) {
167 | // New view needs to be clickable so that Android recognizes it as a
168 | // target for Touch Events
169 | newEmptyView.setClickable(true);
170 |
171 | ViewParent newEmptyViewParent = newEmptyView.getParent();
172 | if (null != newEmptyViewParent && newEmptyViewParent instanceof ViewGroup) {
173 | ((ViewGroup) newEmptyViewParent).removeView(newEmptyView);
174 | }
175 |
176 | // We need to convert any LayoutParams so that it works in our
177 | // FrameLayout
178 | FrameLayout.LayoutParams lp = convertEmptyViewLayoutParams(newEmptyView.getLayoutParams());
179 | if (null != lp) {
180 | refreshableViewWrapper.addView(newEmptyView, lp);
181 | } else {
182 | refreshableViewWrapper.addView(newEmptyView);
183 | }
184 | }
185 |
186 | if (mRefreshableView instanceof EmptyViewMethodAccessor) {
187 | ((EmptyViewMethodAccessor) mRefreshableView).setEmptyViewInternal(newEmptyView);
188 | } else {
189 | mRefreshableView.setEmptyView(newEmptyView);
190 | }
191 | mEmptyView = newEmptyView;
192 | }
193 |
194 | /**
195 | * Pass-through method for {@link PullToRefreshBase#getRefreshableView()
196 | * getRefreshableView()}.
197 | * {@link android.widget.AdapterView#setOnItemClickListener(android.widget.AdapterView.OnItemClickListener)
198 | * setOnItemClickListener(listener)}. This is just for convenience!
199 | *
200 | * @param listener - OnItemClickListener to use
201 | */
202 | public void setOnItemClickListener(OnItemClickListener listener) {
203 | mRefreshableView.setOnItemClickListener(listener);
204 | }
205 |
206 | public final void setOnLastItemVisibleListener(OnLastItemVisibleListener listener) {
207 | mOnLastItemVisibleListener = listener;
208 | }
209 |
210 | public final void setOnScrollListener(OnScrollListener listener) {
211 | mOnScrollListener = listener;
212 | }
213 |
214 | public final void setScrollEmptyView(boolean doScroll) {
215 | mScrollEmptyView = doScroll;
216 | }
217 |
218 | /**
219 | * Sets whether an indicator graphic should be displayed when the View is in
220 | * a state where a Pull-to-Refresh can happen. An example of this state is
221 | * when the Adapter View is scrolled to the top and the mode is set to
222 | * {@link PullToRefreshBase.Mode#PULL_FROM_START}
223 | *
224 | * @param showIndicator - true if the indicators should be shown.
225 | */
226 | public void setShowIndicator(boolean showIndicator) {
227 | mShowIndicator = showIndicator;
228 |
229 | if (getShowIndicatorInternal()) {
230 | // If we're set to Show Indicator, add/update them
231 | addIndicatorViews();
232 | } else {
233 | // If not, then remove then
234 | removeIndicatorViews();
235 | }
236 | }
237 |
238 | ;
239 |
240 | @Override
241 | protected void onPullToRefresh() {
242 | super.onPullToRefresh();
243 |
244 | if (getShowIndicatorInternal()) {
245 | switch (getCurrentMode()) {
246 | case PULL_FROM_END:
247 | mIndicatorIvBottom.pullToRefresh();
248 | break;
249 | case PULL_FROM_START:
250 | mIndicatorIvTop.pullToRefresh();
251 | break;
252 | default:
253 | // NO-OP
254 | break;
255 | }
256 | }
257 | }
258 |
259 | protected void onRefreshing(boolean doScroll) {
260 | super.onRefreshing(doScroll);
261 |
262 | if (getShowIndicatorInternal()) {
263 | updateIndicatorViewsVisibility();
264 | }
265 | }
266 |
267 | @Override
268 | protected void onReleaseToRefresh() {
269 | super.onReleaseToRefresh();
270 |
271 | if (getShowIndicatorInternal()) {
272 | switch (getCurrentMode()) {
273 | case PULL_FROM_END:
274 | mIndicatorIvBottom.releaseToRefresh();
275 | break;
276 | case PULL_FROM_START:
277 | mIndicatorIvTop.releaseToRefresh();
278 | break;
279 | default:
280 | // NO-OP
281 | break;
282 | }
283 | }
284 | }
285 |
286 | @Override
287 | protected void onReset() {
288 | super.onReset();
289 |
290 | if (getShowIndicatorInternal()) {
291 | updateIndicatorViewsVisibility();
292 | }
293 | }
294 |
295 | @Override
296 | protected void handleStyledAttributes(TypedArray a) {
297 | // Set Show Indicator to the XML value, or default value
298 | mShowIndicator = a.getBoolean(R.styleable.PullToRefresh_ptrShowIndicator, !isPullToRefreshOverScrollEnabled());
299 | }
300 |
301 | protected boolean isReadyForPullStart() {
302 | return isFirstItemVisible();
303 | }
304 |
305 | protected boolean isReadyForPullEnd() {
306 | return isLastItemVisible();
307 | }
308 |
309 | @Override
310 | protected void onScrollChanged(int l, int t, int oldl, int oldt) {
311 | super.onScrollChanged(l, t, oldl, oldt);
312 | if (null != mEmptyView && !mScrollEmptyView) {
313 | mEmptyView.scrollTo(-l, -t);
314 | }
315 | }
316 |
317 | @Override
318 | protected void updateUIForMode() {
319 | super.updateUIForMode();
320 |
321 | // Check Indicator Views consistent with new Mode
322 | if (getShowIndicatorInternal()) {
323 | addIndicatorViews();
324 | } else {
325 | removeIndicatorViews();
326 | }
327 | }
328 |
329 | private void addIndicatorViews() {
330 | Mode mode = getMode();
331 | FrameLayout refreshableViewWrapper = getRefreshableViewWrapper();
332 |
333 | if (mode.showHeaderLoadingLayout() && null == mIndicatorIvTop) {
334 | // If the mode can pull down, and we don't have one set already
335 | mIndicatorIvTop = new IndicatorLayout(getContext(), Mode.PULL_FROM_START);
336 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
337 | ViewGroup.LayoutParams.WRAP_CONTENT);
338 | params.rightMargin = getResources().getDimensionPixelSize(R.dimen.indicator_right_padding);
339 | params.gravity = Gravity.TOP | Gravity.RIGHT;
340 | refreshableViewWrapper.addView(mIndicatorIvTop, params);
341 |
342 | } else if (!mode.showHeaderLoadingLayout() && null != mIndicatorIvTop) {
343 | // If we can't pull down, but have a View then remove it
344 | refreshableViewWrapper.removeView(mIndicatorIvTop);
345 | mIndicatorIvTop = null;
346 | }
347 |
348 | if (mode.showFooterLoadingLayout() && null == mIndicatorIvBottom) {
349 | // If the mode can pull down, and we don't have one set already
350 | mIndicatorIvBottom = new IndicatorLayout(getContext(), Mode.PULL_FROM_END);
351 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
352 | ViewGroup.LayoutParams.WRAP_CONTENT);
353 | params.rightMargin = getResources().getDimensionPixelSize(R.dimen.indicator_right_padding);
354 | params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
355 | refreshableViewWrapper.addView(mIndicatorIvBottom, params);
356 |
357 | } else if (!mode.showFooterLoadingLayout() && null != mIndicatorIvBottom) {
358 | // If we can't pull down, but have a View then remove it
359 | refreshableViewWrapper.removeView(mIndicatorIvBottom);
360 | mIndicatorIvBottom = null;
361 | }
362 | }
363 |
364 | private boolean getShowIndicatorInternal() {
365 | return mShowIndicator && isPullToRefreshEnabled();
366 | }
367 |
368 | private boolean isFirstItemVisible() {
369 | final Adapter adapter = mRefreshableView.getAdapter();
370 |
371 | if (null == adapter || adapter.isEmpty()) {
372 | if (DEBUG) {
373 | Log.d(LOG_TAG, "isFirstItemVisible. Empty View.");
374 | }
375 | return true;
376 |
377 | } else {
378 |
379 | /**
380 | * This check should really just be:
381 | * mRefreshableView.getFirstVisiblePosition() == 0, but PtRListView
382 | * internally use a HeaderView which messes the positions up. For
383 | * now we'll just add one to account for it and rely on the inner
384 | * condition which checks getTop().
385 | */
386 | if (mRefreshableView.getFirstVisiblePosition() <= 1) {
387 | final View firstVisibleChild = mRefreshableView.getChildAt(0);
388 | if (firstVisibleChild != null) {
389 | return firstVisibleChild.getTop() >= mRefreshableView.getTop();
390 | }
391 | }
392 | }
393 |
394 | return false;
395 | }
396 |
397 | private boolean isLastItemVisible() {
398 | final Adapter adapter = mRefreshableView.getAdapter();
399 |
400 | if (null == adapter || adapter.isEmpty()) {
401 | if (DEBUG) {
402 | Log.d(LOG_TAG, "isLastItemVisible. Empty View.");
403 | }
404 | return true;
405 | } else {
406 | final int lastItemPosition = mRefreshableView.getCount() - 1;
407 | final int lastVisiblePosition = mRefreshableView.getLastVisiblePosition();
408 |
409 | if (DEBUG) {
410 | Log.d(LOG_TAG, "isLastItemVisible. Last Item Position: " + lastItemPosition + " Last Visible Pos: "
411 | + lastVisiblePosition);
412 | }
413 |
414 | /**
415 | * This check should really just be: lastVisiblePosition ==
416 | * lastItemPosition, but PtRListView internally uses a FooterView
417 | * which messes the positions up. For me we'll just subtract one to
418 | * account for it and rely on the inner condition which checks
419 | * getBottom().
420 | */
421 | if (lastVisiblePosition >= lastItemPosition - 1) {
422 | final int childIndex = lastVisiblePosition - mRefreshableView.getFirstVisiblePosition();
423 | final View lastVisibleChild = mRefreshableView.getChildAt(childIndex);
424 | if (lastVisibleChild != null) {
425 | return lastVisibleChild.getBottom() <= mRefreshableView.getBottom();
426 | }
427 | }
428 | }
429 |
430 | return false;
431 | }
432 |
433 | private void removeIndicatorViews() {
434 | if (null != mIndicatorIvTop) {
435 | getRefreshableViewWrapper().removeView(mIndicatorIvTop);
436 | mIndicatorIvTop = null;
437 | }
438 |
439 | if (null != mIndicatorIvBottom) {
440 | getRefreshableViewWrapper().removeView(mIndicatorIvBottom);
441 | mIndicatorIvBottom = null;
442 | }
443 | }
444 |
445 | private void updateIndicatorViewsVisibility() {
446 | if (null != mIndicatorIvTop) {
447 | if (!isRefreshing() && isReadyForPullStart()) {
448 | if (!mIndicatorIvTop.isVisible()) {
449 | mIndicatorIvTop.show();
450 | }
451 | } else {
452 | if (mIndicatorIvTop.isVisible()) {
453 | mIndicatorIvTop.hide();
454 | }
455 | }
456 | }
457 |
458 | if (null != mIndicatorIvBottom) {
459 | if (!isRefreshing() && isReadyForPullEnd()) {
460 | if (!mIndicatorIvBottom.isVisible()) {
461 | mIndicatorIvBottom.show();
462 | }
463 | } else {
464 | if (mIndicatorIvBottom.isVisible()) {
465 | mIndicatorIvBottom.hide();
466 | }
467 | }
468 | }
469 | }
470 | }
471 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/PullToRefreshExpandableListView.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library;
17 |
18 | import android.annotation.TargetApi;
19 | import android.content.Context;
20 | import android.os.Build.VERSION;
21 | import android.os.Build.VERSION_CODES;
22 | import android.util.AttributeSet;
23 | import android.view.View;
24 | import android.widget.ExpandableListView;
25 | import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor;
26 |
27 | public class PullToRefreshExpandableListView extends PullToRefreshAdapterViewBase {
28 |
29 | public PullToRefreshExpandableListView(Context context) {
30 | super(context);
31 | }
32 |
33 | public PullToRefreshExpandableListView(Context context, AttributeSet attrs) {
34 | super(context, attrs);
35 | }
36 |
37 | public PullToRefreshExpandableListView(Context context, Mode mode) {
38 | super(context, mode);
39 | }
40 |
41 | public PullToRefreshExpandableListView(Context context, Mode mode, AnimationStyle style) {
42 | super(context, mode, style);
43 | }
44 |
45 | @Override
46 | public final Orientation getPullToRefreshScrollDirection() {
47 | return Orientation.VERTICAL;
48 | }
49 |
50 | @Override
51 | protected ExpandableListView createRefreshableView(Context context, AttributeSet attrs) {
52 | final ExpandableListView lv;
53 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
54 | lv = new InternalExpandableListViewSDK9(context, attrs);
55 | } else {
56 | lv = new InternalExpandableListView(context, attrs);
57 | }
58 |
59 | // Set it to this so it can be used in ListActivity/ListFragment
60 | lv.setId(android.R.id.list);
61 | return lv;
62 | }
63 |
64 | class InternalExpandableListView extends ExpandableListView implements EmptyViewMethodAccessor {
65 |
66 | public InternalExpandableListView(Context context, AttributeSet attrs) {
67 | super(context, attrs);
68 | }
69 |
70 | @Override
71 | public void setEmptyView(View emptyView) {
72 | PullToRefreshExpandableListView.this.setEmptyView(emptyView);
73 | }
74 |
75 | @Override
76 | public void setEmptyViewInternal(View emptyView) {
77 | super.setEmptyView(emptyView);
78 | }
79 | }
80 |
81 | @TargetApi(9)
82 | final class InternalExpandableListViewSDK9 extends InternalExpandableListView {
83 |
84 | public InternalExpandableListViewSDK9(Context context, AttributeSet attrs) {
85 | super(context, attrs);
86 | }
87 |
88 | @Override
89 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
90 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
91 |
92 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
93 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
94 |
95 | // Does all of the hard work...
96 | OverscrollHelper.overScrollBy(PullToRefreshExpandableListView.this, deltaX, scrollX, deltaY, scrollY,
97 | isTouchEvent);
98 |
99 | return returnValue;
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/PullToRefreshGridView.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library;
17 |
18 | import android.annotation.TargetApi;
19 | import android.content.Context;
20 | import android.os.Build.VERSION;
21 | import android.os.Build.VERSION_CODES;
22 | import android.util.AttributeSet;
23 | import android.view.View;
24 | import android.widget.GridView;
25 | import com.bulletnoid.android.widget.StaggeredGridViewDemo.R;
26 | import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor;
27 |
28 | public class PullToRefreshGridView extends PullToRefreshAdapterViewBase {
29 |
30 | public PullToRefreshGridView(Context context) {
31 | super(context);
32 | }
33 |
34 | public PullToRefreshGridView(Context context, AttributeSet attrs) {
35 | super(context, attrs);
36 | }
37 |
38 | public PullToRefreshGridView(Context context, Mode mode) {
39 | super(context, mode);
40 | }
41 |
42 | public PullToRefreshGridView(Context context, Mode mode, AnimationStyle style) {
43 | super(context, mode, style);
44 | }
45 |
46 | @Override
47 | public final Orientation getPullToRefreshScrollDirection() {
48 | return Orientation.VERTICAL;
49 | }
50 |
51 | @Override
52 | protected final GridView createRefreshableView(Context context, AttributeSet attrs) {
53 | final GridView gv;
54 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
55 | gv = new InternalGridViewSDK9(context, attrs);
56 | } else {
57 | gv = new InternalGridView(context, attrs);
58 | }
59 |
60 | // Use Generated ID (from res/values/ptr_ids.xml)
61 | gv.setId(R.id.gridview);
62 | return gv;
63 | }
64 |
65 | class InternalGridView extends GridView implements EmptyViewMethodAccessor {
66 |
67 | public InternalGridView(Context context, AttributeSet attrs) {
68 | super(context, attrs);
69 | }
70 |
71 | @Override
72 | public void setEmptyView(View emptyView) {
73 | PullToRefreshGridView.this.setEmptyView(emptyView);
74 | }
75 |
76 | @Override
77 | public void setEmptyViewInternal(View emptyView) {
78 | super.setEmptyView(emptyView);
79 | }
80 | }
81 |
82 | @TargetApi(9)
83 | final class InternalGridViewSDK9 extends InternalGridView {
84 |
85 | public InternalGridViewSDK9(Context context, AttributeSet attrs) {
86 | super(context, attrs);
87 | }
88 |
89 | @Override
90 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
91 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
92 |
93 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
94 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
95 |
96 | // Does all of the hard work...
97 | OverscrollHelper.overScrollBy(PullToRefreshGridView.this, deltaX, scrollX, deltaY, scrollY, isTouchEvent);
98 |
99 | return returnValue;
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/PullToRefreshHorizontalScrollView.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library;
17 |
18 | import android.annotation.TargetApi;
19 | import android.content.Context;
20 | import android.os.Build.VERSION;
21 | import android.os.Build.VERSION_CODES;
22 | import android.util.AttributeSet;
23 | import android.view.View;
24 | import android.widget.HorizontalScrollView;
25 | import com.bulletnoid.android.widget.StaggeredGridViewDemo.R;
26 |
27 | public class PullToRefreshHorizontalScrollView extends PullToRefreshBase {
28 |
29 | public PullToRefreshHorizontalScrollView(Context context) {
30 | super(context);
31 | }
32 |
33 | public PullToRefreshHorizontalScrollView(Context context, AttributeSet attrs) {
34 | super(context, attrs);
35 | }
36 |
37 | public PullToRefreshHorizontalScrollView(Context context, Mode mode) {
38 | super(context, mode);
39 | }
40 |
41 | public PullToRefreshHorizontalScrollView(Context context, Mode mode, AnimationStyle style) {
42 | super(context, mode, style);
43 | }
44 |
45 | @Override
46 | public final Orientation getPullToRefreshScrollDirection() {
47 | return Orientation.HORIZONTAL;
48 | }
49 |
50 | @Override
51 | protected HorizontalScrollView createRefreshableView(Context context, AttributeSet attrs) {
52 | HorizontalScrollView scrollView;
53 |
54 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
55 | scrollView = new InternalHorizontalScrollViewSDK9(context, attrs);
56 | } else {
57 | scrollView = new HorizontalScrollView(context, attrs);
58 | }
59 |
60 | scrollView.setId(R.id.scrollview);
61 | return scrollView;
62 | }
63 |
64 | @Override
65 | protected boolean isReadyForPullStart() {
66 | return mRefreshableView.getScrollX() == 0;
67 | }
68 |
69 | @Override
70 | protected boolean isReadyForPullEnd() {
71 | View scrollViewChild = mRefreshableView.getChildAt(0);
72 | if (null != scrollViewChild) {
73 | return mRefreshableView.getScrollX() >= (scrollViewChild.getWidth() - getWidth());
74 | }
75 | return false;
76 | }
77 |
78 | @TargetApi(9)
79 | final class InternalHorizontalScrollViewSDK9 extends HorizontalScrollView {
80 |
81 | public InternalHorizontalScrollViewSDK9(Context context, AttributeSet attrs) {
82 | super(context, attrs);
83 | }
84 |
85 | @Override
86 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
87 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
88 |
89 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
90 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
91 |
92 | // Does all of the hard work...
93 | OverscrollHelper.overScrollBy(PullToRefreshHorizontalScrollView.this, deltaX, scrollX, deltaY, scrollY,
94 | getScrollRange(), isTouchEvent);
95 |
96 | return returnValue;
97 | }
98 |
99 | /**
100 | * Taken from the AOSP ScrollView source
101 | */
102 | private int getScrollRange() {
103 | int scrollRange = 0;
104 | if (getChildCount() > 0) {
105 | View child = getChildAt(0);
106 | scrollRange = Math.max(0, child.getWidth() - (getWidth() - getPaddingLeft() - getPaddingRight()));
107 | }
108 | return scrollRange;
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/PullToRefreshListView.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library;
17 |
18 | import android.annotation.TargetApi;
19 | import android.content.Context;
20 | import android.content.res.TypedArray;
21 | import android.graphics.Canvas;
22 | import android.os.Build.VERSION;
23 | import android.os.Build.VERSION_CODES;
24 | import android.util.AttributeSet;
25 | import android.view.Gravity;
26 | import android.view.MotionEvent;
27 | import android.view.View;
28 | import android.widget.FrameLayout;
29 | import android.widget.ListAdapter;
30 | import android.widget.ListView;
31 | import com.bulletnoid.android.widget.StaggeredGridViewDemo.R;
32 | import com.handmark.pulltorefresh.library.internal.EmptyViewMethodAccessor;
33 | import com.handmark.pulltorefresh.library.internal.LoadingLayout;
34 |
35 | public class PullToRefreshListView extends PullToRefreshAdapterViewBase {
36 |
37 | private LoadingLayout mHeaderLoadingView;
38 | private LoadingLayout mFooterLoadingView;
39 |
40 | private FrameLayout mLvFooterLoadingFrame;
41 |
42 | private boolean mListViewExtrasEnabled;
43 |
44 | public PullToRefreshListView(Context context) {
45 | super(context);
46 | }
47 |
48 | public PullToRefreshListView(Context context, AttributeSet attrs) {
49 | super(context, attrs);
50 | }
51 |
52 | public PullToRefreshListView(Context context, Mode mode) {
53 | super(context, mode);
54 | }
55 |
56 | public PullToRefreshListView(Context context, Mode mode, AnimationStyle style) {
57 | super(context, mode, style);
58 | }
59 |
60 | @Override
61 | public final Orientation getPullToRefreshScrollDirection() {
62 | return Orientation.VERTICAL;
63 | }
64 |
65 | @Override
66 | protected void onRefreshing(final boolean doScroll) {
67 | /**
68 | * If we're not showing the Refreshing view, or the list is empty, the
69 | * the header/footer views won't show so we use the normal method.
70 | */
71 | ListAdapter adapter = mRefreshableView.getAdapter();
72 | if (!mListViewExtrasEnabled || !getShowViewWhileRefreshing() || null == adapter || adapter.isEmpty()) {
73 | super.onRefreshing(doScroll);
74 | return;
75 | }
76 |
77 | super.onRefreshing(false);
78 |
79 | final LoadingLayout origLoadingView, listViewLoadingView, oppositeListViewLoadingView;
80 | final int selection, scrollToY;
81 |
82 | switch (getCurrentMode()) {
83 | case MANUAL_REFRESH_ONLY:
84 | case PULL_FROM_END:
85 | origLoadingView = getFooterLayout();
86 | listViewLoadingView = mFooterLoadingView;
87 | oppositeListViewLoadingView = mHeaderLoadingView;
88 | selection = mRefreshableView.getCount() - 1;
89 | scrollToY = getScrollY() - getFooterSize();
90 | break;
91 | case PULL_FROM_START:
92 | default:
93 | origLoadingView = getHeaderLayout();
94 | listViewLoadingView = mHeaderLoadingView;
95 | oppositeListViewLoadingView = mFooterLoadingView;
96 | selection = 0;
97 | scrollToY = getScrollY() + getHeaderSize();
98 | break;
99 | }
100 |
101 | // Hide our original Loading View
102 | origLoadingView.reset();
103 | origLoadingView.hideAllViews();
104 |
105 | // Make sure the opposite end is hidden too
106 | oppositeListViewLoadingView.setVisibility(GONE);
107 |
108 | // Show the ListView Loading View and set it to refresh.
109 | listViewLoadingView.setVisibility(VISIBLE);
110 | listViewLoadingView.refreshing();
111 |
112 | if (doScroll) {
113 | // We need to disable the automatic visibility changes for now
114 | disableLoadingLayoutVisibilityChanges();
115 |
116 | // We scroll slightly so that the ListView's header/footer is at the
117 | // same Y position as our normal header/footer
118 | setHeaderScroll(scrollToY);
119 |
120 | // Make sure the ListView is scrolled to show the loading
121 | // header/footer
122 | mRefreshableView.setSelection(selection);
123 |
124 | // Smooth scroll as normal
125 | smoothScrollTo(0);
126 | }
127 | }
128 |
129 | @Override
130 | protected void onReset() {
131 | /**
132 | * If the extras are not enabled, just call up to super and return.
133 | */
134 | if (!mListViewExtrasEnabled) {
135 | super.onReset();
136 | return;
137 | }
138 |
139 | final LoadingLayout originalLoadingLayout, listViewLoadingLayout;
140 | final int scrollToHeight, selection;
141 | final boolean scrollLvToEdge;
142 |
143 | switch (getCurrentMode()) {
144 | case MANUAL_REFRESH_ONLY:
145 | case PULL_FROM_END:
146 | originalLoadingLayout = getFooterLayout();
147 | listViewLoadingLayout = mFooterLoadingView;
148 | selection = mRefreshableView.getCount() - 1;
149 | scrollToHeight = getFooterSize();
150 | scrollLvToEdge = Math.abs(mRefreshableView.getLastVisiblePosition() - selection) <= 1;
151 | break;
152 | case PULL_FROM_START:
153 | default:
154 | originalLoadingLayout = getHeaderLayout();
155 | listViewLoadingLayout = mHeaderLoadingView;
156 | scrollToHeight = -getHeaderSize();
157 | selection = 0;
158 | scrollLvToEdge = Math.abs(mRefreshableView.getFirstVisiblePosition() - selection) <= 1;
159 | break;
160 | }
161 |
162 | // If the ListView header loading layout is showing, then we need to
163 | // flip so that the original one is showing instead
164 | if (listViewLoadingLayout.getVisibility() == VISIBLE) {
165 |
166 | // Set our Original View to Visible
167 | originalLoadingLayout.showInvisibleViews();
168 |
169 | // Hide the ListView Header/Footer
170 | listViewLoadingLayout.setVisibility(GONE);
171 |
172 | /**
173 | * Scroll so the View is at the same Y as the ListView
174 | * header/footer, but only scroll if: we've pulled to refresh, it's
175 | * positioned correctly
176 | */
177 | if (scrollLvToEdge && getState() != State.MANUAL_REFRESHING) {
178 | mRefreshableView.setSelection(selection);
179 | setHeaderScroll(scrollToHeight);
180 | }
181 | }
182 |
183 | // Finally, call up to super
184 | super.onReset();
185 | }
186 |
187 | @Override
188 | protected LoadingLayoutProxy createLoadingLayoutProxy(final boolean includeStart, final boolean includeEnd) {
189 | LoadingLayoutProxy proxy = super.createLoadingLayoutProxy(includeStart, includeEnd);
190 |
191 | if (mListViewExtrasEnabled) {
192 | final Mode mode = getMode();
193 |
194 | if (includeStart && mode.showHeaderLoadingLayout()) {
195 | proxy.addLayout(mHeaderLoadingView);
196 | }
197 | if (includeEnd && mode.showFooterLoadingLayout()) {
198 | proxy.addLayout(mFooterLoadingView);
199 | }
200 | }
201 |
202 | return proxy;
203 | }
204 |
205 | protected ListView createListView(Context context, AttributeSet attrs) {
206 | final ListView lv;
207 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
208 | lv = new InternalListViewSDK9(context, attrs);
209 | } else {
210 | lv = new InternalListView(context, attrs);
211 | }
212 | return lv;
213 | }
214 |
215 | @Override
216 | protected ListView createRefreshableView(Context context, AttributeSet attrs) {
217 | ListView lv = createListView(context, attrs);
218 |
219 | // Set it to this so it can be used in ListActivity/ListFragment
220 | lv.setId(android.R.id.list);
221 | return lv;
222 | }
223 |
224 | @Override
225 | protected void handleStyledAttributes(TypedArray a) {
226 | super.handleStyledAttributes(a);
227 |
228 | mListViewExtrasEnabled = a.getBoolean(R.styleable.PullToRefresh_ptrListViewExtrasEnabled, true);
229 |
230 | if (mListViewExtrasEnabled) {
231 | final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
232 | FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL);
233 |
234 | // Create Loading Views ready for use later
235 | FrameLayout frame = new FrameLayout(getContext());
236 | mHeaderLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_START, a);
237 | mHeaderLoadingView.setVisibility(GONE);
238 | frame.addView(mHeaderLoadingView, lp);
239 | mRefreshableView.addHeaderView(frame, null, false);
240 |
241 | mLvFooterLoadingFrame = new FrameLayout(getContext());
242 | mFooterLoadingView = createLoadingLayout(getContext(), Mode.PULL_FROM_END, a);
243 | mFooterLoadingView.setVisibility(GONE);
244 | mLvFooterLoadingFrame.addView(mFooterLoadingView, lp);
245 |
246 | /**
247 | * If the value for Scrolling While Refreshing hasn't been
248 | * explicitly set via XML, enable Scrolling While Refreshing.
249 | */
250 | if (!a.hasValue(R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled)) {
251 | setScrollingWhileRefreshingEnabled(true);
252 | }
253 | }
254 | }
255 |
256 | @TargetApi(9)
257 | final class InternalListViewSDK9 extends InternalListView {
258 |
259 | public InternalListViewSDK9(Context context, AttributeSet attrs) {
260 | super(context, attrs);
261 | }
262 |
263 | @Override
264 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
265 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
266 |
267 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
268 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
269 |
270 | // Does all of the hard work...
271 | OverscrollHelper.overScrollBy(PullToRefreshListView.this, deltaX, scrollX, deltaY, scrollY, isTouchEvent);
272 |
273 | return returnValue;
274 | }
275 | }
276 |
277 | protected class InternalListView extends ListView implements EmptyViewMethodAccessor {
278 |
279 | private boolean mAddedLvFooter = false;
280 |
281 | public InternalListView(Context context, AttributeSet attrs) {
282 | super(context, attrs);
283 | }
284 |
285 | @Override
286 | protected void dispatchDraw(Canvas canvas) {
287 | /**
288 | * This is a bit hacky, but Samsung's ListView has got a bug in it
289 | * when using Header/Footer Views and the list is empty. This masks
290 | * the issue so that it doesn't cause an FC. See Issue #66.
291 | */
292 | try {
293 | super.dispatchDraw(canvas);
294 | } catch (IndexOutOfBoundsException e) {
295 | e.printStackTrace();
296 | }
297 | }
298 |
299 | @Override
300 | public boolean dispatchTouchEvent(MotionEvent ev) {
301 | /**
302 | * This is a bit hacky, but Samsung's ListView has got a bug in it
303 | * when using Header/Footer Views and the list is empty. This masks
304 | * the issue so that it doesn't cause an FC. See Issue #66.
305 | */
306 | try {
307 | return super.dispatchTouchEvent(ev);
308 | } catch (IndexOutOfBoundsException e) {
309 | e.printStackTrace();
310 | return false;
311 | }
312 | }
313 |
314 | @Override
315 | public void setAdapter(ListAdapter adapter) {
316 | // Add the Footer View at the last possible moment
317 | if (null != mLvFooterLoadingFrame && !mAddedLvFooter) {
318 | addFooterView(mLvFooterLoadingFrame, null, false);
319 | mAddedLvFooter = true;
320 | }
321 |
322 | super.setAdapter(adapter);
323 | }
324 |
325 | @Override
326 | public void setEmptyView(View emptyView) {
327 | PullToRefreshListView.this.setEmptyView(emptyView);
328 | }
329 |
330 | @Override
331 | public void setEmptyViewInternal(View emptyView) {
332 | super.setEmptyView(emptyView);
333 | }
334 |
335 | }
336 |
337 | }
338 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/PullToRefreshScrollView.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library;
17 |
18 | import android.annotation.TargetApi;
19 | import android.content.Context;
20 | import android.os.Build.VERSION;
21 | import android.os.Build.VERSION_CODES;
22 | import android.util.AttributeSet;
23 | import android.view.View;
24 | import android.widget.ScrollView;
25 | import com.bulletnoid.android.widget.StaggeredGridViewDemo.R;
26 |
27 | public class PullToRefreshScrollView extends PullToRefreshBase {
28 |
29 | public PullToRefreshScrollView(Context context) {
30 | super(context);
31 | }
32 |
33 | public PullToRefreshScrollView(Context context, AttributeSet attrs) {
34 | super(context, attrs);
35 | }
36 |
37 | public PullToRefreshScrollView(Context context, Mode mode) {
38 | super(context, mode);
39 | }
40 |
41 | public PullToRefreshScrollView(Context context, Mode mode, AnimationStyle style) {
42 | super(context, mode, style);
43 | }
44 |
45 | @Override
46 | public final Orientation getPullToRefreshScrollDirection() {
47 | return Orientation.VERTICAL;
48 | }
49 |
50 | @Override
51 | protected ScrollView createRefreshableView(Context context, AttributeSet attrs) {
52 | ScrollView scrollView;
53 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
54 | scrollView = new InternalScrollViewSDK9(context, attrs);
55 | } else {
56 | scrollView = new ScrollView(context, attrs);
57 | }
58 |
59 | scrollView.setId(R.id.scrollview);
60 | return scrollView;
61 | }
62 |
63 | @Override
64 | protected boolean isReadyForPullStart() {
65 | return mRefreshableView.getScrollY() == 0;
66 | }
67 |
68 | @Override
69 | protected boolean isReadyForPullEnd() {
70 | View scrollViewChild = mRefreshableView.getChildAt(0);
71 | if (null != scrollViewChild) {
72 | return mRefreshableView.getScrollY() >= (scrollViewChild.getHeight() - getHeight());
73 | }
74 | return false;
75 | }
76 |
77 | @TargetApi(9)
78 | final class InternalScrollViewSDK9 extends ScrollView {
79 |
80 | public InternalScrollViewSDK9(Context context, AttributeSet attrs) {
81 | super(context, attrs);
82 | }
83 |
84 | @Override
85 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
86 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
87 |
88 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
89 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
90 |
91 | // Does all of the hard work...
92 | OverscrollHelper.overScrollBy(PullToRefreshScrollView.this, deltaX, scrollX, deltaY, scrollY,
93 | getScrollRange(), isTouchEvent);
94 |
95 | return returnValue;
96 | }
97 |
98 | /**
99 | * Taken from the AOSP ScrollView source
100 | */
101 | private int getScrollRange() {
102 | int scrollRange = 0;
103 | if (getChildCount() > 0) {
104 | View child = getChildAt(0);
105 | scrollRange = Math.max(0, child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop()));
106 | }
107 | return scrollRange;
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/PullToRefreshStaggeredGridView.java:
--------------------------------------------------------------------------------
1 | package com.handmark.pulltorefresh.library;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 | import android.widget.BaseAdapter;
9 | import com.bulletnoid.android.widget.StaggeredGridView.StaggeredGridView;
10 | import com.bulletnoid.android.widget.StaggeredGridViewDemo.R;
11 |
12 | public class PullToRefreshStaggeredGridView extends PullToRefreshBase {
13 |
14 | public PullToRefreshStaggeredGridView(Context context) {
15 | super(context);
16 | }
17 |
18 | public PullToRefreshStaggeredGridView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | public PullToRefreshStaggeredGridView(Context context, Mode mode) {
23 | super(context, mode);
24 | }
25 |
26 | public PullToRefreshStaggeredGridView(Context context, Mode mode, AnimationStyle style) {
27 | super(context, mode, style);
28 | }
29 |
30 | @Override
31 | public Orientation getPullToRefreshScrollDirection() {
32 | return Orientation.VERTICAL;
33 | }
34 |
35 | @Override
36 | protected StaggeredGridView createRefreshableView(Context context, AttributeSet attrs) {
37 | StaggeredGridView stgv;
38 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
39 | stgv = new InternalStaggeredGridViewSDK9(context, attrs);
40 | } else {
41 | stgv = new StaggeredGridView(context, attrs);
42 | }
43 |
44 | int margin = getResources().getDimensionPixelSize(R.dimen.stgv_margin);
45 | stgv.setColumnCount(2);
46 | stgv.setItemMargin(margin);
47 | stgv.setPadding(margin, 0, margin, 0);
48 |
49 | stgv.setId(R.id.stgv);
50 | return stgv;
51 | }
52 |
53 | @Override
54 | protected boolean isReadyForPullStart() {
55 | return mRefreshableView.mGetToTop;
56 | }
57 |
58 | @Override
59 | protected boolean isReadyForPullEnd() {
60 | return false;
61 | }
62 |
63 | public void setAdapter(BaseAdapter adapter) {
64 | mRefreshableView.setAdapter(adapter);
65 | }
66 |
67 | @TargetApi(9)
68 | final class InternalStaggeredGridViewSDK9 extends StaggeredGridView {
69 |
70 | public InternalStaggeredGridViewSDK9(Context context, AttributeSet attrs) {
71 | super(context, attrs);
72 | }
73 |
74 | @Override
75 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
76 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
77 |
78 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
79 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
80 |
81 | // Does all of the hard work...
82 | OverscrollHelper.overScrollBy(PullToRefreshStaggeredGridView.this, deltaX, scrollX, deltaY, scrollY,
83 | getScrollRange(), isTouchEvent);
84 |
85 | return returnValue;
86 | }
87 |
88 | /**
89 | * Taken from the AOSP ScrollView source
90 | */
91 | private int getScrollRange() {
92 | int scrollRange = 0;
93 | if (getChildCount() > 0) {
94 | View child = getChildAt(0);
95 | scrollRange = Math.max(0, child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop()));
96 | }
97 | return scrollRange;
98 | }
99 | }
100 |
101 | public final void setOnLoadmoreListener(StaggeredGridView.OnLoadmoreListener listener) {
102 | mRefreshableView.setOnLoadmoreListener(listener);
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/PullToRefreshWebView.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library;
17 |
18 | import android.annotation.TargetApi;
19 | import android.content.Context;
20 | import android.os.Build.VERSION;
21 | import android.os.Build.VERSION_CODES;
22 | import android.os.Bundle;
23 | import android.util.AttributeSet;
24 | import android.util.FloatMath;
25 | import android.webkit.WebChromeClient;
26 | import android.webkit.WebView;
27 | import com.bulletnoid.android.widget.StaggeredGridViewDemo.R;
28 |
29 | public class PullToRefreshWebView extends PullToRefreshBase {
30 |
31 | private static final OnRefreshListener defaultOnRefreshListener = new OnRefreshListener() {
32 |
33 | @Override
34 | public void onRefresh(PullToRefreshBase refreshView) {
35 | refreshView.getRefreshableView().reload();
36 | }
37 |
38 | };
39 |
40 | private final WebChromeClient defaultWebChromeClient = new WebChromeClient() {
41 |
42 | @Override
43 | public void onProgressChanged(WebView view, int newProgress) {
44 | if (newProgress == 100) {
45 | onRefreshComplete();
46 | }
47 | }
48 |
49 | };
50 |
51 | public PullToRefreshWebView(Context context) {
52 | super(context);
53 |
54 | /**
55 | * Added so that by default, Pull-to-Refresh refreshes the page
56 | */
57 | setOnRefreshListener(defaultOnRefreshListener);
58 | mRefreshableView.setWebChromeClient(defaultWebChromeClient);
59 | }
60 |
61 | public PullToRefreshWebView(Context context, AttributeSet attrs) {
62 | super(context, attrs);
63 |
64 | /**
65 | * Added so that by default, Pull-to-Refresh refreshes the page
66 | */
67 | setOnRefreshListener(defaultOnRefreshListener);
68 | mRefreshableView.setWebChromeClient(defaultWebChromeClient);
69 | }
70 |
71 | public PullToRefreshWebView(Context context, Mode mode) {
72 | super(context, mode);
73 |
74 | /**
75 | * Added so that by default, Pull-to-Refresh refreshes the page
76 | */
77 | setOnRefreshListener(defaultOnRefreshListener);
78 | mRefreshableView.setWebChromeClient(defaultWebChromeClient);
79 | }
80 |
81 | public PullToRefreshWebView(Context context, Mode mode, AnimationStyle style) {
82 | super(context, mode, style);
83 |
84 | /**
85 | * Added so that by default, Pull-to-Refresh refreshes the page
86 | */
87 | setOnRefreshListener(defaultOnRefreshListener);
88 | mRefreshableView.setWebChromeClient(defaultWebChromeClient);
89 | }
90 |
91 | @Override
92 | public final Orientation getPullToRefreshScrollDirection() {
93 | return Orientation.VERTICAL;
94 | }
95 |
96 | @Override
97 | protected WebView createRefreshableView(Context context, AttributeSet attrs) {
98 | WebView webView;
99 | if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
100 | webView = new InternalWebViewSDK9(context, attrs);
101 | } else {
102 | webView = new WebView(context, attrs);
103 | }
104 |
105 | webView.setId(R.id.webview);
106 | return webView;
107 | }
108 |
109 | @Override
110 | protected boolean isReadyForPullStart() {
111 | return mRefreshableView.getScrollY() == 0;
112 | }
113 |
114 | @Override
115 | protected boolean isReadyForPullEnd() {
116 | float exactContentHeight = FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale());
117 | return mRefreshableView.getScrollY() >= (exactContentHeight - mRefreshableView.getHeight());
118 | }
119 |
120 | @Override
121 | protected void onPtrRestoreInstanceState(Bundle savedInstanceState) {
122 | super.onPtrRestoreInstanceState(savedInstanceState);
123 | mRefreshableView.restoreState(savedInstanceState);
124 | }
125 |
126 | @Override
127 | protected void onPtrSaveInstanceState(Bundle saveState) {
128 | super.onPtrSaveInstanceState(saveState);
129 | mRefreshableView.saveState(saveState);
130 | }
131 |
132 | @TargetApi(9)
133 | final class InternalWebViewSDK9 extends WebView {
134 |
135 | // WebView doesn't always scroll back to it's edge so we add some
136 | // fuzziness
137 | static final int OVERSCROLL_FUZZY_THRESHOLD = 2;
138 |
139 | // WebView seems quite reluctant to overscroll so we use the scale
140 | // factor to scale it's value
141 | static final float OVERSCROLL_SCALE_FACTOR = 1.5f;
142 |
143 | public InternalWebViewSDK9(Context context, AttributeSet attrs) {
144 | super(context, attrs);
145 | }
146 |
147 | @Override
148 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX,
149 | int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
150 |
151 | final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
152 | scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
153 |
154 | // Does all of the hard work...
155 | OverscrollHelper.overScrollBy(PullToRefreshWebView.this, deltaX, scrollX, deltaY, scrollY,
156 | getScrollRange(), OVERSCROLL_FUZZY_THRESHOLD, OVERSCROLL_SCALE_FACTOR, isTouchEvent);
157 |
158 | return returnValue;
159 | }
160 |
161 | private int getScrollRange() {
162 | return (int) Math.max(0, FloatMath.floor(mRefreshableView.getContentHeight() * mRefreshableView.getScale())
163 | - (getHeight() - getPaddingBottom() - getPaddingTop()));
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/extras/PullToRefreshWebView2.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library.extras;
17 |
18 | import android.content.Context;
19 | import android.util.AttributeSet;
20 | import android.webkit.WebView;
21 | import com.handmark.pulltorefresh.library.PullToRefreshWebView;
22 |
23 | import java.util.concurrent.atomic.AtomicBoolean;
24 |
25 | /**
26 | * An advanced version of {@link com.handmark.pulltorefresh.library.PullToRefreshWebView} which delegates the
27 | * triggering of the PullToRefresh gesture to the Javascript running within the
28 | * WebView. This means that you should only use this class if:
29 | *
30 | *
31 | *
{@link com.handmark.pulltorefresh.library.PullToRefreshWebView} doesn't work correctly because you're using
32 | * overflow:scroll or something else which means
33 | * {@link android.webkit.WebView#getScrollY()} doesn't return correct values.
34 | *
You control the web content being displayed, as you need to write some
35 | * Javascript callbacks.
36 | *
37 | *
38 | *
39 | * The way this call works is that when a PullToRefresh gesture is in action,
40 | * the following Javascript methods will be called:
41 | * isReadyForPullDown() and isReadyForPullUp(), it is
42 | * your job to calculate whether the view is in a state where a PullToRefresh
43 | * can happen, and return the result via the callback mechanism. An example can
44 | * be seen below:
45 | *
46 | *
47 | *
48 | * function isReadyForPullDown() {
49 | * var result = ... // Probably using the .scrollTop DOM attribute
50 | * ptr.isReadyForPullDownResponse(result);
51 | * }
52 | *
53 | * function isReadyForPullUp() {
54 | * var result = ... // Probably using the .scrollBottom DOM attribute
55 | * ptr.isReadyForPullUpResponse(result);
56 | * }
57 | *
58 | *
59 | * @author Chris Banes
60 | */
61 | public class PullToRefreshWebView2 extends PullToRefreshWebView {
62 |
63 | static final String JS_INTERFACE_PKG = "ptr";
64 | static final String DEF_JS_READY_PULL_DOWN_CALL = "javascript:isReadyForPullDown();";
65 | static final String DEF_JS_READY_PULL_UP_CALL = "javascript:isReadyForPullUp();";
66 |
67 | public PullToRefreshWebView2(Context context) {
68 | super(context);
69 | }
70 |
71 | public PullToRefreshWebView2(Context context, AttributeSet attrs) {
72 | super(context, attrs);
73 | }
74 |
75 | public PullToRefreshWebView2(Context context, Mode mode) {
76 | super(context, mode);
77 | }
78 |
79 | private JsValueCallback mJsCallback;
80 | private final AtomicBoolean mIsReadyForPullDown = new AtomicBoolean(false);
81 | private final AtomicBoolean mIsReadyForPullUp = new AtomicBoolean(false);
82 |
83 | @Override
84 | protected WebView createRefreshableView(Context context, AttributeSet attrs) {
85 | WebView webView = super.createRefreshableView(context, attrs);
86 |
87 | // Need to add JS Interface so we can get the response back
88 | mJsCallback = new JsValueCallback();
89 | webView.addJavascriptInterface(mJsCallback, JS_INTERFACE_PKG);
90 |
91 | return webView;
92 | }
93 |
94 | @Override
95 | protected boolean isReadyForPullStart() {
96 | // Call Javascript...
97 | getRefreshableView().loadUrl(DEF_JS_READY_PULL_DOWN_CALL);
98 |
99 | // Response will be given to JsValueCallback, which will update
100 | // mIsReadyForPullDown
101 |
102 | return mIsReadyForPullDown.get();
103 | }
104 |
105 | @Override
106 | protected boolean isReadyForPullEnd() {
107 | // Call Javascript...
108 | getRefreshableView().loadUrl(DEF_JS_READY_PULL_UP_CALL);
109 |
110 | // Response will be given to JsValueCallback, which will update
111 | // mIsReadyForPullUp
112 |
113 | return mIsReadyForPullUp.get();
114 | }
115 |
116 | /**
117 | * Used for response from Javascript
118 | *
119 | * @author Chris Banes
120 | */
121 | final class JsValueCallback {
122 |
123 | public void isReadyForPullUpResponse(boolean response) {
124 | mIsReadyForPullUp.set(response);
125 | }
126 |
127 | public void isReadyForPullDownResponse(boolean response) {
128 | mIsReadyForPullDown.set(response);
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/extras/SoundPullEventListener.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library.extras;
17 |
18 | import android.content.Context;
19 | import android.media.MediaPlayer;
20 | import android.view.View;
21 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode;
22 | import com.handmark.pulltorefresh.library.PullToRefreshBase.State;
23 | import com.handmark.pulltorefresh.library.PullToRefreshBase;
24 |
25 | import java.util.HashMap;
26 |
27 | public class SoundPullEventListener implements PullToRefreshBase.OnPullEventListener {
28 |
29 | private final Context mContext;
30 | private final HashMap mSoundMap;
31 |
32 | private MediaPlayer mCurrentMediaPlayer;
33 |
34 | /**
35 | * Constructor
36 | *
37 | * @param context - Context
38 | */
39 | public SoundPullEventListener(Context context) {
40 | mContext = context;
41 | mSoundMap = new HashMap();
42 | }
43 |
44 | @Override
45 | public final void onPullEvent(PullToRefreshBase refreshView, State event, Mode direction) {
46 | Integer soundResIdObj = mSoundMap.get(event);
47 | if (null != soundResIdObj) {
48 | playSound(soundResIdObj.intValue());
49 | }
50 | }
51 |
52 | /**
53 | * Set the Sounds to be played when a Pull Event happens. You specify which
54 | * sound plays for which events by calling this method multiple times for
55 | * each event.
56 | *
57 | * If you've already set a sound for a certain event, and add another sound
58 | * for that event, only the new sound will be played.
59 | *
60 | * @param event - The event for which the sound will be played.
61 | * @param resId - Resource Id of the sound file to be played (e.g.
62 | * R.raw.pull_sound)
63 | */
64 | public void addSoundEvent(State event, int resId) {
65 | mSoundMap.put(event, resId);
66 | }
67 |
68 | /**
69 | * Clears all of the previously set sounds and events.
70 | */
71 | public void clearSounds() {
72 | mSoundMap.clear();
73 | }
74 |
75 | /**
76 | * Gets the current (or last) MediaPlayer instance.
77 | */
78 | public MediaPlayer getCurrentMediaPlayer() {
79 | return mCurrentMediaPlayer;
80 | }
81 |
82 | private void playSound(int resId) {
83 | // Stop current player, if there's one playing
84 | if (null != mCurrentMediaPlayer) {
85 | mCurrentMediaPlayer.stop();
86 | mCurrentMediaPlayer.release();
87 | }
88 |
89 | mCurrentMediaPlayer = MediaPlayer.create(mContext, resId);
90 | if (null != mCurrentMediaPlayer) {
91 | mCurrentMediaPlayer.start();
92 | }
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/internal/EmptyViewMethodAccessor.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library.internal;
17 |
18 | import android.view.View;
19 |
20 | /**
21 | * Interface that allows PullToRefreshBase to hijack the call to
22 | * AdapterView.setEmptyView()
23 | *
24 | * @author chris
25 | */
26 | public interface EmptyViewMethodAccessor {
27 |
28 | /**
29 | * Calls upto AdapterView.setEmptyView()
30 | *
31 | * @param emptyView - to set as Empty View
32 | */
33 | public void setEmptyViewInternal(View emptyView);
34 |
35 | /**
36 | * Should call PullToRefreshBase.setEmptyView() which will then
37 | * automatically call through to setEmptyViewInternal()
38 | *
39 | * @param emptyView - to set as Empty View
40 | */
41 | public void setEmptyView(View emptyView);
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/internal/FlipLoadingLayout.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library.internal;
17 |
18 | import android.annotation.SuppressLint;
19 | import android.content.Context;
20 | import android.content.res.TypedArray;
21 | import android.graphics.Matrix;
22 | import android.graphics.drawable.Drawable;
23 | import android.view.View;
24 | import android.view.ViewGroup;
25 | import android.view.animation.Animation;
26 | import android.view.animation.RotateAnimation;
27 | import android.widget.ImageView.ScaleType;
28 | import com.bulletnoid.android.widget.StaggeredGridViewDemo.R;
29 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode;
30 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Orientation;
31 |
32 | @SuppressLint("ViewConstructor")
33 | public class FlipLoadingLayout extends LoadingLayout {
34 |
35 | static final int FLIP_ANIMATION_DURATION = 150;
36 |
37 | private final Animation mRotateAnimation, mResetRotateAnimation;
38 |
39 | public FlipLoadingLayout(Context context, final Mode mode, final Orientation scrollDirection, TypedArray attrs) {
40 | super(context, mode, scrollDirection, attrs);
41 |
42 | final int rotateAngle = mode == Mode.PULL_FROM_START ? -180 : 180;
43 |
44 | mRotateAnimation = new RotateAnimation(0, rotateAngle, Animation.RELATIVE_TO_SELF, 0.5f,
45 | Animation.RELATIVE_TO_SELF, 0.5f);
46 | mRotateAnimation.setInterpolator(ANIMATION_INTERPOLATOR);
47 | mRotateAnimation.setDuration(FLIP_ANIMATION_DURATION);
48 | mRotateAnimation.setFillAfter(true);
49 |
50 | mResetRotateAnimation = new RotateAnimation(rotateAngle, 0, Animation.RELATIVE_TO_SELF, 0.5f,
51 | Animation.RELATIVE_TO_SELF, 0.5f);
52 | mResetRotateAnimation.setInterpolator(ANIMATION_INTERPOLATOR);
53 | mResetRotateAnimation.setDuration(FLIP_ANIMATION_DURATION);
54 | mResetRotateAnimation.setFillAfter(true);
55 | }
56 |
57 | @Override
58 | protected void onLoadingDrawableSet(Drawable imageDrawable) {
59 | if (null != imageDrawable) {
60 | final int dHeight = imageDrawable.getIntrinsicHeight();
61 | final int dWidth = imageDrawable.getIntrinsicWidth();
62 |
63 | /**
64 | * We need to set the width/height of the ImageView so that it is
65 | * square with each side the size of the largest drawable dimension.
66 | * This is so that it doesn't clip when rotated.
67 | */
68 | ViewGroup.LayoutParams lp = mHeaderImage.getLayoutParams();
69 | lp.width = lp.height = Math.max(dHeight, dWidth);
70 | mHeaderImage.requestLayout();
71 |
72 | /**
73 | * We now rotate the Drawable so that is at the correct rotation,
74 | * and is centered.
75 | */
76 | mHeaderImage.setScaleType(ScaleType.MATRIX);
77 | Matrix matrix = new Matrix();
78 | matrix.postTranslate((lp.width - dWidth) / 2f, (lp.height - dHeight) / 2f);
79 | matrix.postRotate(getDrawableRotationAngle(), lp.width / 2f, lp.height / 2f);
80 | mHeaderImage.setImageMatrix(matrix);
81 | }
82 | }
83 |
84 | @Override
85 | protected void onPullImpl(float scaleOfLayout) {
86 | // NO-OP
87 | }
88 |
89 | @Override
90 | protected void pullToRefreshImpl() {
91 | // Only start reset Animation, we've previously show the rotate anim
92 | if (mRotateAnimation == mHeaderImage.getAnimation()) {
93 | mHeaderImage.startAnimation(mResetRotateAnimation);
94 | }
95 | }
96 |
97 | @Override
98 | protected void refreshingImpl() {
99 | mHeaderImage.clearAnimation();
100 | mHeaderImage.setVisibility(View.INVISIBLE);
101 | mHeaderProgress.setVisibility(View.VISIBLE);
102 | }
103 |
104 | @Override
105 | protected void releaseToRefreshImpl() {
106 | mHeaderImage.startAnimation(mRotateAnimation);
107 | }
108 |
109 | @Override
110 | protected void resetImpl() {
111 | mHeaderImage.clearAnimation();
112 | mHeaderProgress.setVisibility(View.GONE);
113 | mHeaderImage.setVisibility(View.VISIBLE);
114 | }
115 |
116 | @Override
117 | protected int getDefaultDrawableResId() {
118 | return R.drawable.default_ptr_flip;
119 | }
120 |
121 | private float getDrawableRotationAngle() {
122 | float angle = 0f;
123 | switch (mMode) {
124 | case PULL_FROM_END:
125 | if (mScrollDirection == Orientation.HORIZONTAL) {
126 | angle = 90f;
127 | } else {
128 | angle = 180f;
129 | }
130 | break;
131 |
132 | case PULL_FROM_START:
133 | if (mScrollDirection == Orientation.HORIZONTAL) {
134 | angle = 270f;
135 | }
136 | break;
137 |
138 | default:
139 | break;
140 | }
141 |
142 | return angle;
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/internal/IndicatorLayout.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library.internal;
17 |
18 | import android.annotation.SuppressLint;
19 | import android.content.Context;
20 | import android.graphics.Matrix;
21 | import android.graphics.drawable.Drawable;
22 | import android.view.View;
23 | import android.view.animation.*;
24 | import android.view.animation.Animation.AnimationListener;
25 | import android.widget.FrameLayout;
26 | import android.widget.ImageView;
27 | import android.widget.ImageView.ScaleType;
28 | import com.bulletnoid.android.widget.StaggeredGridViewDemo.R;
29 | import com.handmark.pulltorefresh.library.PullToRefreshBase;
30 |
31 | @SuppressLint("ViewConstructor")
32 | public class IndicatorLayout extends FrameLayout implements AnimationListener {
33 |
34 | static final int DEFAULT_ROTATION_ANIMATION_DURATION = 150;
35 |
36 | private Animation mInAnim, mOutAnim;
37 | private ImageView mArrowImageView;
38 |
39 | private final Animation mRotateAnimation, mResetRotateAnimation;
40 |
41 | public IndicatorLayout(Context context, PullToRefreshBase.Mode mode) {
42 | super(context);
43 | mArrowImageView = new ImageView(context);
44 |
45 | Drawable arrowD = getResources().getDrawable(R.drawable.indicator_arrow);
46 | mArrowImageView.setImageDrawable(arrowD);
47 |
48 | final int padding = getResources().getDimensionPixelSize(R.dimen.indicator_internal_padding);
49 | mArrowImageView.setPadding(padding, padding, padding, padding);
50 | addView(mArrowImageView);
51 |
52 | int inAnimResId, outAnimResId;
53 | switch (mode) {
54 | case PULL_FROM_END:
55 | inAnimResId = R.anim.slide_in_from_bottom;
56 | outAnimResId = R.anim.slide_out_to_bottom;
57 | setBackgroundResource(R.drawable.indicator_bg_bottom);
58 |
59 | // Rotate Arrow so it's pointing the correct way
60 | mArrowImageView.setScaleType(ScaleType.MATRIX);
61 | Matrix matrix = new Matrix();
62 | matrix.setRotate(180f, arrowD.getIntrinsicWidth() / 2f, arrowD.getIntrinsicHeight() / 2f);
63 | mArrowImageView.setImageMatrix(matrix);
64 | break;
65 | default:
66 | case PULL_FROM_START:
67 | inAnimResId = R.anim.slide_in_from_top;
68 | outAnimResId = R.anim.slide_out_to_top;
69 | setBackgroundResource(R.drawable.indicator_bg_top);
70 | break;
71 | }
72 |
73 | mInAnim = AnimationUtils.loadAnimation(context, inAnimResId);
74 | mInAnim.setAnimationListener(this);
75 |
76 | mOutAnim = AnimationUtils.loadAnimation(context, outAnimResId);
77 | mOutAnim.setAnimationListener(this);
78 |
79 | final Interpolator interpolator = new LinearInterpolator();
80 | mRotateAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
81 | 0.5f);
82 | mRotateAnimation.setInterpolator(interpolator);
83 | mRotateAnimation.setDuration(DEFAULT_ROTATION_ANIMATION_DURATION);
84 | mRotateAnimation.setFillAfter(true);
85 |
86 | mResetRotateAnimation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f,
87 | Animation.RELATIVE_TO_SELF, 0.5f);
88 | mResetRotateAnimation.setInterpolator(interpolator);
89 | mResetRotateAnimation.setDuration(DEFAULT_ROTATION_ANIMATION_DURATION);
90 | mResetRotateAnimation.setFillAfter(true);
91 |
92 | }
93 |
94 | public final boolean isVisible() {
95 | Animation currentAnim = getAnimation();
96 | if (null != currentAnim) {
97 | return mInAnim == currentAnim;
98 | }
99 |
100 | return getVisibility() == View.VISIBLE;
101 | }
102 |
103 | public void hide() {
104 | startAnimation(mOutAnim);
105 | }
106 |
107 | public void show() {
108 | mArrowImageView.clearAnimation();
109 | startAnimation(mInAnim);
110 | }
111 |
112 | @Override
113 | public void onAnimationEnd(Animation animation) {
114 | if (animation == mOutAnim) {
115 | mArrowImageView.clearAnimation();
116 | setVisibility(View.GONE);
117 | } else if (animation == mInAnim) {
118 | setVisibility(View.VISIBLE);
119 | }
120 |
121 | clearAnimation();
122 | }
123 |
124 | @Override
125 | public void onAnimationRepeat(Animation animation) {
126 | // NO-OP
127 | }
128 |
129 | @Override
130 | public void onAnimationStart(Animation animation) {
131 | setVisibility(View.VISIBLE);
132 | }
133 |
134 | public void releaseToRefresh() {
135 | mArrowImageView.startAnimation(mRotateAnimation);
136 | }
137 |
138 | public void pullToRefresh() {
139 | mArrowImageView.startAnimation(mResetRotateAnimation);
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/internal/LoadingLayout.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library.internal;
17 |
18 | import android.annotation.SuppressLint;
19 | import android.content.Context;
20 | import android.content.res.ColorStateList;
21 | import android.content.res.TypedArray;
22 | import android.graphics.Typeface;
23 | import android.graphics.drawable.AnimationDrawable;
24 | import android.graphics.drawable.Drawable;
25 | import android.text.TextUtils;
26 | import android.util.TypedValue;
27 | import android.view.Gravity;
28 | import android.view.LayoutInflater;
29 | import android.view.View;
30 | import android.view.ViewGroup;
31 | import android.view.animation.Interpolator;
32 | import android.view.animation.LinearInterpolator;
33 | import android.widget.FrameLayout;
34 | import android.widget.ImageView;
35 | import android.widget.ProgressBar;
36 | import android.widget.TextView;
37 | import com.bulletnoid.android.widget.StaggeredGridViewDemo.R;
38 | import com.handmark.pulltorefresh.library.ILoadingLayout;
39 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode;
40 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Orientation;
41 |
42 | @SuppressLint("ViewConstructor")
43 | public abstract class LoadingLayout extends FrameLayout implements ILoadingLayout {
44 |
45 | static final String LOG_TAG = "PullToRefresh-LoadingLayout";
46 |
47 | static final Interpolator ANIMATION_INTERPOLATOR = new LinearInterpolator();
48 |
49 | private FrameLayout mInnerLayout;
50 |
51 | protected final ImageView mHeaderImage;
52 | protected final ProgressBar mHeaderProgress;
53 |
54 | private boolean mUseIntrinsicAnimation;
55 |
56 | private final TextView mHeaderText;
57 | private final TextView mSubHeaderText;
58 |
59 | protected final Mode mMode;
60 | protected final Orientation mScrollDirection;
61 |
62 | private CharSequence mPullLabel;
63 | private CharSequence mRefreshingLabel;
64 | private CharSequence mReleaseLabel;
65 |
66 | public LoadingLayout(Context context, final Mode mode, final Orientation scrollDirection, TypedArray attrs) {
67 | super(context);
68 | mMode = mode;
69 | mScrollDirection = scrollDirection;
70 |
71 | switch (scrollDirection) {
72 | case HORIZONTAL:
73 | LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_header_horizontal, this);
74 | break;
75 | case VERTICAL:
76 | default:
77 | LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_header_vertical, this);
78 | break;
79 | }
80 |
81 | mInnerLayout = (FrameLayout) findViewById(R.id.fl_inner);
82 | mHeaderText = (TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_text);
83 | mHeaderProgress = (ProgressBar) mInnerLayout.findViewById(R.id.pull_to_refresh_progress);
84 | mSubHeaderText = (TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_sub_text);
85 | mHeaderImage = (ImageView) mInnerLayout.findViewById(R.id.pull_to_refresh_image);
86 |
87 | LayoutParams lp = (LayoutParams) mInnerLayout.getLayoutParams();
88 |
89 | switch (mode) {
90 | case PULL_FROM_END:
91 | lp.gravity = scrollDirection == Orientation.VERTICAL ? Gravity.TOP : Gravity.LEFT;
92 |
93 | // Load in labels
94 | mPullLabel = context.getString(R.string.pull_to_refresh_from_bottom_pull_label);
95 | mRefreshingLabel = context.getString(R.string.pull_to_refresh_from_bottom_refreshing_label);
96 | mReleaseLabel = context.getString(R.string.pull_to_refresh_from_bottom_release_label);
97 | break;
98 |
99 | case PULL_FROM_START:
100 | default:
101 | lp.gravity = scrollDirection == Orientation.VERTICAL ? Gravity.BOTTOM : Gravity.RIGHT;
102 |
103 | // Load in labels
104 | mPullLabel = context.getString(R.string.pull_to_refresh_pull_label);
105 | mRefreshingLabel = context.getString(R.string.pull_to_refresh_refreshing_label);
106 | mReleaseLabel = context.getString(R.string.pull_to_refresh_release_label);
107 | break;
108 | }
109 |
110 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderBackground)) {
111 | Drawable background = attrs.getDrawable(R.styleable.PullToRefresh_ptrHeaderBackground);
112 | if (null != background) {
113 | ViewCompat.setBackground(this, background);
114 | }
115 | }
116 |
117 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderTextAppearance)) {
118 | TypedValue styleID = new TypedValue();
119 | attrs.getValue(R.styleable.PullToRefresh_ptrHeaderTextAppearance, styleID);
120 | setTextAppearance(styleID.data);
121 | }
122 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrSubHeaderTextAppearance)) {
123 | TypedValue styleID = new TypedValue();
124 | attrs.getValue(R.styleable.PullToRefresh_ptrSubHeaderTextAppearance, styleID);
125 | setSubTextAppearance(styleID.data);
126 | }
127 |
128 | // Text Color attrs need to be set after TextAppearance attrs
129 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderTextColor)) {
130 | ColorStateList colors = attrs.getColorStateList(R.styleable.PullToRefresh_ptrHeaderTextColor);
131 | if (null != colors) {
132 | setTextColor(colors);
133 | }
134 | }
135 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrHeaderSubTextColor)) {
136 | ColorStateList colors = attrs.getColorStateList(R.styleable.PullToRefresh_ptrHeaderSubTextColor);
137 | if (null != colors) {
138 | setSubTextColor(colors);
139 | }
140 | }
141 |
142 | // Try and get defined drawable from Attrs
143 | Drawable imageDrawable = null;
144 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawable)) {
145 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawable);
146 | }
147 |
148 | // Check Specific Drawable from Attrs, these overrite the generic
149 | // drawable attr above
150 | switch (mode) {
151 | case PULL_FROM_START:
152 | default:
153 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableStart)) {
154 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableStart);
155 | } else if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableTop)) {
156 | Utils.warnDeprecation("ptrDrawableTop", "ptrDrawableStart");
157 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableTop);
158 | }
159 | break;
160 |
161 | case PULL_FROM_END:
162 | if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableEnd)) {
163 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableEnd);
164 | } else if (attrs.hasValue(R.styleable.PullToRefresh_ptrDrawableBottom)) {
165 | Utils.warnDeprecation("ptrDrawableBottom", "ptrDrawableEnd");
166 | imageDrawable = attrs.getDrawable(R.styleable.PullToRefresh_ptrDrawableBottom);
167 | }
168 | break;
169 | }
170 |
171 | // If we don't have a user defined drawable, load the default
172 | if (null == imageDrawable) {
173 | imageDrawable = context.getResources().getDrawable(getDefaultDrawableResId());
174 | }
175 |
176 | // Set Drawable, and save width/height
177 | setLoadingDrawable(imageDrawable);
178 |
179 | reset();
180 | }
181 |
182 | public final void setHeight(int height) {
183 | ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) getLayoutParams();
184 | lp.height = height;
185 | requestLayout();
186 | }
187 |
188 | public final void setWidth(int width) {
189 | ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) getLayoutParams();
190 | lp.width = width;
191 | requestLayout();
192 | }
193 |
194 | public final int getContentSize() {
195 | switch (mScrollDirection) {
196 | case HORIZONTAL:
197 | return mInnerLayout.getWidth();
198 | case VERTICAL:
199 | default:
200 | return mInnerLayout.getHeight();
201 | }
202 | }
203 |
204 | public final void hideAllViews() {
205 | if (View.VISIBLE == mHeaderText.getVisibility()) {
206 | mHeaderText.setVisibility(View.INVISIBLE);
207 | }
208 | if (View.VISIBLE == mHeaderProgress.getVisibility()) {
209 | mHeaderProgress.setVisibility(View.INVISIBLE);
210 | }
211 | if (View.VISIBLE == mHeaderImage.getVisibility()) {
212 | mHeaderImage.setVisibility(View.INVISIBLE);
213 | }
214 | if (View.VISIBLE == mSubHeaderText.getVisibility()) {
215 | mSubHeaderText.setVisibility(View.INVISIBLE);
216 | }
217 | }
218 |
219 | public final void onPull(float scaleOfLayout) {
220 | if (!mUseIntrinsicAnimation) {
221 | onPullImpl(scaleOfLayout);
222 | }
223 | }
224 |
225 | public final void pullToRefresh() {
226 | if (null != mHeaderText) {
227 | mHeaderText.setText(mPullLabel);
228 | }
229 |
230 | // Now call the callback
231 | pullToRefreshImpl();
232 | }
233 |
234 | public final void refreshing() {
235 | if (null != mHeaderText) {
236 | mHeaderText.setText(mRefreshingLabel);
237 | }
238 |
239 | if (mUseIntrinsicAnimation) {
240 | ((AnimationDrawable) mHeaderImage.getDrawable()).start();
241 | } else {
242 | // Now call the callback
243 | refreshingImpl();
244 | }
245 |
246 | if (null != mSubHeaderText) {
247 | mSubHeaderText.setVisibility(View.GONE);
248 | }
249 | }
250 |
251 | public final void releaseToRefresh() {
252 | if (null != mHeaderText) {
253 | mHeaderText.setText(mReleaseLabel);
254 | }
255 |
256 | // Now call the callback
257 | releaseToRefreshImpl();
258 | }
259 |
260 | public final void reset() {
261 | if (null != mHeaderText) {
262 | mHeaderText.setText(mPullLabel);
263 | }
264 | mHeaderImage.setVisibility(View.VISIBLE);
265 |
266 | if (mUseIntrinsicAnimation) {
267 | ((AnimationDrawable) mHeaderImage.getDrawable()).stop();
268 | } else {
269 | // Now call the callback
270 | resetImpl();
271 | }
272 |
273 | if (null != mSubHeaderText) {
274 | if (TextUtils.isEmpty(mSubHeaderText.getText())) {
275 | mSubHeaderText.setVisibility(View.GONE);
276 | } else {
277 | mSubHeaderText.setVisibility(View.VISIBLE);
278 | }
279 | }
280 | }
281 |
282 | @Override
283 | public void setLastUpdatedLabel(CharSequence label) {
284 | setSubHeaderText(label);
285 | }
286 |
287 | public final void setLoadingDrawable(Drawable imageDrawable) {
288 | // Set Drawable
289 | mHeaderImage.setImageDrawable(imageDrawable);
290 | mUseIntrinsicAnimation = (imageDrawable instanceof AnimationDrawable);
291 |
292 | // Now call the callback
293 | onLoadingDrawableSet(imageDrawable);
294 | }
295 |
296 | public void setPullLabel(CharSequence pullLabel) {
297 | mPullLabel = pullLabel;
298 | }
299 |
300 | public void setRefreshingLabel(CharSequence refreshingLabel) {
301 | mRefreshingLabel = refreshingLabel;
302 | }
303 |
304 | public void setReleaseLabel(CharSequence releaseLabel) {
305 | mReleaseLabel = releaseLabel;
306 | }
307 |
308 | @Override
309 | public void setTextTypeface(Typeface tf) {
310 | mHeaderText.setTypeface(tf);
311 | }
312 |
313 | public final void showInvisibleViews() {
314 | if (View.INVISIBLE == mHeaderText.getVisibility()) {
315 | mHeaderText.setVisibility(View.VISIBLE);
316 | }
317 | if (View.INVISIBLE == mHeaderProgress.getVisibility()) {
318 | mHeaderProgress.setVisibility(View.VISIBLE);
319 | }
320 | if (View.INVISIBLE == mHeaderImage.getVisibility()) {
321 | mHeaderImage.setVisibility(View.VISIBLE);
322 | }
323 | if (View.INVISIBLE == mSubHeaderText.getVisibility()) {
324 | mSubHeaderText.setVisibility(View.VISIBLE);
325 | }
326 | }
327 |
328 | /**
329 | * Callbacks for derivative Layouts
330 | */
331 |
332 | protected abstract int getDefaultDrawableResId();
333 |
334 | protected abstract void onLoadingDrawableSet(Drawable imageDrawable);
335 |
336 | protected abstract void onPullImpl(float scaleOfLayout);
337 |
338 | protected abstract void pullToRefreshImpl();
339 |
340 | protected abstract void refreshingImpl();
341 |
342 | protected abstract void releaseToRefreshImpl();
343 |
344 | protected abstract void resetImpl();
345 |
346 | private void setSubHeaderText(CharSequence label) {
347 | if (null != mSubHeaderText) {
348 | if (TextUtils.isEmpty(label)) {
349 | mSubHeaderText.setVisibility(View.GONE);
350 | } else {
351 | mSubHeaderText.setText(label);
352 |
353 | // Only set it to Visible if we're GONE, otherwise VISIBLE will
354 | // be set soon
355 | if (View.GONE == mSubHeaderText.getVisibility()) {
356 | mSubHeaderText.setVisibility(View.VISIBLE);
357 | }
358 | }
359 | }
360 | }
361 |
362 | private void setSubTextAppearance(int value) {
363 | if (null != mSubHeaderText) {
364 | mSubHeaderText.setTextAppearance(getContext(), value);
365 | }
366 | }
367 |
368 | private void setSubTextColor(ColorStateList color) {
369 | if (null != mSubHeaderText) {
370 | mSubHeaderText.setTextColor(color);
371 | }
372 | }
373 |
374 | private void setTextAppearance(int value) {
375 | if (null != mHeaderText) {
376 | mHeaderText.setTextAppearance(getContext(), value);
377 | }
378 | if (null != mSubHeaderText) {
379 | mSubHeaderText.setTextAppearance(getContext(), value);
380 | }
381 | }
382 |
383 | private void setTextColor(ColorStateList color) {
384 | if (null != mHeaderText) {
385 | mHeaderText.setTextColor(color);
386 | }
387 | if (null != mSubHeaderText) {
388 | mSubHeaderText.setTextColor(color);
389 | }
390 | }
391 |
392 | }
393 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/internal/RotateLoadingLayout.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library.internal;
17 |
18 | import android.content.Context;
19 | import android.content.res.TypedArray;
20 | import android.graphics.Matrix;
21 | import android.graphics.drawable.Drawable;
22 | import android.view.animation.Animation;
23 | import android.view.animation.RotateAnimation;
24 | import android.widget.ImageView.ScaleType;
25 | import com.bulletnoid.android.widget.StaggeredGridViewDemo.R;
26 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode;
27 | import com.handmark.pulltorefresh.library.PullToRefreshBase.Orientation;
28 |
29 | public class RotateLoadingLayout extends LoadingLayout {
30 |
31 | static final int ROTATION_ANIMATION_DURATION = 1200;
32 |
33 | private final Animation mRotateAnimation;
34 | private final Matrix mHeaderImageMatrix;
35 |
36 | private float mRotationPivotX, mRotationPivotY;
37 |
38 | private final boolean mRotateDrawableWhilePulling;
39 |
40 | public RotateLoadingLayout(Context context, Mode mode, Orientation scrollDirection, TypedArray attrs) {
41 | super(context, mode, scrollDirection, attrs);
42 |
43 | mRotateDrawableWhilePulling = attrs.getBoolean(R.styleable.PullToRefresh_ptrRotateDrawableWhilePulling, true);
44 |
45 | mHeaderImage.setScaleType(ScaleType.MATRIX);
46 | mHeaderImageMatrix = new Matrix();
47 | mHeaderImage.setImageMatrix(mHeaderImageMatrix);
48 |
49 | mRotateAnimation = new RotateAnimation(0, 720, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
50 | 0.5f);
51 | mRotateAnimation.setInterpolator(ANIMATION_INTERPOLATOR);
52 | mRotateAnimation.setDuration(ROTATION_ANIMATION_DURATION);
53 | mRotateAnimation.setRepeatCount(Animation.INFINITE);
54 | mRotateAnimation.setRepeatMode(Animation.RESTART);
55 | }
56 |
57 | public void onLoadingDrawableSet(Drawable imageDrawable) {
58 | if (null != imageDrawable) {
59 | mRotationPivotX = Math.round(imageDrawable.getIntrinsicWidth() / 2f);
60 | mRotationPivotY = Math.round(imageDrawable.getIntrinsicHeight() / 2f);
61 | }
62 | }
63 |
64 | protected void onPullImpl(float scaleOfLayout) {
65 | float angle;
66 | if (mRotateDrawableWhilePulling) {
67 | angle = scaleOfLayout * 90f;
68 | } else {
69 | angle = Math.max(0f, Math.min(180f, scaleOfLayout * 360f - 180f));
70 | }
71 |
72 | mHeaderImageMatrix.setRotate(angle, mRotationPivotX, mRotationPivotY);
73 | mHeaderImage.setImageMatrix(mHeaderImageMatrix);
74 | }
75 |
76 | @Override
77 | protected void refreshingImpl() {
78 | mHeaderImage.startAnimation(mRotateAnimation);
79 | }
80 |
81 | @Override
82 | protected void resetImpl() {
83 | mHeaderImage.clearAnimation();
84 | resetImageRotation();
85 | }
86 |
87 | private void resetImageRotation() {
88 | if (null != mHeaderImageMatrix) {
89 | mHeaderImageMatrix.reset();
90 | mHeaderImage.setImageMatrix(mHeaderImageMatrix);
91 | }
92 | }
93 |
94 | @Override
95 | protected void pullToRefreshImpl() {
96 | // NO-OP
97 | }
98 |
99 | @Override
100 | protected void releaseToRefreshImpl() {
101 | // NO-OP
102 | }
103 |
104 | @Override
105 | protected int getDefaultDrawableResId() {
106 | return R.drawable.default_ptr_rotate;
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/internal/Utils.java:
--------------------------------------------------------------------------------
1 | package com.handmark.pulltorefresh.library.internal;
2 |
3 | import android.util.Log;
4 |
5 | public class Utils {
6 |
7 | static final String LOG_TAG = "PullToRefresh";
8 |
9 | public static void warnDeprecation(String depreacted, String replacement) {
10 | Log.w(LOG_TAG, "You're using the deprecated " + depreacted + " attr, please switch over to " + replacement);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/com/handmark/pulltorefresh/library/internal/ViewCompat.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011, 2012 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *******************************************************************************/
16 | package com.handmark.pulltorefresh.library.internal;
17 |
18 | import android.annotation.TargetApi;
19 | import android.graphics.drawable.Drawable;
20 | import android.os.Build.VERSION;
21 | import android.os.Build.VERSION_CODES;
22 | import android.view.View;
23 |
24 | @SuppressWarnings("deprecation")
25 | public class ViewCompat {
26 |
27 | public static void postOnAnimation(View view, Runnable runnable) {
28 | if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
29 | SDK16.postOnAnimation(view, runnable);
30 | } else {
31 | view.postDelayed(runnable, 16);
32 | }
33 | }
34 |
35 | public static void setBackground(View view, Drawable background) {
36 | if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
37 | SDK16.setBackground(view, background);
38 | } else {
39 | view.setBackgroundDrawable(background);
40 | }
41 | }
42 |
43 | public static void setLayerType(View view, int layerType) {
44 | if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
45 | SDK11.setLayerType(view, layerType);
46 | }
47 | }
48 |
49 | @TargetApi(11)
50 | static class SDK11 {
51 |
52 | public static void setLayerType(View view, int layerType) {
53 | view.setLayerType(layerType, null);
54 | }
55 | }
56 |
57 | @TargetApi(16)
58 | static class SDK16 {
59 |
60 | public static void postOnAnimation(View view, Runnable runnable) {
61 | view.postOnAnimation(runnable);
62 | }
63 |
64 | public static void setBackground(View view, Drawable background) {
65 | view.setBackground(background);
66 | }
67 |
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------