├── Sample
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── item_custom_stick.xml
│ │ │ │ ├── activity_custom_stick.xml
│ │ │ │ ├── activity_custom.xml
│ │ │ │ └── item_custom.xml
│ │ │ └── values-w820dp
│ │ │ │ └── dimens.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── zly
│ │ │ │ └── www
│ │ │ │ └── simple
│ │ │ │ ├── customdecoration
│ │ │ │ ├── CityBean.java
│ │ │ │ ├── CustomDecorationAdapter.java
│ │ │ │ └── CustomDecorationActivity.java
│ │ │ │ ├── AppApplication.java
│ │ │ │ ├── adapter
│ │ │ │ ├── CustomAdapter.java
│ │ │ │ └── CustomDecorationAdapter.java
│ │ │ │ ├── DisplayUtil.java
│ │ │ │ ├── customall
│ │ │ │ └── CustomAllActivity.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── customheader
│ │ │ │ └── CustomHeaderActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── zly
│ │ │ └── www
│ │ │ └── simple
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── zly
│ │ └── www
│ │ └── simple
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── ErvLibrary
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── ids.xml
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── attrs.xml
│ │ │ │ ├── styles.xml
│ │ │ │ ├── cube_ptr_string.xml
│ │ │ │ └── cube_ptr_attrs.xml
│ │ │ ├── drawable-xxhdpi
│ │ │ │ └── icon_erv_emply.jpg
│ │ │ ├── drawable-xhdpi
│ │ │ │ └── ptr_rotate_arrow.png
│ │ │ └── layout
│ │ │ │ ├── cube_ptr_simple_loading.xml
│ │ │ │ ├── erv_default_empty.xml
│ │ │ │ ├── erv_default_footer.xml
│ │ │ │ ├── erv_layout.xml
│ │ │ │ └── cube_ptr_classic_default_header.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── zly
│ │ │ │ └── www
│ │ │ │ └── easyrecyclerview
│ │ │ │ ├── listener
│ │ │ │ ├── OnLoadListener.java
│ │ │ │ ├── OnRefreshListener.java
│ │ │ │ └── ItemClickSupport.java
│ │ │ │ ├── adapter
│ │ │ │ ├── viewholder
│ │ │ │ │ ├── FooterViewHolder.java
│ │ │ │ │ └── BaseViewHolder.java
│ │ │ │ ├── MultipleAdapter.java
│ │ │ │ └── CommonAdapter.java
│ │ │ │ ├── ptrlib
│ │ │ │ ├── PtrHandler.java
│ │ │ │ ├── PtrUIHandler.java
│ │ │ │ ├── PtrDefaultHandler.java
│ │ │ │ ├── PtrUIHandlerHook.java
│ │ │ │ ├── PtrClassicFrameLayout.java
│ │ │ │ ├── util
│ │ │ │ │ ├── PtrLocalDisplay.java
│ │ │ │ │ └── PtrCLog.java
│ │ │ │ ├── header
│ │ │ │ │ ├── StoreHouseBarItem.java
│ │ │ │ │ ├── MaterialHeader.java
│ │ │ │ │ ├── StoreHouseHeader.java
│ │ │ │ │ ├── StoreHousePath.java
│ │ │ │ │ └── MaterialProgressDrawable.java
│ │ │ │ ├── indicator
│ │ │ │ │ ├── PtrTensionIndicator.java
│ │ │ │ │ └── PtrIndicator.java
│ │ │ │ ├── PtrUIHandlerHolder.java
│ │ │ │ └── PtrClassicDefaultHeader.java
│ │ │ │ ├── footer
│ │ │ │ ├── ErvLoadUIHandle.java
│ │ │ │ └── ErvDefaultFooter.java
│ │ │ │ ├── utils
│ │ │ │ ├── DisplayUtil.java
│ │ │ │ └── LayoutManagerUtil.java
│ │ │ │ ├── EasyDefRecyclerView.java
│ │ │ │ ├── decoration
│ │ │ │ └── StickItemDecoration.java
│ │ │ │ └── EasyRecyclerView.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── zly
│ │ │ └── www
│ │ │ └── easyrecyclerview
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── zly
│ │ └── www
│ │ └── easyrecyclerview
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── gradlew
└── README.md
/Sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/ErvLibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':ErvLibrary', ':Sample'
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RocketZLY/EasyRecyclerView/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/Sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RocketZLY/EasyRecyclerView/HEAD/Sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RocketZLY/EasyRecyclerView/HEAD/Sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RocketZLY/EasyRecyclerView/HEAD/Sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RocketZLY/EasyRecyclerView/HEAD/Sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RocketZLY/EasyRecyclerView/HEAD/Sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/drawable-xxhdpi/icon_erv_emply.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RocketZLY/EasyRecyclerView/HEAD/ErvLibrary/src/main/res/drawable-xxhdpi/icon_erv_emply.jpg
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/drawable-xhdpi/ptr_rotate_arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RocketZLY/EasyRecyclerView/HEAD/ErvLibrary/src/main/res/drawable-xhdpi/ptr_rotate_arrow.png
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | EasyRecyclerView
3 |
4 | 加载中...
5 | 暂无更多数据
6 |
7 |
--------------------------------------------------------------------------------
/Sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/Sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/Sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Simple
3 |
4 |
5 | 定制头部
6 | 定制全部
7 | 定制装饰
8 |
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/listener/OnLoadListener.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.listener;
2 |
3 | /**
4 | * 加载更多
5 | * Created by zly on 2016/10/27 0027.
6 | */
7 |
8 | public interface OnLoadListener {
9 |
10 | void onLoadListener();
11 | }
12 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/listener/OnRefreshListener.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.listener;
2 |
3 | /**
4 | * 刷新
5 | * Created by zly on 2016/10/24 0024.
6 | */
7 |
8 | public interface OnRefreshListener {
9 |
10 | void onRefreshListener();
11 | }
12 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/Sample/src/main/res/layout/item_custom_stick.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/layout/cube_ptr_simple_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/layout/erv_default_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/Sample/src/main/java/com/zly/www/simple/customdecoration/CityBean.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple.customdecoration;
2 |
3 | /**
4 | * Created by zly on 2016/11/28 0028.
5 | */
6 |
7 | public class CityBean {
8 |
9 | public String tag;
10 | public String name;
11 |
12 | public CityBean(String tag, String name) {
13 | this.tag = tag;
14 | this.name = name;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sample/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/adapter/viewholder/FooterViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.adapter.viewholder;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Created by zly on 2016/10/31 0031.
7 | */
8 |
9 | public class FooterViewHolder extends BaseViewHolder {
10 |
11 | public FooterViewHolder(View itemView) {
12 | super(itemView);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/layout/erv_default_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/Sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/adapter/viewholder/BaseViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.adapter.viewholder;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by zly on 2016/10/31 0031.
8 | */
9 |
10 | public class BaseViewHolder extends RecyclerView.ViewHolder {
11 |
12 | public BaseViewHolder(View itemView) {
13 | super(itemView);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sample/src/test/java/com/zly/www/simple/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/Sample/src/main/res/layout/activity_custom_stick.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/ErvLibrary/src/test/java/com/zly/www/easyrecyclerview/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/Sample/src/main/java/com/zly/www/simple/AppApplication.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple;
2 |
3 | import android.app.Application;
4 |
5 | /**
6 | * Created by zly on 2016/12/2 0002.
7 | */
8 |
9 | public class AppApplication extends Application {
10 |
11 | private static AppApplication mApplication;
12 |
13 | @Override
14 | public void onCreate() {
15 | super.onCreate();
16 | mApplication = this;
17 | }
18 |
19 | public static AppApplication getInstance(){
20 | return mApplication;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sample/src/main/res/layout/activity_custom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/PtrHandler.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib;
2 |
3 | import android.view.View;
4 |
5 | public interface PtrHandler {
6 |
7 | /**
8 | * Check can do refresh or not. For example the content is empty or the first child is in view.
9 | *
10 | */
11 | public boolean checkCanDoRefresh(final PtrFrameLayout frame, final View content, final View header);
12 |
13 | /**
14 | * When refresh begin
15 | *
16 | * @param frame
17 | */
18 | public void onRefreshBegin(final PtrFrameLayout frame);
19 | }
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/values/cube_ptr_string.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 下拉刷新
5 | 下拉刷新
6 | 释放刷新
7 | 刷新中...
8 | 刷新完成.
9 |
10 | 最后更新时间:
11 | 秒前
12 | 分钟前
13 | 小时前
14 |
15 |
--------------------------------------------------------------------------------
/Sample/src/main/res/layout/item_custom.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
--------------------------------------------------------------------------------
/Sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/ErvLibrary/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/footer/ErvLoadUIHandle.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.footer;
2 |
3 | import com.zly.www.easyrecyclerview.listener.OnLoadListener;
4 |
5 | /**
6 | * 刷新ui操作
7 | * Created by zly on 2016/10/24 0024.
8 | */
9 |
10 | public interface ErvLoadUIHandle {
11 |
12 | /**
13 | * 允许加载更多
14 | */
15 | int LOAD = 1;
16 |
17 | /**
18 | * 暂无更多数据
19 | */
20 | int NOMORE = 2;
21 |
22 | /**
23 | * 加载失败
24 | */
25 | int LOADFAIL = 3;
26 |
27 | /**
28 | * @return 获取底部当前状态
29 | */
30 | int getState();
31 |
32 | void onLoading();
33 |
34 | void onNoMore();
35 |
36 | void onLoadFail(OnLoadListener listener);
37 |
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | ### Android template
10 | # Built application files
11 | *.apk
12 | *.ap_
13 |
14 | # Files for the ART/Dalvik VM
15 | *.dex
16 |
17 | # Java class files
18 | *.class
19 |
20 | # Generated files
21 | bin/
22 | gen/
23 | out/
24 |
25 | # Gradle files
26 | .gradle/
27 | build/
28 |
29 | # Local configuration file (sdk path, etc)
30 | local.properties
31 |
32 | # Proguard folder generated by Eclipse
33 | proguard/
34 |
35 | # Log Files
36 | *.log
37 |
38 | # Android Studio Navigation editor temp files
39 | .navigation/
40 |
41 | # Android Studio captures folder
42 | captures/
43 |
44 |
45 |
46 | # Keystore files
47 | *.jks
48 |
49 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/adapter/MultipleAdapter.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.adapter;
2 |
3 | import com.zly.www.easyrecyclerview.adapter.viewholder.BaseViewHolder;
4 |
5 | /**
6 | * 多条目adapter
7 | * Created by zly on 2016/10/31 0031.
8 | */
9 |
10 | public abstract class MultipleAdapter extends CommonAdapter {
11 |
12 | @Override
13 | public int getItemViewType(int position) {
14 | if(getFooter() != null && getItemCount() - 1 == position){
15 | return TYPE_FOOTER;
16 | }
17 |
18 | if(customItemViewType(position) == TYPE_FOOTER){
19 | throw new RuntimeException("customItemViewType(int position) 返回值不能为"+TYPE_FOOTER);
20 | }
21 | return customItemViewType(position);
22 | }
23 |
24 | public abstract int customItemViewType(int position);
25 | }
26 |
--------------------------------------------------------------------------------
/Sample/src/androidTest/java/com/zly/www/simple/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.zly.www.simple", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ErvLibrary/src/androidTest/java/com/zly/www/easyrecyclerview/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.zly.www.easyrecyclerview", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/layout/erv_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
15 |
16 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/PtrUIHandler.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib;
2 |
3 |
4 | import com.zly.www.easyrecyclerview.ptrlib.indicator.PtrIndicator;
5 |
6 | /**
7 | *
8 | */
9 | public interface PtrUIHandler {
10 |
11 | /**
12 | * When the content view has reached top and refresh has been completed, view will be reset.
13 | *
14 | * @param frame
15 | */
16 | public void onUIReset(PtrFrameLayout frame);
17 |
18 | /**
19 | * prepare for loading
20 | *
21 | * @param frame
22 | */
23 | public void onUIRefreshPrepare(PtrFrameLayout frame);
24 |
25 | /**
26 | * perform refreshing UI
27 | */
28 | public void onUIRefreshBegin(PtrFrameLayout frame);
29 |
30 | /**
31 | * perform UI after refresh
32 | */
33 | public void onUIRefreshComplete(PtrFrameLayout frame);
34 |
35 | public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator);
36 | }
37 |
--------------------------------------------------------------------------------
/Sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 24
5 | buildToolsVersion "24.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.zly.www.simple"
9 | minSdkVersion 15
10 | targetSdkVersion 24
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | dependencies {
26 | compile fileTree(include: ['*.jar'], dir: 'libs')
27 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
28 | exclude group: 'com.android.support', module: 'support-annotations'
29 | })
30 | compile 'com.android.support:appcompat-v7:24.2.1'
31 | testCompile 'junit:junit:4.12'
32 | compile project(':ErvLibrary')
33 | compile 'com.jakewharton:butterknife:8.4.0'
34 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
35 | }
36 |
--------------------------------------------------------------------------------
/Sample/src/main/java/com/zly/www/simple/adapter/CustomAdapter.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple.adapter;
2 |
3 | import android.view.View;
4 | import android.view.ViewGroup;
5 | import android.widget.TextView;
6 |
7 | import com.zly.www.easyrecyclerview.adapter.CommonAdapter;
8 | import com.zly.www.easyrecyclerview.adapter.viewholder.BaseViewHolder;
9 | import com.zly.www.simple.R;
10 |
11 | import butterknife.BindView;
12 | import butterknife.ButterKnife;
13 |
14 | /**
15 | * Created by zly on 2016/10/13 0013.
16 | */
17 |
18 | public class CustomAdapter extends CommonAdapter {
19 |
20 | @Override
21 | public ViewHolder createCustomViewHolder(ViewGroup parent, int viewType) {
22 | return new ViewHolder(inflateView(R.layout.item_custom, parent));
23 | }
24 |
25 | @Override
26 | public void bindCustomViewHolder(ViewHolder holder, String s, int position) {
27 | holder.tv.setText(s);
28 | }
29 |
30 | class ViewHolder extends BaseViewHolder {
31 | @BindView(R.id.tv)
32 | TextView tv;
33 |
34 | ViewHolder(View itemView) {
35 | super(itemView);
36 | ButterKnife.bind(this,itemView);
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/values/cube_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 |
--------------------------------------------------------------------------------
/Sample/src/main/java/com/zly/www/simple/customdecoration/CustomDecorationAdapter.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple.customdecoration;
2 |
3 | import android.view.View;
4 | import android.view.ViewGroup;
5 | import android.widget.TextView;
6 |
7 | import com.zly.www.easyrecyclerview.adapter.CommonAdapter;
8 | import com.zly.www.easyrecyclerview.adapter.viewholder.BaseViewHolder;
9 | import com.zly.www.simple.R;
10 |
11 | import butterknife.BindView;
12 | import butterknife.ButterKnife;
13 |
14 | /**
15 | * Created by zly on 2016/11/30 0030.
16 | */
17 |
18 | public class CustomDecorationAdapter extends CommonAdapter {
19 |
20 |
21 | @Override
22 | public ViewHolder createCustomViewHolder(ViewGroup parent, int viewType) {
23 | return new ViewHolder(inflateView(R.layout.item_custom_stick,parent));
24 | }
25 |
26 | @Override
27 | public void bindCustomViewHolder(ViewHolder holder, CityBean cityBean, int position) {
28 | holder.tv.setText(cityBean.name);
29 | }
30 |
31 | class ViewHolder extends BaseViewHolder {
32 |
33 | @BindView(R.id.tv)
34 | TextView tv;
35 |
36 | public ViewHolder(View itemView) {
37 | super(itemView);
38 | ButterKnife.bind(this,itemView);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sample/src/main/java/com/zly/www/simple/DisplayUtil.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple;
2 |
3 | /**
4 | * dp sp px转换
5 | * Created by zly on 2016/7/28 0028.
6 | */
7 | public class DisplayUtil {
8 | /**
9 | * 将px值转换为dip或dp值,保证尺寸大小不变
10 | */
11 | public static int px2dip(float pxValue) {
12 | final float scale = AppApplication.getInstance().getResources().getDisplayMetrics().density;
13 | return (int) (pxValue / scale + 0.5f);
14 | }
15 |
16 | /**
17 | * 将dip或dp值转换为px值,保证尺寸大小不变
18 | */
19 | public static int dip2px(float dipValue) {
20 | final float scale = AppApplication.getInstance().getResources().getDisplayMetrics().density;
21 | return (int) (dipValue * scale + 0.5f);
22 | }
23 |
24 | /**
25 | * 将px值转换为sp值,保证文字大小不变
26 | */
27 | public static int px2sp(float pxValue) {
28 | final float fontScale = AppApplication.getInstance().getResources().getDisplayMetrics().scaledDensity;
29 | return (int) (pxValue / fontScale + 0.5f);
30 | }
31 |
32 | /**
33 | * 将sp值转换为px值,保证文字大小不变
34 | */
35 | public static int sp2px(float spValue) {
36 | final float fontScale = AppApplication.getInstance().getResources().getDisplayMetrics().scaledDensity;
37 | return (int) (spValue * fontScale + 0.5f);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Sample/src/main/java/com/zly/www/simple/adapter/CustomDecorationAdapter.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple.adapter;
2 |
3 | import android.view.View;
4 | import android.view.ViewGroup;
5 | import android.widget.TextView;
6 |
7 | import com.zly.www.easyrecyclerview.adapter.CommonAdapter;
8 | import com.zly.www.easyrecyclerview.adapter.viewholder.BaseViewHolder;
9 | import com.zly.www.simple.R;
10 | import com.zly.www.simple.customdecoration.CityBean;
11 |
12 | import butterknife.BindView;
13 | import butterknife.ButterKnife;
14 |
15 | /**
16 | * Created by zly on 2016/11/30 0030.
17 | */
18 |
19 | public class CustomDecorationAdapter extends CommonAdapter {
20 |
21 |
22 | @Override
23 | public ViewHolder createCustomViewHolder(ViewGroup parent, int viewType) {
24 | return new ViewHolder(inflateView(R.layout.item_custom_stick,parent));
25 | }
26 |
27 | @Override
28 | public void bindCustomViewHolder(ViewHolder holder, CityBean cityBean, int position) {
29 | holder.tv.setText(cityBean.name);
30 | }
31 |
32 | class ViewHolder extends BaseViewHolder {
33 |
34 | @BindView(R.id.tv)
35 | TextView tv;
36 |
37 | public ViewHolder(View itemView) {
38 | super(itemView);
39 | ButterKnife.bind(this,itemView);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/utils/DisplayUtil.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.utils;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * dp sp px转换
7 | * Created by zly on 2016/7/28 0028.
8 | */
9 | public class DisplayUtil {
10 | /**
11 | * 将px值转换为dip或dp值,保证尺寸大小不变
12 | */
13 | public static int px2dip(Context context, float pxValue) {
14 | final float scale = context.getResources().getDisplayMetrics().density;
15 | return (int) (pxValue / scale + 0.5f);
16 | }
17 |
18 | /**
19 | * 将dip或dp值转换为px值,保证尺寸大小不变
20 | */
21 | public static int dip2px(Context context, float dipValue) {
22 | final float scale = context.getResources().getDisplayMetrics().density;
23 | return (int) (dipValue * scale + 0.5f);
24 | }
25 |
26 | /**
27 | * 将px值转换为sp值,保证文字大小不变
28 | */
29 | public static int px2sp(Context context, float pxValue) {
30 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
31 | return (int) (pxValue / fontScale + 0.5f);
32 | }
33 |
34 | /**
35 | * 将sp值转换为px值,保证文字大小不变
36 | */
37 | public static int sp2px(Context context, float spValue) {
38 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
39 | return (int) (spValue * fontScale + 0.5f);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/ErvLibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.novoda.bintray-release'//添加
3 |
4 | android {
5 | compileSdkVersion 24
6 | buildToolsVersion "24.0.2"
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 24
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(include: ['*.jar'], dir: 'libs')
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'com.android.support:appcompat-v7:24.2.1'
28 | testCompile 'junit:junit:4.12'
29 | compile 'com.android.support:recyclerview-v7:24.2.1'
30 |
31 | }
32 |
33 |
34 | //添加
35 | publish {
36 | userOrg = 'zhuliyuan'//bintray.com用户名
37 | groupId = 'com.yysauce'//jcenter上的路径
38 | artifactId = 'easyrecyclerview'//项目名称
39 | publishVersion = '1.0.0'//版本号
40 | desc = 'EasyRecyclerView'//描述,不重要
41 | website = 'https://github.com/zhuliyuan921112/EasyRecyclerView'//网站,不重要;尽量模拟github上的地址,例如我这样的;当然你有地址最好了
42 | }
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/PtrDefaultHandler.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib;
2 |
3 | import android.view.View;
4 | import android.widget.AbsListView;
5 |
6 | public abstract class PtrDefaultHandler implements PtrHandler {
7 |
8 | public static boolean canChildScrollUp(View view) {
9 | if (android.os.Build.VERSION.SDK_INT < 14) {
10 | if (view instanceof AbsListView) {
11 | final AbsListView absListView = (AbsListView) view;
12 | return absListView.getChildCount() > 0
13 | && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
14 | .getTop() < absListView.getPaddingTop());
15 | } else {
16 | return view.getScrollY() > 0;
17 | }
18 | } else {
19 | return view.canScrollVertically(-1);
20 | }
21 | }
22 |
23 | /**
24 | * Default implement for check can perform pull to refresh
25 | *
26 | * @param frame
27 | * @param content
28 | * @param header
29 | * @return
30 | */
31 | public static boolean checkContentCanBePulledDown(PtrFrameLayout frame, View content, View header) {
32 | return !canChildScrollUp(content);
33 | }
34 |
35 | @Override
36 | public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
37 | return checkContentCanBePulledDown(frame, content, header);
38 | }
39 | }
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/PtrUIHandlerHook.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib;
2 |
3 | /**
4 | * Run a hook runnable, the runnable will run only once.
5 | * After the runnable is done, call resume to resume.
6 | * Once run, call takeover will directory call the resume action
7 | */
8 | public abstract class PtrUIHandlerHook implements Runnable {
9 |
10 | private Runnable mResumeAction;
11 | private static final byte STATUS_PREPARE = 0;
12 | private static final byte STATUS_IN_HOOK = 1;
13 | private static final byte STATUS_RESUMED = 2;
14 | private byte mStatus = STATUS_PREPARE;
15 |
16 | public void takeOver() {
17 | takeOver(null);
18 | }
19 |
20 | public void takeOver(Runnable resumeAction) {
21 | if (resumeAction != null) {
22 | mResumeAction = resumeAction;
23 | }
24 | switch (mStatus) {
25 | case STATUS_PREPARE:
26 | mStatus = STATUS_IN_HOOK;
27 | run();
28 | break;
29 | case STATUS_IN_HOOK:
30 | break;
31 | case STATUS_RESUMED:
32 | resume();
33 | break;
34 | }
35 | }
36 |
37 | public void reset() {
38 | mStatus = STATUS_PREPARE;
39 | }
40 |
41 | public void resume() {
42 | if (mResumeAction != null) {
43 | mResumeAction.run();
44 | }
45 | mStatus = STATUS_RESUMED;
46 | }
47 |
48 | /**
49 | * Hook should always have a resume action, which is hooked by this hook.
50 | *
51 | * @param runnable
52 | */
53 | public void setResumeAction(Runnable runnable) {
54 | mResumeAction = runnable;
55 | }
56 | }
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/PtrClassicFrameLayout.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 |
6 | public class PtrClassicFrameLayout extends PtrFrameLayout {
7 |
8 | private PtrClassicDefaultHeader mPtrClassicHeader;
9 |
10 | public PtrClassicFrameLayout(Context context) {
11 | super(context);
12 | initViews();
13 | }
14 |
15 | public PtrClassicFrameLayout(Context context, AttributeSet attrs) {
16 | super(context, attrs);
17 | initViews();
18 | }
19 |
20 | public PtrClassicFrameLayout(Context context, AttributeSet attrs, int defStyle) {
21 | super(context, attrs, defStyle);
22 | initViews();
23 | }
24 |
25 | private void initViews() {
26 | mPtrClassicHeader = new PtrClassicDefaultHeader(getContext());
27 | setHeaderView(mPtrClassicHeader);
28 | addPtrUIHandler(mPtrClassicHeader);
29 | }
30 |
31 | public PtrClassicDefaultHeader getHeader() {
32 | return mPtrClassicHeader;
33 | }
34 |
35 | /**
36 | * Specify the last update time by this key string
37 | *
38 | * @param key
39 | */
40 | public void setLastUpdateTimeKey(String key) {
41 | if (mPtrClassicHeader != null) {
42 | mPtrClassicHeader.setLastUpdateTimeKey(key);
43 | }
44 | }
45 |
46 | /**
47 | * Using an object to specify the last update time.
48 | *
49 | * @param object
50 | */
51 | public void setLastUpdateTimeRelateObject(Object object) {
52 | if (mPtrClassicHeader != null) {
53 | mPtrClassicHeader.setLastUpdateTimeRelateObject(object);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/util/PtrLocalDisplay.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib.util;
2 |
3 | import android.content.Context;
4 | import android.util.DisplayMetrics;
5 | import android.view.View;
6 | import android.view.WindowManager;
7 |
8 | public class PtrLocalDisplay {
9 |
10 | public static int SCREEN_WIDTH_PIXELS;
11 | public static int SCREEN_HEIGHT_PIXELS;
12 | public static float SCREEN_DENSITY;
13 | public static int SCREEN_WIDTH_DP;
14 | public static int SCREEN_HEIGHT_DP;
15 |
16 | public static void init(Context context) {
17 | if (context == null) {
18 | return;
19 | }
20 | DisplayMetrics dm = new DisplayMetrics();
21 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
22 | wm.getDefaultDisplay().getMetrics(dm);
23 | SCREEN_WIDTH_PIXELS = dm.widthPixels;
24 | SCREEN_HEIGHT_PIXELS = dm.heightPixels;
25 | SCREEN_DENSITY = dm.density;
26 | SCREEN_WIDTH_DP = (int) (SCREEN_WIDTH_PIXELS / dm.density);
27 | SCREEN_HEIGHT_DP = (int) (SCREEN_HEIGHT_PIXELS / dm.density);
28 | }
29 |
30 | public static int dp2px(float dp) {
31 | final float scale = SCREEN_DENSITY;
32 | return (int) (dp * scale + 0.5f);
33 | }
34 |
35 | public static int designedDP2px(float designedDp) {
36 | if (SCREEN_WIDTH_DP != 320) {
37 | designedDp = designedDp * SCREEN_WIDTH_DP / 320f;
38 | }
39 | return dp2px(designedDp);
40 | }
41 |
42 | public static void setPadding(final View view, float left, float top, float right, float bottom) {
43 | view.setPadding(designedDP2px(left), dp2px(top), designedDP2px(right), dp2px(bottom));
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/EasyDefRecyclerView.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.LinearLayoutManager;
5 | import android.util.AttributeSet;
6 | import android.view.View;
7 |
8 | import com.zly.www.easyrecyclerview.footer.ErvDefaultFooter;
9 | import com.zly.www.easyrecyclerview.ptrlib.PtrClassicDefaultHeader;
10 |
11 | /**
12 | * 默认底部空布局实现
13 | * Created by zly on 2016/11/1 0001.
14 | */
15 |
16 | public class EasyDefRecyclerView extends EasyRecyclerView {
17 |
18 |
19 | private PtrClassicDefaultHeader mPtrClassicHeader;
20 |
21 | public EasyDefRecyclerView(Context context) {
22 | this(context, null);
23 | }
24 |
25 | public EasyDefRecyclerView(Context context, AttributeSet attrs) {
26 | this(context, attrs, 0);
27 | }
28 |
29 | public EasyDefRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
30 | super(context, attrs, defStyleAttr);
31 | init(context);
32 | }
33 |
34 | private void init(Context context) {
35 | setHeaderView(mPtrClassicHeader = new PtrClassicDefaultHeader(context));
36 | setFooterView(new ErvDefaultFooter(context));
37 | setLayoutManager(new LinearLayoutManager(getContext()));
38 | setOnEmptyViewClick(new OnClickListener() {
39 | @Override
40 | public void onClick(View v) {
41 | autoRefresh();
42 | }
43 | });
44 | }
45 |
46 | public void setLastUpdateTimeKey(String key) {
47 | if (mPtrClassicHeader != null) {
48 | mPtrClassicHeader.setLastUpdateTimeKey(key);
49 | }
50 | }
51 |
52 | public void setLastUpdateTimeRelateObject(Object object) {
53 | if (mPtrClassicHeader != null) {
54 | mPtrClassicHeader.setLastUpdateTimeRelateObject(object);
55 | }
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/Sample/src/main/java/com/zly/www/simple/customall/CustomAllActivity.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple.customall;
2 |
3 | import android.os.Bundle;
4 | import android.os.Handler;
5 | import android.support.v7.app.AppCompatActivity;
6 |
7 | import com.zly.www.easyrecyclerview.EasyDefRecyclerView;
8 | import com.zly.www.easyrecyclerview.listener.OnLoadListener;
9 | import com.zly.www.easyrecyclerview.listener.OnRefreshListener;
10 | import com.zly.www.simple.R;
11 | import com.zly.www.simple.adapter.CustomAdapter;
12 |
13 | import butterknife.BindView;
14 | import butterknife.ButterKnife;
15 |
16 | /**
17 | * 自定义头部和底部
18 | * Created by zly on 2016/11/28 0028.
19 | */
20 | public class CustomAllActivity extends AppCompatActivity implements OnRefreshListener, OnLoadListener {
21 |
22 | @BindView(R.id.erv)
23 | EasyDefRecyclerView erv;
24 |
25 | private CustomAdapter mAdapter;
26 | private Handler handler = new Handler();
27 | private boolean isFail = false;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | setContentView(R.layout.activity_custom);
33 | ButterKnife.bind(this);
34 |
35 | erv.setAdapter(mAdapter = new CustomAdapter());
36 | erv.setLastUpdateTimeRelateObject(this);
37 | erv.setOnRefreshListener(this);
38 | erv.setOnLoadListener(this);
39 | }
40 |
41 | @Override
42 | public void onRefreshListener() {
43 | handler.postDelayed(new Runnable() {
44 | @Override
45 | public void run() {
46 | for (int i = 0; i < 10; i++) {
47 | mAdapter.insert("最新数据",0);
48 | }
49 | }
50 | }, 3000);
51 | }
52 |
53 | @Override
54 | public void onLoadListener() {
55 | handler.postDelayed(new Runnable() {
56 | @Override
57 | public void run() {
58 |
59 | if (mAdapter.getItemCount() > 20 && !isFail) {
60 | isFail = true;
61 | erv.loadFail();
62 | } else if (mAdapter.getItemCount() > 30) {
63 | erv.noMore();
64 | } else {
65 | for (int i = 0; i < 10; i++) {
66 | mAdapter.add("更多数据");
67 | }
68 | }
69 | }
70 | }, 3000);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/header/StoreHouseBarItem.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib.header;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 | import android.graphics.PointF;
6 | import android.view.animation.Animation;
7 | import android.view.animation.Transformation;
8 |
9 | import java.util.Random;
10 |
11 | /**
12 | * Created by srain on 11/6/14.
13 | */
14 | public class StoreHouseBarItem extends Animation {
15 |
16 | public PointF midPoint;
17 | public float translationX;
18 | public int index;
19 |
20 | private final Paint mPaint = new Paint();
21 | private float mFromAlpha = 1.0f;
22 | private float mToAlpha = 0.4f;
23 | private PointF mCStartPoint;
24 | private PointF mCEndPoint;
25 |
26 | public StoreHouseBarItem(int index, PointF start, PointF end, int color, int lineWidth) {
27 | this.index = index;
28 |
29 | midPoint = new PointF((start.x + end.x) / 2, (start.y + end.y) / 2);
30 |
31 | mCStartPoint = new PointF(start.x - midPoint.x, start.y - midPoint.y);
32 | mCEndPoint = new PointF(end.x - midPoint.x, end.y - midPoint.y);
33 |
34 | setColor(color);
35 | setLineWidth(lineWidth);
36 | mPaint.setAntiAlias(true);
37 | mPaint.setStyle(Paint.Style.STROKE);
38 | }
39 |
40 | public void setLineWidth(int width) {
41 | mPaint.setStrokeWidth(width);
42 | }
43 |
44 | public void setColor(int color) {
45 | mPaint.setColor(color);
46 | }
47 |
48 | public void resetPosition(int horizontalRandomness) {
49 | Random random = new Random();
50 | int randomNumber = -random.nextInt(horizontalRandomness) + horizontalRandomness;
51 | translationX = randomNumber;
52 | }
53 |
54 | @Override
55 | protected void applyTransformation(float interpolatedTime, Transformation t) {
56 | float alpha = mFromAlpha;
57 | alpha = alpha + ((mToAlpha - alpha) * interpolatedTime);
58 | setAlpha(alpha);
59 | }
60 |
61 | public void start(float fromAlpha, float toAlpha) {
62 | mFromAlpha = fromAlpha;
63 | mToAlpha = toAlpha;
64 | super.start();
65 | }
66 |
67 | public void setAlpha(float alpha) {
68 | mPaint.setAlpha((int) (alpha * 255));
69 | }
70 |
71 | public void draw(Canvas canvas) {
72 | canvas.drawLine(mCStartPoint.x, mCStartPoint.y, mCEndPoint.x, mCEndPoint.y, mPaint);
73 | }
74 | }
--------------------------------------------------------------------------------
/ErvLibrary/src/main/res/layout/cube_ptr_classic_default_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
17 |
18 |
24 |
25 |
32 |
33 |
34 |
40 |
41 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/Sample/src/main/java/com/zly/www/simple/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.GridLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.View;
9 |
10 | import com.zly.www.easyrecyclerview.EasyRecyclerView;
11 | import com.zly.www.easyrecyclerview.listener.ItemClickSupport;
12 | import com.zly.www.simple.adapter.CustomAdapter;
13 | import com.zly.www.simple.customall.CustomAllActivity;
14 | import com.zly.www.simple.customdecoration.CustomDecorationActivity;
15 | import com.zly.www.simple.customheader.CustomHeaderActivity;
16 |
17 | import butterknife.BindView;
18 | import butterknife.ButterKnife;
19 |
20 | /**
21 | * Created by zly on 2016/11/28 0028.
22 | */
23 |
24 | public class MainActivity extends AppCompatActivity {
25 |
26 | @BindView(R.id.erv)
27 | EasyRecyclerView erv;
28 |
29 | private CustomAdapter mAdapter;
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.activity_main);
35 | ButterKnife.bind(this);
36 |
37 | initView();
38 | initData();
39 | }
40 |
41 | private void initView() {
42 | erv.setLayoutManager(new GridLayoutManager(this,3));
43 | erv.setAdapter(mAdapter = new CustomAdapter());
44 |
45 | ItemClickSupport.addTo(erv.getRecyclerView()).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
46 | @Override
47 | public void onItemClicked(RecyclerView recyclerView, int position, View v) {
48 | switch (position){
49 | case 0:
50 | startActivity(new Intent(MainActivity.this,CustomHeaderActivity.class));
51 | break;
52 | case 1:
53 | startActivity(new Intent(MainActivity.this,CustomAllActivity.class));
54 | break;
55 | case 2:
56 | startActivity(new Intent(MainActivity.this,CustomDecorationActivity.class));
57 | break;
58 | }
59 | }
60 | });
61 | }
62 |
63 | private void initData() {
64 | mAdapter.add(getString(R.string.custom_header));
65 | mAdapter.add(getString(R.string.custom_header_footer));
66 | mAdapter.add(getString(R.string.custom_item_decoration));
67 | }
68 |
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/Sample/src/main/java/com/zly/www/simple/customdecoration/CustomDecorationActivity.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple.customdecoration;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.support.v7.widget.LinearLayoutManager;
6 |
7 | import com.zly.www.easyrecyclerview.EasyRecyclerView;
8 | import com.zly.www.easyrecyclerview.decoration.StickItemDecoration;
9 | import com.zly.www.simple.R;
10 |
11 | import butterknife.BindView;
12 | import butterknife.ButterKnife;
13 |
14 | /**
15 | * 条目吸附demo
16 | * Created by zly on 2016/11/28 0028.
17 | */
18 |
19 | public class CustomDecorationActivity extends AppCompatActivity {
20 |
21 | @BindView(R.id.erv)
22 | EasyRecyclerView erv;
23 |
24 | private CustomDecorationAdapter mAdapter;
25 |
26 | @Override
27 | protected void onCreate(Bundle savedInstanceState) {
28 | super.onCreate(savedInstanceState);
29 | setContentView(R.layout.activity_custom_stick);
30 | ButterKnife.bind(this);
31 |
32 | initView();
33 | initData();
34 | }
35 |
36 | private void initView() {
37 | erv.setLayoutManager(new LinearLayoutManager(this));
38 | erv.setAdapter(mAdapter = new CustomDecorationAdapter());
39 | erv.addItemDecoration(new StickItemDecoration(this,mAdapter.getData()) {
40 | @Override
41 | public String getTag(int position) {
42 | return mAdapter.getItem(position).tag;
43 | }
44 |
45 | });
46 | }
47 |
48 | private void initData() {
49 | mAdapter.add(new CityBean("a","安庆"));
50 | mAdapter.add(new CityBean("a","安徽"));
51 | mAdapter.add(new CityBean("a","鞍山"));
52 | mAdapter.add(new CityBean("a","澳门"));
53 | mAdapter.add(new CityBean("b","白城"));
54 | mAdapter.add(new CityBean("b","白沙"));
55 | mAdapter.add(new CityBean("b","北海"));
56 | mAdapter.add(new CityBean("b","北京"));
57 | mAdapter.add(new CityBean("b","保定"));
58 | mAdapter.add(new CityBean("b","宝鸡"));
59 | mAdapter.add(new CityBean("c","长沙"));
60 | mAdapter.add(new CityBean("c","成都"));
61 | mAdapter.add(new CityBean("c","常德"));
62 | mAdapter.add(new CityBean("d","大理"));
63 | mAdapter.add(new CityBean("d","大连"));
64 | mAdapter.add(new CityBean("d","大庆"));
65 | mAdapter.add(new CityBean("e","鄂州"));
66 | mAdapter.add(new CityBean("h","哈尔滨"));
67 | mAdapter.add(new CityBean("w","万宁"));
68 | mAdapter.add(new CityBean("w","潍坊"));
69 | mAdapter.add(new CityBean("w","威海"));
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/Sample/src/main/java/com/zly/www/simple/customheader/CustomHeaderActivity.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.simple.customheader;
2 |
3 | import android.os.Bundle;
4 | import android.os.Handler;
5 | import android.support.v7.app.AppCompatActivity;
6 |
7 | import com.zly.www.easyrecyclerview.EasyDefRecyclerView;
8 | import com.zly.www.easyrecyclerview.listener.OnLoadListener;
9 | import com.zly.www.easyrecyclerview.listener.OnRefreshListener;
10 | import com.zly.www.easyrecyclerview.ptrlib.header.MaterialHeader;
11 | import com.zly.www.simple.DisplayUtil;
12 | import com.zly.www.simple.R;
13 | import com.zly.www.simple.adapter.CustomAdapter;
14 |
15 | import butterknife.BindView;
16 | import butterknife.ButterKnife;
17 |
18 | /**
19 | * 自定义头部
20 | * Created by zly on 2016/11/28 0028.
21 | */
22 | public class CustomHeaderActivity extends AppCompatActivity implements OnRefreshListener, OnLoadListener {
23 |
24 | @BindView(R.id.erv)
25 | EasyDefRecyclerView erv;
26 |
27 | private CustomAdapter mAdapter;
28 | private Handler handler = new Handler();
29 | private boolean isFail = false;
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.activity_custom);
35 | ButterKnife.bind(this);
36 |
37 | MaterialHeader materialHeader = new MaterialHeader(this);
38 | materialHeader.setPadding(0, DisplayUtil.dip2px(20),0,DisplayUtil.dip2px(20));
39 | erv.setHeaderView(materialHeader);
40 | erv.setPinContent(true);
41 | erv.setAdapter(mAdapter = new CustomAdapter());
42 | erv.setLastUpdateTimeRelateObject(this);
43 | erv.setOnRefreshListener(this);
44 | erv.setOnLoadListener(this);
45 | }
46 |
47 | @Override
48 | public void onRefreshListener() {
49 | handler.postDelayed(new Runnable() {
50 | @Override
51 | public void run() {
52 | for (int i = 0; i < 10; i++) {
53 | mAdapter.insert("最新数据",0);
54 | }
55 | }
56 | }, 3000);
57 | }
58 |
59 | @Override
60 | public void onLoadListener() {
61 | handler.postDelayed(new Runnable() {
62 | @Override
63 | public void run() {
64 |
65 | if (mAdapter.getItemCount() > 20 && !isFail) {
66 | isFail = true;
67 | erv.loadFail();
68 | } else if (mAdapter.getItemCount() > 30) {
69 | erv.noMore();
70 | } else {
71 | for (int i = 0; i < 10; i++) {
72 | mAdapter.add("更多数据");
73 | }
74 | }
75 | }
76 | }, 3000);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/footer/ErvDefaultFooter.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.footer;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.text.SpannableStringBuilder;
6 | import android.text.TextPaint;
7 | import android.text.method.LinkMovementMethod;
8 | import android.text.style.ClickableSpan;
9 | import android.util.AttributeSet;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.FrameLayout;
14 | import android.widget.TextView;
15 |
16 | import com.zly.www.easyrecyclerview.R;
17 | import com.zly.www.easyrecyclerview.listener.OnLoadListener;
18 |
19 | import java.lang.ref.WeakReference;
20 |
21 | import static android.text.Spanned.SPAN_INCLUSIVE_INCLUSIVE;
22 |
23 | /**
24 | * 默认底部实现
25 | * Created by zly on 2016/10/25 0025.
26 | */
27 |
28 | public class ErvDefaultFooter extends FrameLayout implements ErvLoadUIHandle {
29 |
30 | private TextView footer;
31 | private int mState = LOAD;
32 |
33 | public ErvDefaultFooter(Context context) {
34 | this(context, null);
35 | }
36 |
37 | public ErvDefaultFooter(Context context, AttributeSet attrs) {
38 | this(context, attrs, 0);
39 | }
40 |
41 | public ErvDefaultFooter(Context context, AttributeSet attrs, int defStyleAttr) {
42 | super(context, attrs, defStyleAttr);
43 | init();
44 | }
45 |
46 | private void init() {
47 | setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT));
48 | View view = LayoutInflater.from(getContext()).inflate(R.layout.erv_default_footer, this);
49 | footer = (TextView) view.findViewById(R.id.footerTv);
50 | onLoading();
51 | }
52 |
53 | @Override
54 | public void onLoading() {
55 | mState = LOAD;
56 | footer.setText(getResources().getString(R.string.erv_loading));
57 | }
58 |
59 | @Override
60 | public void onNoMore() {
61 | mState = NOMORE;
62 | footer.setText(getResources().getString(R.string.erv_noMore));
63 | }
64 |
65 | @Override
66 | public void onLoadFail(OnLoadListener listener) {
67 | mState = LOADFAIL;
68 | String content = "加载失败,";
69 | String click = "点击重试";
70 | SpannableStringBuilder builder = new SpannableStringBuilder(content+click);
71 | builder.setSpan(new Click(listener),content.length(),content.length()+click.length(),SPAN_INCLUSIVE_INCLUSIVE);
72 | footer.setText(builder);
73 | footer.setMovementMethod(new LinkMovementMethod());
74 | }
75 |
76 | @Override
77 | public int getState() {
78 | return mState;
79 | }
80 |
81 | class Click extends ClickableSpan{
82 |
83 | private WeakReference reference;
84 |
85 | public Click(OnLoadListener listener) {
86 | this.reference = new WeakReference(listener);
87 | }
88 |
89 | @Override
90 | public void onClick(View v) {
91 | onLoading();
92 | OnLoadListener onLoadListener = reference.get();
93 | if(onLoadListener != null){
94 | onLoadListener.onLoadListener();
95 | }
96 | }
97 |
98 | @Override
99 | public void updateDrawState(TextPaint ds) {
100 | ds.setColor(Color.BLUE);
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/utils/LayoutManagerUtil.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.utils;
2 |
3 | import android.support.v7.widget.GridLayoutManager;
4 | import android.support.v7.widget.LinearLayoutManager;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.support.v7.widget.StaggeredGridLayoutManager;
7 |
8 | import java.util.Arrays;
9 |
10 | import static com.zly.www.easyrecyclerview.utils.LayoutManagerUtil.LAYOUT_MANAGER_TYPE.GRID;
11 | import static com.zly.www.easyrecyclerview.utils.LayoutManagerUtil.LAYOUT_MANAGER_TYPE.LINEAR;
12 | import static com.zly.www.easyrecyclerview.utils.LayoutManagerUtil.LAYOUT_MANAGER_TYPE.STAGGERED_GRID;
13 |
14 | /**
15 | * @author chenzj
16 | * 类的描述 -
17 | * 2016/10/9 10:36
18 | * admin@chenzhongjin.cn
19 | */
20 | public class LayoutManagerUtil {
21 |
22 | public static int getFirstVisibleItemPosition(RecyclerView.LayoutManager layoutManager) {
23 | int firstVisibleItemPosition = -1;
24 |
25 | switch (getLayoutManagerType(layoutManager)) {
26 | case LINEAR:
27 | case GRID:
28 | firstVisibleItemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
29 | break;
30 | case STAGGERED_GRID:
31 | firstVisibleItemPosition = caseStaggeredGrid(layoutManager, true);
32 | break;
33 | }
34 | return firstVisibleItemPosition;
35 | }
36 |
37 | public static int getLastVisibleItemPosition(RecyclerView.LayoutManager layoutManager) {
38 | int lastVisibleItemPosition = -1;
39 | switch (getLayoutManagerType(layoutManager)) {
40 | case LINEAR:
41 | case GRID:
42 | lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
43 | break;
44 | case STAGGERED_GRID:
45 | lastVisibleItemPosition = caseStaggeredGrid(layoutManager, false);
46 | break;
47 | }
48 | return lastVisibleItemPosition;
49 | }
50 |
51 | private static LAYOUT_MANAGER_TYPE getLayoutManagerType(RecyclerView.LayoutManager layoutManager) {
52 | LAYOUT_MANAGER_TYPE layoutManagerType;
53 |
54 | if (layoutManager instanceof GridLayoutManager) {
55 | layoutManagerType = GRID;
56 | } else if (layoutManager instanceof LinearLayoutManager) {
57 | layoutManagerType = LINEAR;
58 | } else if (layoutManager instanceof StaggeredGridLayoutManager) {
59 | layoutManagerType = STAGGERED_GRID;
60 | } else {
61 | throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager " +
62 | "and StaggeredGridLayoutManager");
63 | }
64 | return layoutManagerType;
65 | }
66 |
67 | private static int caseStaggeredGrid(RecyclerView.LayoutManager layoutManager, boolean findFirst) {
68 | StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
69 | int[] positions = new int[staggeredGridLayoutManager.getSpanCount()];
70 |
71 | if (findFirst) {
72 | staggeredGridLayoutManager.findFirstVisibleItemPositions(positions);
73 | Arrays.sort(positions);
74 | return positions[0];
75 | } else {
76 | staggeredGridLayoutManager.findLastVisibleItemPositions(positions);
77 | Arrays.sort(positions);
78 | return positions[positions.length - 1];
79 | }
80 | }
81 |
82 | public enum LAYOUT_MANAGER_TYPE {
83 | LINEAR,
84 | GRID,
85 | STAGGERED_GRID
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/listener/ItemClickSupport.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.listener;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 |
6 | import com.zly.www.easyrecyclerview.R;
7 |
8 | /**
9 | * RecyclerView item click
10 | * Created by zly on 2016/11/22 0022.
11 | */
12 | public class ItemClickSupport {
13 | private final RecyclerView mRecyclerView;
14 | private OnItemClickListener mOnItemClickListener;
15 | private OnItemLongClickListener mOnItemLongClickListener;
16 |
17 | private View.OnClickListener mOnClickListener = new View.OnClickListener() {
18 | @Override
19 | public void onClick(View v) {
20 | if (mOnItemClickListener != null) {
21 | RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
22 | mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
23 | }
24 | }
25 | };
26 |
27 | private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
28 | @Override
29 | public boolean onLongClick(View v) {
30 | if (mOnItemLongClickListener != null) {
31 | RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
32 | return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
33 | }
34 | return false;
35 | }
36 | };
37 |
38 | private RecyclerView.OnChildAttachStateChangeListener mAttachListener
39 | = new RecyclerView.OnChildAttachStateChangeListener() {
40 | @Override
41 | public void onChildViewAttachedToWindow(View view) {
42 | if (mOnItemClickListener != null) {
43 | view.setOnClickListener(mOnClickListener);
44 | }
45 | if (mOnItemLongClickListener != null) {
46 | view.setOnLongClickListener(mOnLongClickListener);
47 | }
48 | }
49 |
50 | @Override
51 | public void onChildViewDetachedFromWindow(View view) {
52 | }
53 | };
54 |
55 | private ItemClickSupport(RecyclerView recyclerView) {
56 | mRecyclerView = recyclerView;
57 | mRecyclerView.setTag(R.id.item_click_support, this);
58 | mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
59 | }
60 |
61 | public static ItemClickSupport addTo(RecyclerView view) {
62 | ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
63 | if (support == null) {
64 | support = new ItemClickSupport(view);
65 | }
66 | return support;
67 | }
68 |
69 | public static ItemClickSupport removeFrom(RecyclerView view) {
70 | ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
71 | if (support != null) {
72 | support.detach(view);
73 | }
74 | return support;
75 | }
76 |
77 | public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
78 | mOnItemClickListener = listener;
79 | return this;
80 | }
81 |
82 | public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
83 | mOnItemLongClickListener = listener;
84 | return this;
85 | }
86 |
87 | private void detach(RecyclerView view) {
88 | view.removeOnChildAttachStateChangeListener(mAttachListener);
89 | view.setTag(R.id.item_click_support, null);
90 | }
91 |
92 | public interface OnItemClickListener {
93 | void onItemClicked(RecyclerView recyclerView, int position, View v);
94 | }
95 |
96 | public interface OnItemLongClickListener {
97 | boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/indicator/PtrTensionIndicator.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib.indicator;
2 |
3 | public class PtrTensionIndicator extends PtrIndicator {
4 |
5 | private float DRAG_RATE = 0.5f;
6 | private float mDownY;
7 | private float mDownPos;
8 | private float mOneHeight = 0;
9 |
10 | private float mCurrentDragPercent;
11 |
12 | private int mReleasePos;
13 | private float mReleasePercent = -1;
14 |
15 | @Override
16 | public void onPressDown(float x, float y) {
17 | super.onPressDown(x, y);
18 | mDownY = y;
19 | mDownPos = getCurrentPosY();
20 | }
21 |
22 | @Override
23 | public void onRelease() {
24 | super.onRelease();
25 | mReleasePos = getCurrentPosY();
26 | mReleasePercent = mCurrentDragPercent;
27 | }
28 |
29 | @Override
30 | public void onUIRefreshComplete() {
31 | mReleasePos = getCurrentPosY();
32 | mReleasePercent = getOverDragPercent();
33 | }
34 |
35 | @Override
36 | public void setHeaderHeight(int height) {
37 | super.setHeaderHeight(height);
38 | mOneHeight = height * 4f / 5;
39 | }
40 |
41 | @Override
42 | protected void processOnMove(float currentX, float currentY, float offsetX, float offsetY) {
43 |
44 | if (currentY < mDownY) {
45 | super.processOnMove(currentX, currentY, offsetX, offsetY);
46 | return;
47 | }
48 |
49 | // distance from top
50 | final float scrollTop = (currentY - mDownY) * DRAG_RATE + mDownPos;
51 | final float currentDragPercent = scrollTop / mOneHeight;
52 |
53 | if (currentDragPercent < 0) {
54 | setOffset(offsetX, 0);
55 | return;
56 | }
57 |
58 | mCurrentDragPercent = currentDragPercent;
59 |
60 | // 0 ~ 1
61 | float boundedDragPercent = Math.min(1f, Math.abs(currentDragPercent));
62 | float extraOS = scrollTop - mOneHeight;
63 |
64 | // 0 ~ 2
65 | // if extraOS lower than 0, which means scrollTop lower than onHeight, tensionSlingshotPercent will be 0.
66 | float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, mOneHeight * 2) / mOneHeight);
67 |
68 | float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow((tensionSlingshotPercent / 4), 2)) * 2f;
69 | float extraMove = (mOneHeight) * tensionPercent / 2;
70 | int targetY = (int) ((mOneHeight * boundedDragPercent) + extraMove);
71 | int change = targetY - getCurrentPosY();
72 |
73 | setOffset(currentX, change);
74 | }
75 |
76 | private float offsetToTarget(float scrollTop) {
77 |
78 | // distance from top
79 | final float currentDragPercent = scrollTop / mOneHeight;
80 |
81 | mCurrentDragPercent = currentDragPercent;
82 |
83 | // 0 ~ 1
84 | float boundedDragPercent = Math.min(1f, Math.abs(currentDragPercent));
85 | float extraOS = scrollTop - mOneHeight;
86 |
87 | // 0 ~ 2
88 | // if extraOS lower than 0, which means scrollTop lower than mOneHeight, tensionSlingshotPercent will be 0.
89 | float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, mOneHeight * 2) / mOneHeight);
90 |
91 | float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow((tensionSlingshotPercent / 4), 2)) * 2f;
92 | float extraMove = (mOneHeight) * tensionPercent / 2;
93 | int targetY = (int) ((mOneHeight * boundedDragPercent) + extraMove);
94 |
95 | return 0;
96 | }
97 |
98 | @Override
99 | public int getOffsetToKeepHeaderWhileLoading() {
100 | return getOffsetToRefresh();
101 | }
102 |
103 | @Override
104 | public int getOffsetToRefresh() {
105 | return (int) mOneHeight;
106 | }
107 |
108 | public float getOverDragPercent() {
109 | if (isUnderTouch()) {
110 | return mCurrentDragPercent;
111 | } else {
112 | if (mReleasePercent <= 0) {
113 | return 1.0f * getCurrentPosY() / getOffsetToKeepHeaderWhileLoading();
114 | }
115 | // after release
116 | return mReleasePercent * getCurrentPosY() / mReleasePos;
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/PtrUIHandlerHolder.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib;
2 |
3 |
4 | import com.zly.www.easyrecyclerview.ptrlib.indicator.PtrIndicator;
5 |
6 | /**
7 | * A single linked list to wrap PtrUIHandler
8 | */
9 | class PtrUIHandlerHolder implements PtrUIHandler {
10 |
11 | private PtrUIHandler mHandler;
12 | private PtrUIHandlerHolder mNext;
13 |
14 | private boolean contains(PtrUIHandler handler) {
15 | return mHandler != null && mHandler == handler;
16 | }
17 |
18 | private PtrUIHandlerHolder() {
19 |
20 | }
21 |
22 | public boolean hasHandler() {
23 | return mHandler != null;
24 | }
25 |
26 | private PtrUIHandler getHandler() {
27 | return mHandler;
28 | }
29 |
30 | public static void addHandler(PtrUIHandlerHolder head, PtrUIHandler handler) {
31 |
32 | if (null == handler) {
33 | return;
34 | }
35 | if (head == null) {
36 | return;
37 | }
38 | if (null == head.mHandler) {
39 | head.mHandler = handler;
40 | return;
41 | }
42 |
43 | PtrUIHandlerHolder current = head;
44 | for (; ; current = current.mNext) {
45 |
46 | // duplicated
47 | if (current.contains(handler)) {
48 | return;
49 | }
50 | if (current.mNext == null) {
51 | break;
52 | }
53 | }
54 |
55 | PtrUIHandlerHolder newHolder = new PtrUIHandlerHolder();
56 | newHolder.mHandler = handler;
57 | current.mNext = newHolder;
58 | }
59 |
60 | public static PtrUIHandlerHolder create() {
61 | return new PtrUIHandlerHolder();
62 | }
63 |
64 | public static PtrUIHandlerHolder removeHandler(PtrUIHandlerHolder head, PtrUIHandler handler) {
65 | if (head == null || handler == null || null == head.mHandler) {
66 | return head;
67 | }
68 |
69 | PtrUIHandlerHolder current = head;
70 | PtrUIHandlerHolder pre = null;
71 | do {
72 |
73 | // delete current: link pre to next, unlink next from current;
74 | // pre will no change, current move to next element;
75 | if (current.contains(handler)) {
76 |
77 | // current is head
78 | if (pre == null) {
79 |
80 | head = current.mNext;
81 | current.mNext = null;
82 |
83 | current = head;
84 | } else {
85 |
86 | pre.mNext = current.mNext;
87 | current.mNext = null;
88 | current = pre.mNext;
89 | }
90 | } else {
91 | pre = current;
92 | current = current.mNext;
93 | }
94 |
95 | } while (current != null);
96 |
97 | if (head == null) {
98 | head = new PtrUIHandlerHolder();
99 | }
100 | return head;
101 | }
102 |
103 | @Override
104 | public void onUIReset(PtrFrameLayout frame) {
105 | PtrUIHandlerHolder current = this;
106 | do {
107 | final PtrUIHandler handler = current.getHandler();
108 | if (null != handler) {
109 | handler.onUIReset(frame);
110 | }
111 | } while ((current = current.mNext) != null);
112 | }
113 |
114 | @Override
115 | public void onUIRefreshPrepare(PtrFrameLayout frame) {
116 | if (!hasHandler()) {
117 | return;
118 | }
119 | PtrUIHandlerHolder current = this;
120 | do {
121 | final PtrUIHandler handler = current.getHandler();
122 | if (null != handler) {
123 | handler.onUIRefreshPrepare(frame);
124 | }
125 | } while ((current = current.mNext) != null);
126 | }
127 |
128 | @Override
129 | public void onUIRefreshBegin(PtrFrameLayout frame) {
130 | PtrUIHandlerHolder current = this;
131 | do {
132 | final PtrUIHandler handler = current.getHandler();
133 | if (null != handler) {
134 | handler.onUIRefreshBegin(frame);
135 | }
136 | } while ((current = current.mNext) != null);
137 | }
138 |
139 | @Override
140 | public void onUIRefreshComplete(PtrFrameLayout frame) {
141 | PtrUIHandlerHolder current = this;
142 | do {
143 | final PtrUIHandler handler = current.getHandler();
144 | if (null != handler) {
145 | handler.onUIRefreshComplete(frame);
146 | }
147 | } while ((current = current.mNext) != null);
148 | }
149 |
150 | @Override
151 | public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {
152 | PtrUIHandlerHolder current = this;
153 | do {
154 | final PtrUIHandler handler = current.getHandler();
155 | if (null != handler) {
156 | handler.onUIPositionChange(frame, isUnderTouch, status, ptrIndicator);
157 | }
158 | } while ((current = current.mNext) != null);
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/indicator/PtrIndicator.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib.indicator;
2 |
3 | import android.graphics.PointF;
4 |
5 | public class PtrIndicator {
6 |
7 | public final static int POS_START = 0;
8 | protected int mOffsetToRefresh = 0;
9 | private PointF mPtLastMove = new PointF();
10 | private float mOffsetX;
11 | private float mOffsetY;
12 | private int mCurrentPos = 0;
13 | private int mLastPos = 0;
14 | private int mHeaderHeight;
15 | private int mPressedPos = 0;
16 |
17 | private float mRatioOfHeaderHeightToRefresh = 1.2f;
18 | private float mResistance = 1.7f;
19 | private boolean mIsUnderTouch = false;
20 | private int mOffsetToKeepHeaderWhileLoading = -1;
21 | // record the refresh complete position
22 | private int mRefreshCompleteY = 0;
23 |
24 | public boolean isUnderTouch() {
25 | return mIsUnderTouch;
26 | }
27 |
28 | public float getResistance() {
29 | return mResistance;
30 | }
31 |
32 | public void setResistance(float resistance) {
33 | mResistance = resistance;
34 | }
35 |
36 | public void onRelease() {
37 | mIsUnderTouch = false;
38 | }
39 |
40 | public void onUIRefreshComplete() {
41 | mRefreshCompleteY = mCurrentPos;
42 | }
43 |
44 | public boolean goDownCrossFinishPosition() {
45 | return mCurrentPos >= mRefreshCompleteY;
46 | }
47 |
48 | protected void processOnMove(float currentX, float currentY, float offsetX, float offsetY) {
49 | setOffset(offsetX, offsetY / mResistance);
50 | }
51 |
52 | public void setRatioOfHeaderHeightToRefresh(float ratio) {
53 | mRatioOfHeaderHeightToRefresh = ratio;
54 | mOffsetToRefresh = (int) (mHeaderHeight * ratio);
55 | }
56 |
57 | public float getRatioOfHeaderToHeightRefresh() {
58 | return mRatioOfHeaderHeightToRefresh;
59 | }
60 |
61 | public int getOffsetToRefresh() {
62 | return mOffsetToRefresh;
63 | }
64 |
65 | public void setOffsetToRefresh(int offset) {
66 | mRatioOfHeaderHeightToRefresh = mHeaderHeight * 1f / offset;
67 | mOffsetToRefresh = offset;
68 | }
69 |
70 | public void onPressDown(float x, float y) {
71 | mIsUnderTouch = true;
72 | mPressedPos = mCurrentPos;
73 | mPtLastMove.set(x, y);
74 | }
75 |
76 | public final void onMove(float x, float y) {
77 | float offsetX = x - mPtLastMove.x;
78 | float offsetY = (y - mPtLastMove.y);
79 | processOnMove(x, y, offsetX, offsetY);
80 | mPtLastMove.set(x, y);
81 | }
82 |
83 | protected void setOffset(float x, float y) {
84 | mOffsetX = x;
85 | mOffsetY = y;
86 | }
87 |
88 | public float getOffsetX() {
89 | return mOffsetX;
90 | }
91 |
92 | public float getOffsetY() {
93 | return mOffsetY;
94 | }
95 |
96 | public int getLastPosY() {
97 | return mLastPos;
98 | }
99 |
100 | public int getCurrentPosY() {
101 | return mCurrentPos;
102 | }
103 |
104 | /**
105 | * Update current position before update the UI
106 | */
107 | public final void setCurrentPos(int current) {
108 | mLastPos = mCurrentPos;
109 | mCurrentPos = current;
110 | onUpdatePos(current, mLastPos);
111 | }
112 |
113 | protected void onUpdatePos(int current, int last) {
114 |
115 | }
116 |
117 | public int getHeaderHeight() {
118 | return mHeaderHeight;
119 | }
120 |
121 | public void setHeaderHeight(int height) {
122 | mHeaderHeight = height;
123 | updateHeight();
124 | }
125 |
126 | protected void updateHeight() {
127 | mOffsetToRefresh = (int) (mRatioOfHeaderHeightToRefresh * mHeaderHeight);
128 | }
129 |
130 | public void convertFrom(PtrIndicator ptrSlider) {
131 | mCurrentPos = ptrSlider.mCurrentPos;
132 | mLastPos = ptrSlider.mLastPos;
133 | mHeaderHeight = ptrSlider.mHeaderHeight;
134 | }
135 |
136 | public boolean hasLeftStartPosition() {
137 | return mCurrentPos > POS_START;
138 | }
139 |
140 | public boolean hasJustLeftStartPosition() {
141 | return mLastPos == POS_START && hasLeftStartPosition();
142 | }
143 |
144 | public boolean hasJustBackToStartPosition() {
145 | return mLastPos != POS_START && isInStartPosition();
146 | }
147 |
148 | public boolean isOverOffsetToRefresh() {
149 | return mCurrentPos >= getOffsetToRefresh();
150 | }
151 |
152 | public boolean hasMovedAfterPressedDown() {
153 | return mCurrentPos != mPressedPos;
154 | }
155 |
156 | public boolean isInStartPosition() {
157 | return mCurrentPos == POS_START;
158 | }
159 |
160 | public boolean crossRefreshLineFromTopToBottom() {
161 | return mLastPos < getOffsetToRefresh() && mCurrentPos >= getOffsetToRefresh();
162 | }
163 |
164 | public boolean hasJustReachedHeaderHeightFromTopToBottom() {
165 | return mLastPos < mHeaderHeight && mCurrentPos >= mHeaderHeight;
166 | }
167 |
168 | public boolean isOverOffsetToKeepHeaderWhileLoading() {
169 | return mCurrentPos > getOffsetToKeepHeaderWhileLoading();
170 | }
171 |
172 | public void setOffsetToKeepHeaderWhileLoading(int offset) {
173 | mOffsetToKeepHeaderWhileLoading = offset;
174 | }
175 |
176 | public int getOffsetToKeepHeaderWhileLoading() {
177 | return mOffsetToKeepHeaderWhileLoading >= 0 ? mOffsetToKeepHeaderWhileLoading : mHeaderHeight;
178 | }
179 |
180 | public boolean isAlreadyHere(int to) {
181 | return mCurrentPos == to;
182 | }
183 |
184 | public float getLastPercent() {
185 | final float oldPercent = mHeaderHeight == 0 ? 0 : mLastPos * 1f / mHeaderHeight;
186 | return oldPercent;
187 | }
188 |
189 | public float getCurrentPercent() {
190 | final float currentPercent = mHeaderHeight == 0 ? 0 : mCurrentPos * 1f / mHeaderHeight;
191 | return currentPercent;
192 | }
193 |
194 | public boolean willOverTop(int to) {
195 | return to < POS_START;
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EasyRecyclerView
2 | ## **描述**
3 | 这是一个下拉刷新上拉加载更多框架(ps:后期还会加入一些常用的功能.),头部用的秋哥的[android-Ultra-Pull-To-Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh),底部和没有数据的状态自己实现的.其实刚刚开始我是想找个库直接用的,试了几个排名靠前的,感觉跟自己想要的不太一样,索性自己写了一个,当然这当中也遇到了问题,多亏[仲锦大师](https://github.com/chenzj-king)的帮助在此感谢.
4 |
5 | ### 特点:
6 | - 可定制的头部 (可以查看[android-Ultra-Pull-To-Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh)文档)
7 | - 可定制的底部 (加载中/没有数据/加载失败 三种状态的定制)
8 | - 可定制的没有数据状态显示 (目前只有一个状态)
9 | - 目前提供一个实现好的ItemDecoration(头部吸附效果)
10 |
11 |
12 | ## **效果预览**
13 | ### 1.定制头部&定制脚步
14 | - 头部秋哥已经定制了很多样式就直接使用了
15 | - 脚部这边使用的已经实现好的ErvDefaultFooter
16 |
17 | 默认头部与顶部效果
18 |
19 | 
20 |
21 | material style头部
22 |
23 | 
24 |
25 |
26 | ### 2.头部吸附
27 | 
28 |
29 |
30 | ## **使用方式**
31 | ### 依赖
32 | gradle
33 |
34 | compile 'com.yysauce:easyrecyclerview:1.0.0'
35 |
36 | ### 配置
37 | 目前有两个参数可以配置
38 | - app:emply_layout
39 | 没有数据时候布局
40 |
41 | - app:number_load_more
42 | 最后可见条目 + number_load_more > total 触发加载更多;默认值为4
43 |
44 | ### xml中配置示例
45 |
50 |
51 |
52 |
53 |
54 |
55 | ### activity代码配置
56 |
57 | erv.setAdapter(rvAdapter = new RvAdapter());//设置adapter
58 | erv.setLastUpdateTimeRelateObject(this);//传入参数类名作为记录刷新时间key
59 | erv.setOnRefreshListener(this);//设置刷新监听
60 | erv.setOnLoadListener(this);//设置加载更多监听
61 |
62 | 由于这里使用的EasyDefRecyclerView,头部就是默认经典样式所以需要调用,使用其他头部时不需要调用
63 |
64 | erv.setLastUpdateTimeRelateObject(this);//传入参数类名作为记录刷新时间key
65 |
66 |
67 | ### adapter代码配置
68 |
69 | adapter需要实现CommonAdapter或者MultipleAdapter抽象方法
70 |
71 | //创建ViewHolder
72 | public abstract VH createCustomViewHolder(ViewGroup parent, int viewType);
73 | //ViewHolder设置数据
74 | public abstract void bindCustomViewHolder(VH holder, T t, int position);
75 |
76 | MultipleAdapter多条目布局还多一个方法需要实现
77 |
78 | //返回多条目的type
79 | public abstract int customItemViewType(int position);
80 |
81 | 目前提供了下面这些方法操作adapter数据,具体实现可以在CommonAdapter中查看
82 |
83 | 新增数据
84 | - public void add(@NonNull T object)
85 | - public void addAll(@NonNull Collection extends T> collection)
86 | - public void addAll(@NonNull T... items)
87 | - public void insert(@NonNull T object, int index)
88 | - public void insertAll(@NonNull Collection extends T> collection, int index)
89 |
90 | 删除数据
91 | - public void remove(int index)
92 | - public boolean remove(@NonNull T object)
93 | - public void clear()
94 |
95 | 修改数据
96 | - public void update(@NonNull List mDatas)
97 |
98 | 查看数据
99 | - public T getItem(int position)
100 | - public int getPosition(T item)
101 | - public List getData()
102 |
103 | 排序
104 | - public void sort(Comparator super T> comparator)
105 |
106 | 加载布局
107 | - public View inflateView(@LayoutRes int resId, ViewGroup parent)
108 |
109 | adapter中ViewHolder需要继承BaseViewHolder
110 |
111 |
112 |
113 | ## **其他配置**
114 | ### 头部吸附效果
115 |
116 | mItemDecoration = new StickItemDecoration(context,dataList) {
117 | @Override
118 | public String getTag(int position) {
119 | return "吸附头部显示的文字";
120 | }
121 | }
122 | erv.addItemDecoration(mItemDecoration);
123 |
124 | 这里StickItemDecoration提供了如下方法来定制吸附效果
125 |
126 | //设置吸附条目高度
127 | public void setStickHeight(int mStickHeight)
128 | //设置吸附条目背景
129 | public void setStickBackgroundColor(int mStickBackgroundColor)
130 | //设置吸附文字颜色
131 | public void setStickTextColor(int mStickTextColor)
132 | //设置吸附文字大小
133 | public void setStickTextSize(int mStickTextSize)
134 | //设置吸附文字leftmargin
135 | public void setStickTextoffset(int mStickTextoffset)
136 |
137 | ## **自定义**
138 |
139 | 头部使用秋哥的[android-Ultra-Pull-To-Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh)
140 | 秋哥默认已经实现了3个头部
141 | - MaterialHeader
142 | - PtrClassicDefaultHeader
143 | - StoreHouseHeader
144 |
145 | 一般情况下这些样式应该够了,如果有特殊需求可以自定义头部.然后erv.setHeaderView(view);
146 |
147 | 底部的话目前我只实现了一个ErvDefaultFooter,自定义的话需要实现ErvLoadUIHandle接口.写法可以参考ErvDefaultFooter
148 |
149 | public interface ErvLoadUIHandle {
150 |
151 | /**
152 | * 允许加载更多
153 | */
154 | int LOAD = 1;
155 |
156 | /**
157 | * 暂无更多数据
158 | */
159 | int NOMORE = 2;
160 |
161 | /**
162 | * 加载失败
163 | */
164 | int LOADFAIL = 3;
165 |
166 | /**
167 | * @return 获取底部当前状态
168 | */
169 | int getState();
170 |
171 | void onLoading();//loading状态实现
172 |
173 | void onNoMore();//没有数据状态实现
174 |
175 | void onLoadFail(OnLoadListener listener);//加载失败实现
176 |
177 |
178 | }
179 |
180 | 实现后调用setFooterView()方法设置
181 |
182 |
183 | ## **总结**
184 | 目前还在EasyRecyclerView还在优化欢迎各位提出你们宝贵的意见,例子可以参考Sample
185 |
186 |
187 | ## **感谢**
188 | 秋哥的[android-Ultra-Pull-To-Refresh](https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh)
189 | [仲锦大师](https://github.com/chenzj-king)的帮助
190 |
191 |
192 | ## **联系方式**
193 | qq:1835556188
194 | blog:http://blog.csdn.net/zly921112
195 |
196 |
197 | ## **License**
198 | Copyright (c) 2016 zhuliyuan
199 |
200 | Licensed under the Apache License, Version 2.0 (the "License");
201 | you may not use this file except in compliance with the License.
202 | You may obtain a copy of the License at
203 |
204 | http://www.apache.org/licenses/LICENSE-2.0
205 |
206 | Unless required by applicable law or agreed to in writing, software
207 | distributed under the License is distributed on an "AS IS" BASIS,
208 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
209 | See the License for the specific language governing permissions and
210 | limitations under the License.
211 | Come on, don't tell me you read that.
212 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/header/MaterialHeader.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib.header;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Rect;
7 | import android.graphics.drawable.Drawable;
8 | import android.util.AttributeSet;
9 | import android.view.View;
10 | import android.view.animation.Animation;
11 | import android.view.animation.Transformation;
12 |
13 | import com.zly.www.easyrecyclerview.ptrlib.PtrFrameLayout;
14 | import com.zly.www.easyrecyclerview.ptrlib.PtrUIHandler;
15 | import com.zly.www.easyrecyclerview.ptrlib.PtrUIHandlerHook;
16 | import com.zly.www.easyrecyclerview.ptrlib.indicator.PtrIndicator;
17 |
18 |
19 | public class MaterialHeader extends View implements PtrUIHandler {
20 |
21 | private MaterialProgressDrawable mDrawable;
22 | private float mScale = 1f;
23 | private PtrFrameLayout mPtrFrameLayout;
24 |
25 | private Animation mScaleAnimation = new Animation() {
26 | @Override
27 | public void applyTransformation(float interpolatedTime, Transformation t) {
28 | mScale = 1f - interpolatedTime;
29 | mDrawable.setAlpha((int) (255 * mScale));
30 | invalidate();
31 | }
32 | };
33 |
34 | public MaterialHeader(Context context) {
35 | super(context);
36 | initView();
37 | }
38 |
39 | public MaterialHeader(Context context, AttributeSet attrs) {
40 | super(context, attrs);
41 | initView();
42 | }
43 |
44 | public MaterialHeader(Context context, AttributeSet attrs, int defStyleAttr) {
45 | super(context, attrs, defStyleAttr);
46 | initView();
47 | }
48 |
49 | public void setPtrFrameLayout(PtrFrameLayout layout) {
50 |
51 | final PtrUIHandlerHook mPtrUIHandlerHook = new PtrUIHandlerHook() {
52 | @Override
53 | public void run() {
54 | startAnimation(mScaleAnimation);
55 | }
56 | };
57 |
58 | mScaleAnimation.setDuration(200);
59 | mScaleAnimation.setAnimationListener(new Animation.AnimationListener() {
60 | @Override
61 | public void onAnimationStart(Animation animation) {
62 |
63 | }
64 |
65 | @Override
66 | public void onAnimationEnd(Animation animation) {
67 | mPtrUIHandlerHook.resume();
68 | }
69 |
70 | @Override
71 | public void onAnimationRepeat(Animation animation) {
72 |
73 | }
74 | });
75 |
76 | mPtrFrameLayout = layout;
77 | mPtrFrameLayout.setRefreshCompleteHook(mPtrUIHandlerHook);
78 | }
79 |
80 | private void initView() {
81 | mDrawable = new MaterialProgressDrawable(getContext(), this);
82 | mDrawable.setBackgroundColor(Color.WHITE);
83 | mDrawable.setCallback(this);
84 | }
85 |
86 | @Override
87 | public void invalidateDrawable(Drawable dr) {
88 | if (dr == mDrawable) {
89 | invalidate();
90 | } else {
91 | super.invalidateDrawable(dr);
92 | }
93 | }
94 |
95 | public void setColorSchemeColors(int[] colors) {
96 | mDrawable.setColorSchemeColors(colors);
97 | invalidate();
98 | }
99 |
100 | @Override
101 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
102 | int height = mDrawable.getIntrinsicHeight() + getPaddingTop() + getPaddingBottom();
103 | heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
104 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
105 | }
106 |
107 | @Override
108 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
109 | final int size = mDrawable.getIntrinsicHeight();
110 | mDrawable.setBounds(0, 0, size, size);
111 | }
112 |
113 | @Override
114 | protected void onDraw(Canvas canvas) {
115 | final int saveCount = canvas.save();
116 | Rect rect = mDrawable.getBounds();
117 | int l = getPaddingLeft() + (getMeasuredWidth() - mDrawable.getIntrinsicWidth()) / 2;
118 | canvas.translate(l, getPaddingTop());
119 | canvas.scale(mScale, mScale, rect.exactCenterX(), rect.exactCenterY());
120 | mDrawable.draw(canvas);
121 | canvas.restoreToCount(saveCount);
122 | }
123 |
124 | /**
125 | * When the content view has reached top and refresh has been completed, view will be reset.
126 | *
127 | * @param frame
128 | */
129 | @Override
130 | public void onUIReset(PtrFrameLayout frame) {
131 | mScale = 1f;
132 | mDrawable.stop();
133 | }
134 |
135 | /**
136 | * prepare for loading
137 | *
138 | * @param frame
139 | */
140 | @Override
141 | public void onUIRefreshPrepare(PtrFrameLayout frame) {
142 | }
143 |
144 | /**
145 | * perform refreshing UI
146 | *
147 | * @param frame
148 | */
149 | @Override
150 | public void onUIRefreshBegin(PtrFrameLayout frame) {
151 | mDrawable.setAlpha(255);
152 | mDrawable.start();
153 | }
154 |
155 | /**
156 | * perform UI after refresh
157 | *
158 | * @param frame
159 | */
160 | @Override
161 | public void onUIRefreshComplete(PtrFrameLayout frame) {
162 | mDrawable.stop();
163 | }
164 |
165 | @Override
166 | public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {
167 |
168 | float percent = Math.min(1f, ptrIndicator.getCurrentPercent());
169 |
170 | if (status == PtrFrameLayout.PTR_STATUS_PREPARE) {
171 | mDrawable.setAlpha((int) (255 * percent));
172 | mDrawable.showArrow(true);
173 |
174 | float strokeStart = ((percent) * .8f);
175 | mDrawable.setStartEndTrim(0f, Math.min(0.8f, strokeStart));
176 | mDrawable.setArrowScale(Math.min(1f, percent));
177 |
178 | // magic
179 | float rotation = (-0.25f + .4f * percent + percent * 2) * .5f;
180 | mDrawable.setProgressRotation(rotation);
181 | invalidate();
182 | }
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/decoration/StickItemDecoration.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.decoration;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.view.View;
11 |
12 | import com.zly.www.easyrecyclerview.utils.DisplayUtil;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * 顶部吸附ItemDecoration
18 | * Created by zly on 2016/11/28 0028.
19 | */
20 |
21 | public abstract class StickItemDecoration extends RecyclerView.ItemDecoration {
22 |
23 | private Context mContext;
24 | private Paint mPaint;
25 | private List mList;
26 |
27 | private int mStickHeight;
28 | private int mStickBackgroundColor;
29 | private int mStickTextColor;
30 | private int mStickTextSize;
31 | private int mStickTextoffset;
32 |
33 | public StickItemDecoration(Context context, List mList) {
34 | this.mList = mList;
35 | this.mContext = context;
36 | this.mPaint = new Paint();
37 |
38 | initProperty();
39 | }
40 |
41 | private void initProperty() {
42 | mStickHeight = DisplayUtil.dip2px(mContext, 30);
43 | mStickTextSize = DisplayUtil.sp2px(mContext, 16);
44 | mStickTextoffset = DisplayUtil.dip2px(mContext, 2);
45 |
46 | mStickTextColor = Color.WHITE;
47 | mStickBackgroundColor = 0xffA3A3A3;
48 |
49 | mPaint.setTextSize(mStickTextSize);
50 | mPaint.setTextAlign(Paint.Align.LEFT);
51 | }
52 |
53 | @Override
54 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
55 | super.onDraw(c, parent, state);
56 | int left = parent.getPaddingLeft();
57 | int right = parent.getWidth() - parent.getPaddingRight();
58 |
59 | int childCount = parent.getChildCount();
60 | for (int i = 0; i < childCount; i++) {
61 | View view = parent.getChildAt(i);
62 | int position = parent.getChildAdapterPosition(view);
63 | if (position != RecyclerView.NO_POSITION) {
64 | if (position == 0) {
65 | drawStickDecoration(left, view.getTop() - mStickHeight, right, view.getTop(), parent.getPaddingTop(), getTag(position), c);
66 | } else {
67 | if (position < mList.size() && !getTag(position).equals(getTag(position - 1))) {
68 | drawStickDecoration(left, view.getTop() - mStickHeight, right, view.getTop(), parent.getPaddingTop(), getTag(position), c);
69 | }
70 | }
71 | }
72 | }
73 | }
74 |
75 | @Override
76 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
77 | super.onDrawOver(c, parent, state);
78 | int position = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition();
79 | int left = parent.getPaddingLeft();
80 | int top = parent.getPaddingTop();
81 | int right = parent.getWidth() - parent.getPaddingRight();
82 |
83 | /**
84 | * 增加顶部动画效果
85 | */
86 | View childView = parent.findViewHolderForLayoutPosition(position).itemView;
87 | if (position + 1 < mList.size()) {
88 | if (!getTag(position).equals(getTag(position + 1))) {
89 | if (childView.getHeight() + childView.getTop() < mStickHeight) {
90 | c.translate(0, childView.getHeight() + childView.getTop() - mStickHeight);
91 | }
92 | }
93 | }
94 |
95 | drawStickDecoration(left, top, right, top + mStickHeight, parent.getPaddingTop(), getTag(position), c);
96 | }
97 |
98 | @Override
99 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
100 | int position = parent.getChildAdapterPosition(view);
101 | if (position != RecyclerView.NO_POSITION) {
102 | if (position == 0) {
103 | outRect.set(0, mStickHeight, 0, 0);
104 | } else {
105 | if (position < mList.size() && !getTag(position).equals(getTag(position - 1))) {
106 | outRect.set(0, mStickHeight, 0, 0);
107 | } else {
108 | outRect.set(0, 0, 0, 0);
109 | }
110 | }
111 | }
112 | }
113 |
114 | /**
115 | * 绘制stick
116 | */
117 | private void drawStickDecoration(int left, int top, int right, int bottom, int parentTopPadding, String text, Canvas c) {
118 | if (top >= parentTopPadding) {
119 | Rect rect = new Rect(left, top, right, bottom);
120 | mPaint.setColor(mStickBackgroundColor);
121 | c.drawRect(rect, mPaint);
122 |
123 | mPaint.setColor(mStickTextColor);
124 | Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
125 | float textTop = fontMetrics.top;
126 | float textBottom = fontMetrics.bottom;
127 | int textY = (int) (rect.centerY() - textTop / 2 - textBottom / 2);
128 | c.drawText(text, left + mStickTextoffset, textY, mPaint);
129 | }
130 | }
131 |
132 | public abstract String getTag(int position);
133 |
134 |
135 | public int getStickHeight() {
136 | return mStickHeight;
137 | }
138 |
139 | public void setStickHeight(int mStickHeight) {
140 | this.mStickHeight = mStickHeight;
141 | }
142 |
143 | public int getStickBackgroundColor() {
144 | return mStickBackgroundColor;
145 | }
146 |
147 | public void setStickBackgroundColor(int mStickBackgroundColor) {
148 | this.mStickBackgroundColor = mStickBackgroundColor;
149 | }
150 |
151 | public int getStickTextColor() {
152 | return mStickTextColor;
153 | }
154 |
155 | public void setStickTextColor(int mStickTextColor) {
156 | this.mStickTextColor = mStickTextColor;
157 | }
158 |
159 | public int getStickTextSize() {
160 | return mStickTextSize;
161 | }
162 |
163 | public void setStickTextSize(int mStickTextSize) {
164 | this.mStickTextSize = mStickTextSize;
165 | }
166 |
167 | public int getStickTextoffset() {
168 | return mStickTextoffset;
169 | }
170 |
171 | public void setStickTextoffset(int mStickTextoffset) {
172 | this.mStickTextoffset = mStickTextoffset;
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/util/PtrCLog.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib.util;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * An encapsulation of {@link Log}, enable log level and print log with parameters.
7 | *
8 | * @author http://www.liaohuqiu.net/
9 | */
10 | public class PtrCLog {
11 |
12 | public static final int LEVEL_VERBOSE = 0;
13 | public static final int LEVEL_DEBUG = 1;
14 | public static final int LEVEL_INFO = 2;
15 | public static final int LEVEL_WARNING = 3;
16 | public static final int LEVEL_ERROR = 4;
17 | public static final int LEVEL_FATAL = 5;
18 |
19 | private static int sLevel = LEVEL_VERBOSE;
20 |
21 | /**
22 | * set log level, the level lower than this level will not be logged
23 | *
24 | * @param level
25 | */
26 | public static void setLogLevel(int level) {
27 | sLevel = level;
28 | }
29 |
30 | /**
31 | * Send a VERBOSE log message.
32 | *
33 | * @param tag
34 | * @param msg
35 | */
36 | public static void v(String tag, String msg) {
37 | if (sLevel > LEVEL_VERBOSE) {
38 | return;
39 | }
40 | Log.v(tag, msg);
41 | }
42 |
43 | /**
44 | * Send a VERBOSE log message.
45 | *
46 | * @param tag
47 | * @param msg
48 | * @param throwable
49 | */
50 | public static void v(String tag, String msg, Throwable throwable) {
51 | if (sLevel > LEVEL_VERBOSE) {
52 | return;
53 | }
54 | Log.v(tag, msg, throwable);
55 | }
56 |
57 | /**
58 | * Send a VERBOSE log message.
59 | *
60 | * @param tag
61 | * @param msg
62 | * @param args
63 | */
64 | public static void v(String tag, String msg, Object... args) {
65 | if (sLevel > LEVEL_VERBOSE) {
66 | return;
67 | }
68 | if (args.length > 0) {
69 | msg = String.format(msg, args);
70 | }
71 | Log.v(tag, msg);
72 | }
73 |
74 | /**
75 | * Send a DEBUG log message
76 | *
77 | * @param tag
78 | * @param msg
79 | */
80 | public static void d(String tag, String msg) {
81 | if (sLevel > LEVEL_DEBUG) {
82 | return;
83 | }
84 | Log.d(tag, msg);
85 | }
86 |
87 | /**
88 | * Send a DEBUG log message
89 | *
90 | * @param tag
91 | * @param msg
92 | * @param args
93 | */
94 | public static void d(String tag, String msg, Object... args) {
95 | if (sLevel > LEVEL_DEBUG) {
96 | return;
97 | }
98 | if (args.length > 0) {
99 | msg = String.format(msg, args);
100 | }
101 | Log.d(tag, msg);
102 | }
103 |
104 | /**
105 | * Send a DEBUG log message
106 | *
107 | * @param tag
108 | * @param msg
109 | * @param throwable
110 | */
111 | public static void d(String tag, String msg, Throwable throwable) {
112 | if (sLevel > LEVEL_DEBUG) {
113 | return;
114 | }
115 | Log.d(tag, msg, throwable);
116 | }
117 |
118 | /**
119 | * Send an INFO log message
120 | *
121 | * @param tag
122 | * @param msg
123 | */
124 | public static void i(String tag, String msg) {
125 | if (sLevel > LEVEL_INFO) {
126 | return;
127 | }
128 | Log.i(tag, msg);
129 | }
130 |
131 | /**
132 | * Send an INFO log message
133 | *
134 | * @param tag
135 | * @param msg
136 | * @param args
137 | */
138 | public static void i(String tag, String msg, Object... args) {
139 | if (sLevel > LEVEL_INFO) {
140 | return;
141 | }
142 | if (args.length > 0) {
143 | msg = String.format(msg, args);
144 | }
145 | Log.i(tag, msg);
146 | }
147 |
148 | /**
149 | * Send an INFO log message
150 | *
151 | * @param tag
152 | * @param msg
153 | */
154 | public static void i(String tag, String msg, Throwable throwable) {
155 | if (sLevel > LEVEL_INFO) {
156 | return;
157 | }
158 | Log.i(tag, msg, throwable);
159 | }
160 |
161 | /**
162 | * Send a WARNING log message
163 | *
164 | * @param tag
165 | * @param msg
166 | */
167 | public static void w(String tag, String msg) {
168 | if (sLevel > LEVEL_WARNING) {
169 | return;
170 | }
171 | Log.w(tag, msg);
172 | }
173 |
174 | /**
175 | * Send a WARNING log message
176 | *
177 | * @param tag
178 | * @param msg
179 | * @param args
180 | */
181 | public static void w(String tag, String msg, Object... args) {
182 | if (sLevel > LEVEL_WARNING) {
183 | return;
184 | }
185 | if (args.length > 0) {
186 | msg = String.format(msg, args);
187 | }
188 | Log.w(tag, msg);
189 | }
190 |
191 | /**
192 | * Send a WARNING log message
193 | *
194 | * @param tag
195 | * @param msg
196 | * @param throwable
197 | */
198 | public static void w(String tag, String msg, Throwable throwable) {
199 | if (sLevel > LEVEL_WARNING) {
200 | return;
201 | }
202 | Log.w(tag, msg, throwable);
203 | }
204 |
205 | /**
206 | * Send an ERROR log message
207 | *
208 | * @param tag
209 | * @param msg
210 | */
211 | public static void e(String tag, String msg) {
212 | if (sLevel > LEVEL_ERROR) {
213 | return;
214 | }
215 | Log.e(tag, msg);
216 | }
217 |
218 | /**
219 | * Send an ERROR log message
220 | *
221 | * @param tag
222 | * @param msg
223 | * @param args
224 | */
225 | public static void e(String tag, String msg, Object... args) {
226 | if (sLevel > LEVEL_ERROR) {
227 | return;
228 | }
229 | if (args.length > 0) {
230 | msg = String.format(msg, args);
231 | }
232 | Log.e(tag, msg);
233 | }
234 |
235 | /**
236 | * Send an ERROR log message
237 | *
238 | * @param tag
239 | * @param msg
240 | * @param throwable
241 | */
242 | public static void e(String tag, String msg, Throwable throwable) {
243 | if (sLevel > LEVEL_ERROR) {
244 | return;
245 | }
246 | Log.e(tag, msg, throwable);
247 | }
248 |
249 | /**
250 | * Send a FATAL ERROR log message
251 | *
252 | * @param tag
253 | * @param msg
254 | */
255 | public static void f(String tag, String msg) {
256 | if (sLevel > LEVEL_FATAL) {
257 | return;
258 | }
259 | Log.wtf(tag, msg);
260 | }
261 |
262 | /**
263 | * Send a FATAL ERROR log message
264 | *
265 | * @param tag
266 | * @param msg
267 | * @param args
268 | */
269 | public static void f(String tag, String msg, Object... args) {
270 | if (sLevel > LEVEL_FATAL) {
271 | return;
272 | }
273 | if (args.length > 0) {
274 | msg = String.format(msg, args);
275 | }
276 | Log.wtf(tag, msg);
277 | }
278 |
279 | /**
280 | * Send a FATAL ERROR log message
281 | *
282 | * @param tag
283 | * @param msg
284 | * @param throwable
285 | */
286 | public static void f(String tag, String msg, Throwable throwable) {
287 | if (sLevel > LEVEL_FATAL) {
288 | return;
289 | }
290 | Log.wtf(tag, msg, throwable);
291 | }
292 | }
293 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/adapter/CommonAdapter.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.LayoutRes;
5 | import android.support.annotation.NonNull;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.Log;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 |
12 | import com.zly.www.easyrecyclerview.adapter.viewholder.BaseViewHolder;
13 | import com.zly.www.easyrecyclerview.adapter.viewholder.FooterViewHolder;
14 |
15 | import java.util.ArrayList;
16 | import java.util.Collection;
17 | import java.util.Collections;
18 | import java.util.Comparator;
19 | import java.util.List;
20 |
21 | /**
22 | * 通用adapter
23 | * Created by zly on 2016/10/27 0027.
24 | */
25 |
26 | public abstract class CommonAdapter extends RecyclerView.Adapter {
27 |
28 | public static final int TYPE_FOOTER = 233333;
29 | private final String TAG = "CommonAdapter";
30 | private final Object mLock = new Object();
31 | private Context mContext;
32 |
33 | private View mFooter;
34 | private RecyclerView mRecyclerView;
35 | private List mDatas = new ArrayList<>();
36 |
37 | @Override
38 | public int getItemCount() {
39 | return mDatas == null || mDatas.size() == 0 ? 0 : mFooter == null ? mDatas.size() : mDatas.size() + 1;
40 | }
41 |
42 | @Override
43 | public int getItemViewType(int position) {
44 | if (getFooter() != null && getItemCount() - 1 == position) {
45 | return TYPE_FOOTER;
46 | } else {
47 | return super.getItemViewType(position);
48 | }
49 | }
50 |
51 | @Override
52 | public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
53 | if (mContext == null)
54 | mContext = parent.getContext();
55 | if (mRecyclerView == null)
56 | mRecyclerView = (RecyclerView) parent;
57 |
58 | if (viewType == TYPE_FOOTER) {
59 | return new FooterViewHolder(mFooter);
60 | }
61 | return createCustomViewHolder(parent, viewType);
62 | }
63 |
64 | @Override
65 | public void onBindViewHolder(BaseViewHolder holder, int position) {
66 | if (getItemViewType(position) != TYPE_FOOTER)
67 | bindCustomViewHolder((VH) holder, mDatas.get(position), position);
68 | }
69 |
70 | public View inflateView(@LayoutRes int resId, ViewGroup parent) {
71 | return LayoutInflater.from(mContext).inflate(resId, parent, false);
72 | }
73 |
74 | public Context getContext() {
75 | return mContext;
76 | }
77 |
78 | public View getFooter() {
79 | return mFooter;
80 | }
81 |
82 | public void setFooter(View footer) {
83 | mFooter = footer;
84 | }
85 |
86 | public void removeFooter() {
87 | mFooter = null;
88 | }
89 |
90 | public void add(@NonNull T object) {
91 | synchronized (mLock) {
92 | if (null != mDatas) {
93 | mDatas.add(object);
94 | notifyItemInserted(mDatas.size() - 1);
95 | }
96 | }
97 | }
98 |
99 | public void addAll(@NonNull Collection extends T> collection) {
100 | synchronized (mLock) {
101 | if (null != mDatas) {
102 | mDatas.addAll(collection);
103 |
104 | if (mDatas.size() - collection.size() != 0) {
105 | notifyItemRangeInserted(mDatas.size() - collection.size(), collection.size());
106 | } else {
107 | notifyDataSetChanged();
108 | }
109 | }
110 | }
111 |
112 | }
113 |
114 | @SafeVarargs
115 | public final void addAll(@NonNull T... items) {
116 | synchronized (mLock) {
117 | if (null != mDatas) {
118 | Collections.addAll(mDatas, items);
119 | if (mDatas.size() - items.length != 0) {
120 | notifyItemRangeInserted(mDatas.size() - items.length, items.length);
121 | } else {
122 | notifyDataSetChanged();
123 | }
124 | }
125 | }
126 | }
127 |
128 | public void insert(@NonNull T object, int index) {
129 | if (mDatas == null || index < 0 || index > mDatas.size()) {
130 | Log.i(TAG, "insert: index error");
131 | return;
132 | }
133 | synchronized (mLock) {
134 | if (null != mDatas) {
135 | mDatas.add(index, object);
136 | notifyItemInserted(index);
137 |
138 | if (index == 0)
139 | scrollTop();
140 | }
141 | }
142 | }
143 |
144 | public void insertAll(@NonNull Collection extends T> collection, int index) {
145 | if (mDatas == null || index < 0 || index > mDatas.size()) {
146 | Log.i(TAG, "insertAll: index error");
147 | return;
148 | }
149 | synchronized (mLock) {
150 | if (null != mDatas) {
151 | mDatas.addAll(index, collection);
152 | notifyItemRangeInserted(index, collection.size());
153 |
154 | if (index == 0)
155 | scrollTop();
156 | }
157 | }
158 | }
159 |
160 | public void remove(int index) {
161 | if (mDatas == null || index < 0 || index > mDatas.size() - 1) {
162 | Log.i(TAG, "remove: index error");
163 | return;
164 | }
165 | synchronized (mLock) {
166 | mDatas.remove(index);
167 | notifyItemRemoved(index);
168 | }
169 | }
170 |
171 | public boolean remove(@NonNull T object) {
172 | int removeIndex = -1;
173 | boolean removeSuccess = false;
174 |
175 | if (mDatas == null || mDatas.size() == 0) {
176 | Log.i(TAG, "remove fail datas emply");
177 | return false;
178 | }
179 |
180 | synchronized (mLock) {
181 | removeIndex = mDatas.indexOf(object);
182 | removeSuccess = mDatas.remove(object);
183 | }
184 |
185 | if (removeSuccess) {
186 | notifyItemRemoved(removeIndex);
187 | return true;
188 | }
189 | return false;
190 | }
191 |
192 | public void clear() {
193 | synchronized (mLock) {
194 | if (mDatas != null) {
195 | mDatas.clear();
196 | }
197 | }
198 | notifyDataSetChanged();
199 | }
200 |
201 | public void sort(Comparator super T> comparator) {
202 | synchronized (mLock) {
203 | if (mDatas != null) {
204 | Collections.sort(mDatas, comparator);
205 | }
206 | }
207 | notifyDataSetChanged();
208 | }
209 |
210 | public void update(@NonNull List mDatas) {
211 | synchronized (mLock) {
212 | this.mDatas = mDatas;
213 | }
214 | notifyDataSetChanged();
215 | }
216 |
217 | public T getItem(int position) {
218 | return mDatas.get(position);
219 | }
220 |
221 | public int getPosition(T item) {
222 | return mDatas.indexOf(item);
223 | }
224 |
225 | public List getData() {
226 | if (null == mDatas)
227 | mDatas = new ArrayList<>();
228 | return mDatas;
229 | }
230 |
231 | /**
232 | * 处理insert index为0时新数据未显示问题
233 | */
234 | private void scrollTop() {
235 | if (null != mRecyclerView)
236 | mRecyclerView.scrollToPosition(0);
237 | }
238 |
239 | public abstract VH createCustomViewHolder(ViewGroup parent, int viewType);
240 |
241 | public abstract void bindCustomViewHolder(VH holder, T t, int position);
242 |
243 | }
244 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/PtrClassicDefaultHeader.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.content.res.TypedArray;
6 | import android.text.TextUtils;
7 | import android.util.AttributeSet;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.animation.LinearInterpolator;
11 | import android.view.animation.RotateAnimation;
12 | import android.widget.FrameLayout;
13 | import android.widget.TextView;
14 |
15 | import com.zly.www.easyrecyclerview.R;
16 | import com.zly.www.easyrecyclerview.ptrlib.indicator.PtrIndicator;
17 |
18 | import java.text.SimpleDateFormat;
19 | import java.util.Date;
20 |
21 |
22 | public class PtrClassicDefaultHeader extends FrameLayout implements PtrUIHandler {
23 |
24 | private final static String KEY_SharedPreferences = "cube_ptr_classic_last_update";
25 | private static SimpleDateFormat sDataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
26 | private int mRotateAniTime = 150;
27 | private RotateAnimation mFlipAnimation;
28 | private RotateAnimation mReverseFlipAnimation;
29 | private TextView mTitleTextView;
30 | private View mRotateView;
31 | private View mProgressBar;
32 | private long mLastUpdateTime = -1;
33 | private TextView mLastUpdateTextView;
34 | private String mLastUpdateTimeKey;
35 | private boolean mShouldShowLastUpdate;
36 |
37 | private LastUpdateTimeUpdater mLastUpdateTimeUpdater = new LastUpdateTimeUpdater();
38 |
39 | public PtrClassicDefaultHeader(Context context) {
40 | super(context);
41 | initViews(null);
42 | }
43 |
44 | public PtrClassicDefaultHeader(Context context, AttributeSet attrs) {
45 | super(context, attrs);
46 | initViews(attrs);
47 | }
48 |
49 | public PtrClassicDefaultHeader(Context context, AttributeSet attrs, int defStyle) {
50 | super(context, attrs, defStyle);
51 | initViews(attrs);
52 | }
53 |
54 | protected void initViews(AttributeSet attrs) {
55 | TypedArray arr = getContext().obtainStyledAttributes(attrs, R.styleable.PtrClassicHeader, 0, 0);
56 | if (arr != null) {
57 | mRotateAniTime = arr.getInt(R.styleable.PtrClassicHeader_ptr_rotate_ani_time, mRotateAniTime);
58 | }
59 | buildAnimation();
60 | View header = LayoutInflater.from(getContext()).inflate(R.layout.cube_ptr_classic_default_header, this);
61 |
62 | mRotateView = header.findViewById(R.id.ptr_classic_header_rotate_view);
63 |
64 | mTitleTextView = (TextView) header.findViewById(R.id.ptr_classic_header_rotate_view_header_title);
65 | mLastUpdateTextView = (TextView) header.findViewById(R.id.ptr_classic_header_rotate_view_header_last_update);
66 | mProgressBar = header.findViewById(R.id.ptr_classic_header_rotate_view_progressbar);
67 |
68 | resetView();
69 | }
70 |
71 | @Override
72 | protected void onDetachedFromWindow() {
73 | super.onDetachedFromWindow();
74 | if (mLastUpdateTimeUpdater != null) {
75 | mLastUpdateTimeUpdater.stop();
76 | }
77 | }
78 |
79 | public void setRotateAniTime(int time) {
80 | if (time == mRotateAniTime || time == 0) {
81 | return;
82 | }
83 | mRotateAniTime = time;
84 | buildAnimation();
85 | }
86 |
87 | /**
88 | * Specify the last update time by this key string
89 | *
90 | * @param key
91 | */
92 | public void setLastUpdateTimeKey(String key) {
93 | if (TextUtils.isEmpty(key)) {
94 | return;
95 | }
96 | mLastUpdateTimeKey = key;
97 | }
98 |
99 | /**
100 | * Using an object to specify the last update time.
101 | *
102 | * @param object
103 | */
104 | public void setLastUpdateTimeRelateObject(Object object) {
105 | setLastUpdateTimeKey(object.getClass().getName());
106 | }
107 |
108 | private void buildAnimation() {
109 | mFlipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
110 | mFlipAnimation.setInterpolator(new LinearInterpolator());
111 | mFlipAnimation.setDuration(mRotateAniTime);
112 | mFlipAnimation.setFillAfter(true);
113 |
114 | mReverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
115 | mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
116 | mReverseFlipAnimation.setDuration(mRotateAniTime);
117 | mReverseFlipAnimation.setFillAfter(true);
118 | }
119 |
120 | private void resetView() {
121 | hideRotateView();
122 | mProgressBar.setVisibility(INVISIBLE);
123 | }
124 |
125 | private void hideRotateView() {
126 | mRotateView.clearAnimation();
127 | mRotateView.setVisibility(INVISIBLE);
128 | }
129 |
130 | @Override
131 | public void onUIReset(PtrFrameLayout frame) {
132 | resetView();
133 | mShouldShowLastUpdate = true;
134 | tryUpdateLastUpdateTime();
135 | }
136 |
137 | @Override
138 | public void onUIRefreshPrepare(PtrFrameLayout frame) {
139 |
140 | mShouldShowLastUpdate = true;
141 | tryUpdateLastUpdateTime();
142 | mLastUpdateTimeUpdater.start();
143 |
144 | mProgressBar.setVisibility(INVISIBLE);
145 |
146 | mRotateView.setVisibility(VISIBLE);
147 | mTitleTextView.setVisibility(VISIBLE);
148 | if (frame.isPullToRefresh()) {
149 | mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down_to_refresh));
150 | } else {
151 | mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down));
152 | }
153 | }
154 |
155 | @Override
156 | public void onUIRefreshBegin(PtrFrameLayout frame) {
157 | mShouldShowLastUpdate = false;
158 | hideRotateView();
159 | mProgressBar.setVisibility(VISIBLE);
160 | mTitleTextView.setVisibility(VISIBLE);
161 | mTitleTextView.setText(R.string.cube_ptr_refreshing);
162 |
163 | tryUpdateLastUpdateTime();
164 | mLastUpdateTimeUpdater.stop();
165 | }
166 |
167 | @Override
168 | public void onUIRefreshComplete(PtrFrameLayout frame) {
169 |
170 | hideRotateView();
171 | mProgressBar.setVisibility(INVISIBLE);
172 |
173 | mTitleTextView.setVisibility(VISIBLE);
174 | mTitleTextView.setText(getResources().getString(R.string.cube_ptr_refresh_complete));
175 |
176 | // update last update time
177 | SharedPreferences sharedPreferences = getContext().getSharedPreferences(KEY_SharedPreferences, 0);
178 | if (!TextUtils.isEmpty(mLastUpdateTimeKey)) {
179 | mLastUpdateTime = new Date().getTime();
180 | sharedPreferences.edit().putLong(mLastUpdateTimeKey, mLastUpdateTime).commit();
181 | }
182 | }
183 |
184 | private void tryUpdateLastUpdateTime() {
185 | if (TextUtils.isEmpty(mLastUpdateTimeKey) || !mShouldShowLastUpdate) {
186 | mLastUpdateTextView.setVisibility(GONE);
187 | } else {
188 | String time = getLastUpdateTime();
189 | if (TextUtils.isEmpty(time)) {
190 | mLastUpdateTextView.setVisibility(GONE);
191 | } else {
192 | mLastUpdateTextView.setVisibility(VISIBLE);
193 | mLastUpdateTextView.setText(time);
194 | }
195 | }
196 | }
197 |
198 | private String getLastUpdateTime() {
199 |
200 | if (mLastUpdateTime == -1 && !TextUtils.isEmpty(mLastUpdateTimeKey)) {
201 | mLastUpdateTime = getContext().getSharedPreferences(KEY_SharedPreferences, 0).getLong(mLastUpdateTimeKey, -1);
202 | }
203 | if (mLastUpdateTime == -1) {
204 | return null;
205 | }
206 | long diffTime = new Date().getTime() - mLastUpdateTime;
207 | int seconds = (int) (diffTime / 1000);
208 | if (diffTime < 0) {
209 | return null;
210 | }
211 | if (seconds <= 0) {
212 | return null;
213 | }
214 | StringBuilder sb = new StringBuilder();
215 | sb.append(getContext().getString(R.string.cube_ptr_last_update));
216 |
217 | if (seconds < 60) {
218 | sb.append(seconds + getContext().getString(R.string.cube_ptr_seconds_ago));
219 | } else {
220 | int minutes = (seconds / 60);
221 | if (minutes > 60) {
222 | int hours = minutes / 60;
223 | if (hours > 24) {
224 | Date date = new Date(mLastUpdateTime);
225 | sb.append(sDataFormat.format(date));
226 | } else {
227 | sb.append(hours + getContext().getString(R.string.cube_ptr_hours_ago));
228 | }
229 |
230 | } else {
231 | sb.append(minutes + getContext().getString(R.string.cube_ptr_minutes_ago));
232 | }
233 | }
234 | return sb.toString();
235 | }
236 |
237 | @Override
238 | public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {
239 |
240 | final int mOffsetToRefresh = frame.getOffsetToRefresh();
241 | final int currentPos = ptrIndicator.getCurrentPosY();
242 | final int lastPos = ptrIndicator.getLastPosY();
243 |
244 | if (currentPos < mOffsetToRefresh && lastPos >= mOffsetToRefresh) {
245 | if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
246 | crossRotateLineFromBottomUnderTouch(frame);
247 | if (mRotateView != null) {
248 | mRotateView.clearAnimation();
249 | mRotateView.startAnimation(mReverseFlipAnimation);
250 | }
251 | }
252 | } else if (currentPos > mOffsetToRefresh && lastPos <= mOffsetToRefresh) {
253 | if (isUnderTouch && status == PtrFrameLayout.PTR_STATUS_PREPARE) {
254 | crossRotateLineFromTopUnderTouch(frame);
255 | if (mRotateView != null) {
256 | mRotateView.clearAnimation();
257 | mRotateView.startAnimation(mFlipAnimation);
258 | }
259 | }
260 | }
261 | }
262 |
263 | private void crossRotateLineFromTopUnderTouch(PtrFrameLayout frame) {
264 | if (!frame.isPullToRefresh()) {
265 | mTitleTextView.setVisibility(VISIBLE);
266 | mTitleTextView.setText(R.string.cube_ptr_release_to_refresh);
267 | }
268 | }
269 |
270 | private void crossRotateLineFromBottomUnderTouch(PtrFrameLayout frame) {
271 | mTitleTextView.setVisibility(VISIBLE);
272 | if (frame.isPullToRefresh()) {
273 | mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down_to_refresh));
274 | } else {
275 | mTitleTextView.setText(getResources().getString(R.string.cube_ptr_pull_down));
276 | }
277 | }
278 |
279 | private class LastUpdateTimeUpdater implements Runnable {
280 |
281 | private boolean mRunning = false;
282 |
283 | private void start() {
284 | if (TextUtils.isEmpty(mLastUpdateTimeKey)) {
285 | return;
286 | }
287 | mRunning = true;
288 | run();
289 | }
290 |
291 | private void stop() {
292 | mRunning = false;
293 | removeCallbacks(this);
294 | }
295 |
296 | @Override
297 | public void run() {
298 | tryUpdateLastUpdateTime();
299 | if (mRunning) {
300 | postDelayed(this, 1000);
301 | }
302 | }
303 | }
304 | }
305 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/header/StoreHouseHeader.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib.header;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Matrix;
7 | import android.graphics.PointF;
8 | import android.util.AttributeSet;
9 | import android.view.View;
10 | import android.view.animation.Transformation;
11 |
12 | import com.zly.www.easyrecyclerview.ptrlib.PtrFrameLayout;
13 | import com.zly.www.easyrecyclerview.ptrlib.PtrUIHandler;
14 | import com.zly.www.easyrecyclerview.ptrlib.indicator.PtrIndicator;
15 | import com.zly.www.easyrecyclerview.ptrlib.util.PtrLocalDisplay;
16 |
17 | import java.util.ArrayList;
18 |
19 |
20 | public class StoreHouseHeader extends View implements PtrUIHandler {
21 |
22 | public ArrayList mItemList = new ArrayList();
23 |
24 | private int mLineWidth = -1;
25 | private float mScale = 1;
26 | private int mDropHeight = -1;
27 | private float mInternalAnimationFactor = 0.7f;
28 | private int mHorizontalRandomness = -1;
29 |
30 | private float mProgress = 0;
31 |
32 | private int mDrawZoneWidth = 0;
33 | private int mDrawZoneHeight = 0;
34 | private int mOffsetX = 0;
35 | private int mOffsetY = 0;
36 | private float mBarDarkAlpha = 0.4f;
37 | private float mFromAlpha = 1.0f;
38 | private float mToAlpha = 0.4f;
39 |
40 | private int mLoadingAniDuration = 1000;
41 | private int mLoadingAniSegDuration = 1000;
42 | private int mLoadingAniItemDuration = 400;
43 |
44 | private Transformation mTransformation = new Transformation();
45 | private boolean mIsInLoading = false;
46 | private AniController mAniController = new AniController();
47 | private int mTextColor = Color.WHITE;
48 |
49 | public StoreHouseHeader(Context context) {
50 | super(context);
51 | initView();
52 | }
53 |
54 | public StoreHouseHeader(Context context, AttributeSet attrs) {
55 | super(context, attrs);
56 | initView();
57 | }
58 |
59 | public StoreHouseHeader(Context context, AttributeSet attrs, int defStyleAttr) {
60 | super(context, attrs, defStyleAttr);
61 | initView();
62 | }
63 |
64 | private void initView() {
65 | PtrLocalDisplay.init(getContext());
66 | mLineWidth = PtrLocalDisplay.dp2px(1);
67 | mDropHeight = PtrLocalDisplay.dp2px(40);
68 | mHorizontalRandomness = PtrLocalDisplay.SCREEN_WIDTH_PIXELS / 2;
69 | }
70 |
71 | private void setProgress(float progress) {
72 | mProgress = progress;
73 | }
74 |
75 | public int getLoadingAniDuration() {
76 | return mLoadingAniDuration;
77 | }
78 |
79 | public void setLoadingAniDuration(int duration) {
80 | mLoadingAniDuration = duration;
81 | mLoadingAniSegDuration = duration;
82 | }
83 |
84 | public StoreHouseHeader setLineWidth(int width) {
85 | mLineWidth = width;
86 | for (int i = 0; i < mItemList.size(); i++) {
87 | mItemList.get(i).setLineWidth(width);
88 | }
89 | return this;
90 | }
91 |
92 | public StoreHouseHeader setTextColor(int color) {
93 | mTextColor = color;
94 | for (int i = 0; i < mItemList.size(); i++) {
95 | mItemList.get(i).setColor(color);
96 | }
97 | return this;
98 | }
99 |
100 | public StoreHouseHeader setDropHeight(int height) {
101 | mDropHeight = height;
102 | return this;
103 | }
104 |
105 | @Override
106 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
107 | int height = getTopOffset() + mDrawZoneHeight + getBottomOffset();
108 | heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
109 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
110 |
111 | mOffsetX = (getMeasuredWidth() - mDrawZoneWidth) / 2;
112 | mOffsetY = getTopOffset();
113 | mDropHeight = getTopOffset();
114 | }
115 |
116 | private int getTopOffset() {
117 | return getPaddingTop() + PtrLocalDisplay.dp2px(10);
118 | }
119 |
120 | private int getBottomOffset() {
121 | return getPaddingBottom() + PtrLocalDisplay.dp2px(10);
122 | }
123 |
124 | public void initWithString(String str) {
125 | initWithString(str, 25);
126 | }
127 |
128 | public void initWithString(String str, int fontSize) {
129 | ArrayList pointList = StoreHousePath.getPath(str, fontSize * 0.01f, 14);
130 | initWithPointList(pointList);
131 | }
132 |
133 | public void initWithStringArray(int id) {
134 | String[] points = getResources().getStringArray(id);
135 | ArrayList pointList = new ArrayList();
136 | for (int i = 0; i < points.length; i++) {
137 | String[] x = points[i].split(",");
138 | float[] f = new float[4];
139 | for (int j = 0; j < 4; j++) {
140 | f[j] = Float.parseFloat(x[j]);
141 | }
142 | pointList.add(f);
143 | }
144 | initWithPointList(pointList);
145 | }
146 |
147 | public float getScale() {
148 | return mScale;
149 | }
150 |
151 | public void setScale(float scale) {
152 | mScale = scale;
153 | }
154 |
155 | public void initWithPointList(ArrayList pointList) {
156 |
157 | float drawWidth = 0;
158 | float drawHeight = 0;
159 | boolean shouldLayout = mItemList.size() > 0;
160 | mItemList.clear();
161 | for (int i = 0; i < pointList.size(); i++) {
162 | float[] line = pointList.get(i);
163 | PointF startPoint = new PointF(PtrLocalDisplay.dp2px(line[0]) * mScale, PtrLocalDisplay.dp2px(line[1]) * mScale);
164 | PointF endPoint = new PointF(PtrLocalDisplay.dp2px(line[2]) * mScale, PtrLocalDisplay.dp2px(line[3]) * mScale);
165 |
166 | drawWidth = Math.max(drawWidth, startPoint.x);
167 | drawWidth = Math.max(drawWidth, endPoint.x);
168 |
169 | drawHeight = Math.max(drawHeight, startPoint.y);
170 | drawHeight = Math.max(drawHeight, endPoint.y);
171 |
172 | StoreHouseBarItem item = new StoreHouseBarItem(i, startPoint, endPoint, mTextColor, mLineWidth);
173 | item.resetPosition(mHorizontalRandomness);
174 | mItemList.add(item);
175 | }
176 | mDrawZoneWidth = (int) Math.ceil(drawWidth);
177 | mDrawZoneHeight = (int) Math.ceil(drawHeight);
178 | if (shouldLayout) {
179 | requestLayout();
180 | }
181 | }
182 |
183 | private void beginLoading() {
184 | mIsInLoading = true;
185 | mAniController.start();
186 | invalidate();
187 | }
188 |
189 | private void loadFinish() {
190 | mIsInLoading = false;
191 | mAniController.stop();
192 | }
193 |
194 | @Override
195 | public void onDraw(Canvas canvas) {
196 | super.onDraw(canvas);
197 | float progress = mProgress;
198 | int c1 = canvas.save();
199 | int len = mItemList.size();
200 |
201 | for (int i = 0; i < len; i++) {
202 |
203 | canvas.save();
204 | StoreHouseBarItem storeHouseBarItem = mItemList.get(i);
205 | float offsetX = mOffsetX + storeHouseBarItem.midPoint.x;
206 | float offsetY = mOffsetY + storeHouseBarItem.midPoint.y;
207 |
208 | if (mIsInLoading) {
209 | storeHouseBarItem.getTransformation(getDrawingTime(), mTransformation);
210 | canvas.translate(offsetX, offsetY);
211 | } else {
212 |
213 | if (progress == 0) {
214 | storeHouseBarItem.resetPosition(mHorizontalRandomness);
215 | continue;
216 | }
217 |
218 | float startPadding = (1 - mInternalAnimationFactor) * i / len;
219 | float endPadding = 1 - mInternalAnimationFactor - startPadding;
220 |
221 | // done
222 | if (progress == 1 || progress >= 1 - endPadding) {
223 | canvas.translate(offsetX, offsetY);
224 | storeHouseBarItem.setAlpha(mBarDarkAlpha);
225 | } else {
226 | float realProgress;
227 | if (progress <= startPadding) {
228 | realProgress = 0;
229 | } else {
230 | realProgress = Math.min(1, (progress - startPadding) / mInternalAnimationFactor);
231 | }
232 | offsetX += storeHouseBarItem.translationX * (1 - realProgress);
233 | offsetY += -mDropHeight * (1 - realProgress);
234 | Matrix matrix = new Matrix();
235 | matrix.postRotate(360 * realProgress);
236 | matrix.postScale(realProgress, realProgress);
237 | matrix.postTranslate(offsetX, offsetY);
238 | storeHouseBarItem.setAlpha(mBarDarkAlpha * realProgress);
239 | canvas.concat(matrix);
240 | }
241 | }
242 | storeHouseBarItem.draw(canvas);
243 | canvas.restore();
244 | }
245 | if (mIsInLoading) {
246 | invalidate();
247 | }
248 | canvas.restoreToCount(c1);
249 | }
250 |
251 | @Override
252 | public void onUIReset(PtrFrameLayout frame) {
253 | loadFinish();
254 | for (int i = 0; i < mItemList.size(); i++) {
255 | mItemList.get(i).resetPosition(mHorizontalRandomness);
256 |
257 | }
258 | }
259 |
260 | @Override
261 | public void onUIRefreshPrepare(PtrFrameLayout frame) {
262 |
263 | }
264 |
265 | @Override
266 | public void onUIRefreshBegin(PtrFrameLayout frame) {
267 | beginLoading();
268 | }
269 |
270 | @Override
271 | public void onUIRefreshComplete(PtrFrameLayout frame) {
272 | loadFinish();
273 | }
274 |
275 | @Override
276 | public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {
277 |
278 | float currentPercent = Math.min(1f, ptrIndicator.getCurrentPercent());
279 | setProgress(currentPercent);
280 | invalidate();
281 | }
282 |
283 | private class AniController implements Runnable {
284 |
285 | private int mTick = 0;
286 | private int mCountPerSeg = 0;
287 | private int mSegCount = 0;
288 | private int mInterval = 0;
289 | private boolean mRunning = true;
290 |
291 | private void start() {
292 | mRunning = true;
293 | mTick = 0;
294 |
295 | mInterval = mLoadingAniDuration / mItemList.size();
296 | mCountPerSeg = mLoadingAniSegDuration / mInterval;
297 | mSegCount = mItemList.size() / mCountPerSeg + 1;
298 | run();
299 | }
300 |
301 | @Override
302 | public void run() {
303 |
304 | int pos = mTick % mCountPerSeg;
305 | for (int i = 0; i < mSegCount; i++) {
306 |
307 | int index = i * mCountPerSeg + pos;
308 | if (index > mTick) {
309 | continue;
310 | }
311 |
312 | index = index % mItemList.size();
313 | StoreHouseBarItem item = mItemList.get(index);
314 |
315 | item.setFillAfter(false);
316 | item.setFillEnabled(true);
317 | item.setFillBefore(false);
318 | item.setDuration(mLoadingAniItemDuration);
319 | item.start(mFromAlpha, mToAlpha);
320 | }
321 |
322 | mTick++;
323 | if (mRunning) {
324 | postDelayed(this, mInterval);
325 | }
326 | }
327 |
328 | private void stop() {
329 | mRunning = false;
330 | removeCallbacks(this);
331 | }
332 | }
333 | }
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/header/StoreHousePath.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview.ptrlib.header;
2 |
3 | import android.util.SparseArray;
4 |
5 | import java.util.ArrayList;
6 |
7 | /**
8 | * Created by srain on 11/7/14.
9 | */
10 | public class StoreHousePath {
11 |
12 | private static final SparseArray sPointList;
13 |
14 | static {
15 | sPointList = new SparseArray();
16 | float[][] LETTERS = new float[][]{
17 | new float[]{
18 | // A
19 | 24, 0, 1, 22,
20 | 1, 22, 1, 72,
21 | 24, 0, 47, 22,
22 | 47, 22, 47, 72,
23 | 1, 48, 47, 48
24 | },
25 |
26 | new float[]{
27 | // B
28 | 0, 0, 0, 72,
29 | 0, 0, 37, 0,
30 | 37, 0, 47, 11,
31 | 47, 11, 47, 26,
32 | 47, 26, 38, 36,
33 | 38, 36, 0, 36,
34 | 38, 36, 47, 46,
35 | 47, 46, 47, 61,
36 | 47, 61, 38, 71,
37 | 37, 72, 0, 72,
38 | },
39 |
40 | new float[]{
41 | // C
42 | 47, 0, 0, 0,
43 | 0, 0, 0, 72,
44 | 0, 72, 47, 72,
45 | },
46 |
47 | new float[]{
48 | // D
49 | 0, 0, 0, 72,
50 | 0, 0, 24, 0,
51 | 24, 0, 47, 22,
52 | 47, 22, 47, 48,
53 | 47, 48, 23, 72,
54 | 23, 72, 0, 72,
55 | },
56 |
57 | new float[]{
58 | // E
59 | 0, 0, 0, 72,
60 | 0, 0, 47, 0,
61 | 0, 36, 37, 36,
62 | 0, 72, 47, 72,
63 | },
64 |
65 | new float[]{
66 | // F
67 | 0, 0, 0, 72,
68 | 0, 0, 47, 0,
69 | 0, 36, 37, 36,
70 | },
71 |
72 | new float[]{
73 | // G
74 | 47, 23, 47, 0,
75 | 47, 0, 0, 0,
76 | 0, 0, 0, 72,
77 | 0, 72, 47, 72,
78 | 47, 72, 47, 48,
79 | 47, 48, 24, 48,
80 | },
81 |
82 | new float[]{
83 | // H
84 | 0, 0, 0, 72,
85 | 0, 36, 47, 36,
86 | 47, 0, 47, 72,
87 | },
88 |
89 | new float[]{
90 | // I
91 | 0, 0, 47, 0,
92 | 24, 0, 24, 72,
93 | 0, 72, 47, 72,
94 | },
95 |
96 | new float[]{
97 | // J
98 | 47, 0, 47, 72,
99 | 47, 72, 24, 72,
100 | 24, 72, 0, 48,
101 | },
102 |
103 | new float[]{
104 | // K
105 | 0, 0, 0, 72,
106 | 47, 0, 3, 33,
107 | 3, 38, 47, 72,
108 | },
109 |
110 | new float[]{
111 | // L
112 | 0, 0, 0, 72,
113 | 0, 72, 47, 72,
114 | },
115 |
116 | new float[]{
117 | // M
118 | 0, 0, 0, 72,
119 | 0, 0, 24, 23,
120 | 24, 23, 47, 0,
121 | 47, 0, 47, 72,
122 | },
123 |
124 | new float[]{
125 | // N
126 | 0, 0, 0, 72,
127 | 0, 0, 47, 72,
128 | 47, 72, 47, 0,
129 | },
130 |
131 | new float[]{
132 | // O
133 | 0, 0, 0, 72,
134 | 0, 72, 47, 72,
135 | 47, 72, 47, 0,
136 | 47, 0, 0, 0,
137 | },
138 |
139 | new float[]{
140 | // P
141 | 0, 0, 0, 72,
142 | 0, 0, 47, 0,
143 | 47, 0, 47, 36,
144 | 47, 36, 0, 36,
145 | },
146 |
147 | new float[]{
148 | // Q
149 | 0, 0, 0, 72,
150 | 0, 72, 23, 72,
151 | 23, 72, 47, 48,
152 | 47, 48, 47, 0,
153 | 47, 0, 0, 0,
154 | 24, 28, 47, 71,
155 | },
156 |
157 | new float[]{
158 | // R
159 | 0, 0, 0, 72,
160 | 0, 0, 47, 0,
161 | 47, 0, 47, 36,
162 | 47, 36, 0, 36,
163 | 0, 37, 47, 72,
164 | },
165 |
166 | new float[]{
167 | // S
168 | 47, 0, 0, 0,
169 | 0, 0, 0, 36,
170 | 0, 36, 47, 36,
171 | 47, 36, 47, 72,
172 | 47, 72, 0, 72,
173 | },
174 |
175 | new float[]{
176 | // T
177 | 0, 0, 47, 0,
178 | 24, 0, 24, 72,
179 | },
180 |
181 | new float[]{
182 | // U
183 | 0, 0, 0, 72,
184 | 0, 72, 47, 72,
185 | 47, 72, 47, 0,
186 | },
187 |
188 | new float[]{
189 | // V
190 | 0, 0, 24, 72,
191 | 24, 72, 47, 0,
192 | },
193 |
194 | new float[]{
195 | // W
196 | 0, 0, 0, 72,
197 | 0, 72, 24, 49,
198 | 24, 49, 47, 72,
199 | 47, 72, 47, 0
200 | },
201 |
202 | new float[]{
203 | // X
204 | 0, 0, 47, 72,
205 | 47, 0, 0, 72
206 | },
207 |
208 | new float[]{
209 | // Y
210 | 0, 0, 24, 23,
211 | 47, 0, 24, 23,
212 | 24, 23, 24, 72
213 | },
214 |
215 | new float[]{
216 | // Z
217 | 0, 0, 47, 0,
218 | 47, 0, 0, 72,
219 | 0, 72, 47, 72
220 | },
221 | };
222 | final float[][] NUMBERS = new float[][]{
223 | new float[]{
224 | // 0
225 | 0, 0, 0, 72,
226 | 0, 72, 47, 72,
227 | 47, 72, 47, 0,
228 | 47, 0, 0, 0,
229 | },
230 | new float[]{
231 | // 1
232 | 24, 0, 24, 72,
233 | },
234 |
235 | new float[]{
236 | // 2
237 | 0, 0, 47, 0,
238 | 47, 0, 47, 36,
239 | 47, 36, 0, 36,
240 | 0, 36, 0, 72,
241 | 0, 72, 47, 72
242 | },
243 |
244 | new float[]{
245 | // 3
246 | 0, 0, 47, 0,
247 | 47, 0, 47, 36,
248 | 47, 36, 0, 36,
249 | 47, 36, 47, 72,
250 | 47, 72, 0, 72,
251 | },
252 |
253 | new float[]{
254 | // 4
255 | 0, 0, 0, 36,
256 | 0, 36, 47, 36,
257 | 47, 0, 47, 72,
258 | },
259 |
260 | new float[]{
261 | // 5
262 | 0, 0, 0, 36,
263 | 0, 36, 47, 36,
264 | 47, 36, 47, 72,
265 | 47, 72, 0, 72,
266 | 0, 0, 47, 0
267 | },
268 |
269 | new float[]{
270 | // 6
271 | 0, 0, 0, 72,
272 | 0, 72, 47, 72,
273 | 47, 72, 47, 36,
274 | 47, 36, 0, 36
275 | },
276 |
277 | new float[]{
278 | // 7
279 | 0, 0, 47, 0,
280 | 47, 0, 47, 72
281 | },
282 |
283 | new float[]{
284 | // 8
285 | 0, 0, 0, 72,
286 | 0, 72, 47, 72,
287 | 47, 72, 47, 0,
288 | 47, 0, 0, 0,
289 | 0, 36, 47, 36
290 | },
291 |
292 | new float[]{
293 | // 9
294 | 47, 0, 0, 0,
295 | 0, 0, 0, 36,
296 | 0, 36, 47, 36,
297 | 47, 0, 47, 72,
298 | }
299 | };
300 | // A - Z
301 | for (int i = 0; i < LETTERS.length; i++) {
302 | sPointList.append(i + 65, LETTERS[i]);
303 | }
304 | // a - z
305 | for (int i = 0; i < LETTERS.length; i++) {
306 | sPointList.append(i + 65 + 32, LETTERS[i]);
307 | }
308 | // 0 - 9
309 | for (int i = 0; i < NUMBERS.length; i++) {
310 | sPointList.append(i + 48, NUMBERS[i]);
311 | }
312 | // blank
313 | addChar(' ', new float[]{});
314 | // -
315 | addChar('-', new float[]{
316 | 0, 36, 47, 36
317 | });
318 | // .
319 | addChar('.', new float[]{
320 | 24, 60, 24, 72
321 | });
322 | }
323 |
324 | public static void addChar(char c, float[] points) {
325 | sPointList.append(c, points);
326 | }
327 |
328 | public static ArrayList getPath(String str) {
329 | return getPath(str, 1, 14);
330 | }
331 |
332 | /**
333 | * @param str
334 | * @param scale
335 | * @param gapBetweenLetter
336 | * @return ArrayList of float[] {x1, y1, x2, y2}
337 | */
338 | public static ArrayList getPath(String str, float scale, int gapBetweenLetter) {
339 | ArrayList list = new ArrayList();
340 | float offsetForWidth = 0;
341 | for (int i = 0; i < str.length(); i++) {
342 | int pos = str.charAt(i);
343 | int key = sPointList.indexOfKey(pos);
344 | if (key == -1) {
345 | continue;
346 | }
347 | float[] points = sPointList.get(pos);
348 | int pointCount = points.length / 4;
349 |
350 | for (int j = 0; j < pointCount; j++) {
351 | float[] line = new float[4];
352 | for (int k = 0; k < 4; k++) {
353 | float l = points[j * 4 + k];
354 | // x
355 | if (k % 2 == 0) {
356 | line[k] = (l + offsetForWidth) * scale;
357 | }
358 | // y
359 | else {
360 | line[k] = l * scale;
361 | }
362 | }
363 | list.add(line);
364 | }
365 | offsetForWidth += 57 + gapBetweenLetter;
366 | }
367 | return list;
368 | }
369 | }
370 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/EasyRecyclerView.java:
--------------------------------------------------------------------------------
1 | package com.zly.www.easyrecyclerview;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.support.v7.widget.GridLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.AttributeSet;
8 | import android.util.Log;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewStub;
12 | import android.widget.FrameLayout;
13 |
14 | import com.zly.www.easyrecyclerview.adapter.CommonAdapter;
15 | import com.zly.www.easyrecyclerview.footer.ErvLoadUIHandle;
16 | import com.zly.www.easyrecyclerview.listener.OnLoadListener;
17 | import com.zly.www.easyrecyclerview.listener.OnRefreshListener;
18 | import com.zly.www.easyrecyclerview.ptrlib.PtrDefaultHandler;
19 | import com.zly.www.easyrecyclerview.ptrlib.PtrFrameLayout;
20 | import com.zly.www.easyrecyclerview.ptrlib.PtrHandler;
21 | import com.zly.www.easyrecyclerview.ptrlib.PtrUIHandler;
22 | import com.zly.www.easyrecyclerview.utils.LayoutManagerUtil;
23 |
24 |
25 | /**
26 | * 刷新
27 | * Created by zly on 2016/10/20 0020.
28 | */
29 |
30 | public class EasyRecyclerView extends FrameLayout {
31 |
32 | private final String TAG = getClass().getSimpleName();
33 | //Erv属性
34 | private int mNumLoadMore;//最后可见条目 + mNumLoadMore > total 触发加载更多
35 |
36 | //Ptr属性
37 | private float mResistance;
38 | private int mDurationToClose;
39 | private int mDurationToCloseHeader;
40 | private float mRatio;
41 | private boolean mKeepHeaderWhenRefresh;
42 | private boolean mPullToRefresh;
43 |
44 | private boolean mLoadingMore = false;
45 | private boolean mReviseMovePoi = false;//是否需要修正滑动位置
46 | private int mMovePoi = 0;
47 |
48 | private PtrFrameLayout mPtrFrame;
49 | private RecyclerView mRecyclerView;
50 | private OnRefreshListener mRefreshListener;
51 | private OnLoadListener mOnLoadListener;
52 | private View mFooterView;
53 | private CommonAdapter mAdapter;
54 | private ErvLoadUIHandle mLoadUIHandler;
55 | private View mEmptyView;
56 | private ViewStub mStubEmpty;
57 | private int mEmptyRes;
58 |
59 | public EasyRecyclerView(Context context) {
60 | this(context, null);
61 | }
62 |
63 | public EasyRecyclerView(Context context, AttributeSet attrs) {
64 | this(context, attrs, 0);
65 | }
66 |
67 | public EasyRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
68 | super(context, attrs, defStyleAttr);
69 | initAttr(context, attrs);
70 | initView(context);
71 | }
72 |
73 | public void initAttr(Context context, AttributeSet attrs) {
74 | TypedArray PtrAttr = context.obtainStyledAttributes(attrs, R.styleable.PtrFrameLayout);
75 | TypedArray ErvAttr = context.obtainStyledAttributes(attrs, R.styleable.EasyRecyclerView);
76 |
77 | //初始化Erv
78 | mNumLoadMore = ErvAttr.getInteger(R.styleable.EasyRecyclerView_number_load_more, 4);
79 | mEmptyRes = ErvAttr.getResourceId(R.styleable.EasyRecyclerView_emply_layout, 0);
80 |
81 | //初始化Ptr
82 | mResistance = PtrAttr.getFloat(R.styleable.PtrFrameLayout_ptr_resistance, 1.7f);
83 | mRatio = PtrAttr.getFloat(R.styleable.PtrFrameLayout_ptr_ratio_of_header_height_to_refresh, 1.2f);
84 | mDurationToClose = PtrAttr.getInt(R.styleable.PtrFrameLayout_ptr_duration_to_close, 200);
85 | mDurationToCloseHeader = PtrAttr.getInt(R.styleable.PtrFrameLayout_ptr_duration_to_close_header, 1000);
86 | mKeepHeaderWhenRefresh = PtrAttr.getBoolean(R.styleable.PtrFrameLayout_ptr_keep_header_when_refresh, true);
87 | mPullToRefresh = PtrAttr.getBoolean(R.styleable.PtrFrameLayout_ptr_pull_to_fresh, false);
88 |
89 | ErvAttr.recycle();
90 | PtrAttr.recycle();
91 | }
92 |
93 | private void initView(Context context) {
94 | View view = LayoutInflater.from(context).inflate(R.layout.erv_layout, this);
95 | mPtrFrame = (PtrFrameLayout) view.findViewById(R.id.ptr);
96 | mRecyclerView = (RecyclerView) view.findViewById(R.id.list);
97 | mStubEmpty = (ViewStub) view.findViewById(R.id.stubEmpty);
98 |
99 | if (mEmptyRes != 0) {//加载没有数据状态下布局
100 | mStubEmpty.setLayoutResource(mEmptyRes);
101 | mEmptyView = mStubEmpty.inflate();
102 | }
103 |
104 | initPtr();
105 | initRecyclerView();
106 | }
107 |
108 |
109 | /**
110 | * 初始化ptr属性
111 | */
112 | private void initPtr() {
113 | mPtrFrame.setResistance(mResistance);
114 | mPtrFrame.setRatioOfHeaderHeightToRefresh(mRatio);
115 | mPtrFrame.setDurationToClose(mDurationToClose);
116 | mPtrFrame.setDurationToCloseHeader(mDurationToCloseHeader);
117 | mPtrFrame.setKeepHeaderWhenRefresh(mKeepHeaderWhenRefresh);
118 | mPtrFrame.setPullToRefresh(mPullToRefresh);
119 |
120 |
121 | mPtrFrame.setPtrHandler(new PtrHandler() {
122 | @Override
123 | public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
124 | return PtrDefaultHandler.checkContentCanBePulledDown(frame, mRecyclerView, header);
125 | }
126 |
127 | @Override
128 | public void onRefreshBegin(PtrFrameLayout frame) {
129 | if (mRefreshListener != null) {
130 | mRefreshListener.onRefreshListener();
131 | }
132 | }
133 | });
134 | }
135 |
136 |
137 | private void initRecyclerView() {
138 | mRecyclerView.addOnScrollListener(new ReviseMoveListener());
139 | mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
140 | @Override
141 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
142 | super.onScrolled(recyclerView, dx, dy);
143 | if (mRecyclerView.getAdapter() != null && getLayoutManager() != null && mLoadUIHandler != null) {
144 | int itemCount = mRecyclerView.getAdapter().getItemCount();
145 | int lastVisibleItemPosition = LayoutManagerUtil.getLastVisibleItemPosition(getLayoutManager());
146 | if (dy > 0 && itemCount != 0 && lastVisibleItemPosition + mNumLoadMore > itemCount - 1 &&
147 | !mLoadingMore && mLoadUIHandler.getState() == ErvLoadUIHandle.LOAD) {
148 | mLoadingMore = true;
149 | if (mOnLoadListener != null) {
150 | mOnLoadListener.onLoadListener();
151 | }
152 | }
153 | }
154 |
155 | }
156 |
157 | @Override
158 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
159 | super.onScrollStateChanged(recyclerView, newState);
160 | }
161 | });
162 | }
163 |
164 | public void setAdapter(CommonAdapter adapter) {
165 | if (adapter == null) {
166 | throw new NullPointerException("adapter 不能为空");
167 | }
168 | this.mAdapter = adapter;
169 | mRecyclerView.setAdapter(mAdapter);
170 | mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
171 | @Override
172 | public void onChanged() {
173 | super.onChanged();
174 | dataComplete();
175 | }
176 |
177 | @Override
178 | public void onItemRangeChanged(int positionStart, int itemCount) {
179 | super.onItemRangeChanged(positionStart, itemCount);
180 | dataComplete();
181 | }
182 |
183 | @Override
184 | public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
185 | super.onItemRangeChanged(positionStart, itemCount, payload);
186 | dataComplete();
187 | }
188 |
189 | @Override
190 | public void onItemRangeInserted(int positionStart, int itemCount) {
191 | super.onItemRangeInserted(positionStart, itemCount);
192 | dataComplete();
193 | }
194 |
195 | @Override
196 | public void onItemRangeRemoved(int positionStart, int itemCount) {
197 | super.onItemRangeRemoved(positionStart, itemCount);
198 | dataComplete();
199 | }
200 |
201 | @Override
202 | public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
203 | super.onItemRangeMoved(fromPosition, toPosition, itemCount);
204 | dataComplete();
205 | }
206 |
207 | private void dataComplete() {
208 | refreshComplete();
209 | loadComplete();
210 | if (mEmptyView != null) {
211 | if (mAdapter.getItemCount() == 0) {
212 | showEmptyView();
213 | } else {
214 | hideEmptyView();
215 | }
216 | }
217 |
218 | }
219 | });
220 |
221 | addFooter();
222 | }
223 |
224 | public void showEmptyView() {
225 | mRecyclerView.setVisibility(View.GONE);
226 | mStubEmpty.setVisibility(View.VISIBLE);
227 | }
228 |
229 | public void hideEmptyView() {
230 | mRecyclerView.setVisibility(View.VISIBLE);
231 | mStubEmpty.setVisibility(View.GONE);
232 | }
233 |
234 | public void setFooterView(View footerView) {
235 | if (!(footerView instanceof ErvLoadUIHandle)) {
236 | throw new RuntimeException("footerView 必须实现ErvLoadUIHandler");
237 | }
238 | this.mFooterView = footerView;
239 | this.mLoadUIHandler = (ErvLoadUIHandle) footerView;
240 | addFooter();
241 | }
242 |
243 | private void addFooter() {
244 | if (mAdapter != null && mFooterView != null) {
245 | mAdapter.setFooter(mFooterView);
246 | }
247 | }
248 |
249 | public void removeFooter() {
250 | if (mAdapter != null) {
251 | mAdapter.removeFooter();
252 | }
253 | }
254 |
255 | public void loading() {
256 | if (mLoadUIHandler != null) {
257 | mLoadUIHandler.onLoading();
258 | }
259 | }
260 |
261 | public void noMore() {
262 | if (mLoadUIHandler != null) {
263 | mLoadUIHandler.onNoMore();
264 | }
265 | }
266 |
267 | public void loadFail() {
268 | if (mOnLoadListener == null) {
269 | throw new RuntimeException("OnLoadListener 还未设置");
270 | }
271 |
272 | if (mLoadUIHandler != null) {
273 | mLoadUIHandler.onLoadFail(mOnLoadListener);
274 | }
275 | }
276 |
277 | public void setHeaderView(View headerView) {
278 | if (!(headerView instanceof PtrUIHandler)) {
279 | throw new RuntimeException("headerView 必须实现PtrUIHandler");
280 | }
281 | mPtrFrame.setHeaderView(headerView);
282 | mPtrFrame.addPtrUIHandler((PtrUIHandler) headerView);
283 | }
284 |
285 | public void setPtrHandler(PtrHandler ptrHandler) {
286 | mPtrFrame.setPtrHandler(ptrHandler);
287 | }
288 |
289 | public void autoRefresh() {
290 | mPtrFrame.autoRefresh(true, mDurationToCloseHeader);
291 | }
292 |
293 | public void autoRefresh(boolean atOnce) {
294 | mPtrFrame.autoRefresh(atOnce, mDurationToCloseHeader);
295 | }
296 |
297 | public PtrFrameLayout getPtrFrame() {
298 | return mPtrFrame;
299 | }
300 |
301 | public void setResistance(float resistance) {
302 | mPtrFrame.setResistance(resistance);
303 | }
304 |
305 | public void setRatioOfHeaderHeightToRefresh(float ratio) {
306 | mPtrFrame.setRatioOfHeaderHeightToRefresh(ratio);
307 | }
308 |
309 | public void setDurationToClose(int duration) {
310 | mPtrFrame.setDurationToClose(duration);
311 | }
312 |
313 | public void setDurationToCloseHeader(int duration) {
314 | mPtrFrame.setDurationToCloseHeader(duration);
315 | }
316 |
317 | public void setKeepHeaderWhenRefresh(boolean keepOrNot) {
318 | mPtrFrame.setKeepHeaderWhenRefresh(keepOrNot);
319 | }
320 |
321 | public void setPullToRefresh(boolean pullToRefresh) {
322 | mPtrFrame.setPullToRefresh(pullToRefresh);
323 | }
324 |
325 | public void setPinContent(boolean pinContent) {
326 | mPtrFrame.setPinContent(pinContent);
327 | }
328 |
329 | public RecyclerView getRecyclerView() {
330 | return mRecyclerView;
331 | }
332 |
333 | public void addItemDecoration(RecyclerView.ItemDecoration itemDecoration) {
334 | mRecyclerView.addItemDecoration(itemDecoration);
335 | }
336 |
337 | public void setLayoutManager(final RecyclerView.LayoutManager manager) {
338 | if (manager instanceof GridLayoutManager) {
339 | ((GridLayoutManager) manager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
340 | @Override
341 | public int getSpanSize(int position) {
342 | switch (mAdapter.getItemViewType(position)) {
343 | case CommonAdapter.TYPE_FOOTER:
344 | return ((GridLayoutManager) manager).getSpanCount();
345 | default:
346 | return 1;
347 | }
348 | }
349 | });
350 | }
351 | mRecyclerView.setLayoutManager(manager);
352 | }
353 |
354 | public RecyclerView.LayoutManager getLayoutManager() {
355 | return mRecyclerView.getLayoutManager();
356 | }
357 |
358 | public void refreshComplete() {
359 | mPtrFrame.refreshComplete();
360 | }
361 |
362 | public void loadComplete() {
363 | mLoadingMore = false;
364 | }
365 |
366 | public void setOnRefreshListener(OnRefreshListener listener) {
367 | this.mRefreshListener = listener;
368 | }
369 |
370 | public void setOnLoadListener(OnLoadListener listener) {
371 | this.mOnLoadListener = listener;
372 | }
373 |
374 | public void setOnEmptyViewClick(OnClickListener listener) {
375 | if (mEmptyView != null) {
376 | mEmptyView.setOnClickListener(listener);
377 | }
378 | }
379 |
380 |
381 | public void move(int position) {
382 | move(position, true);
383 | }
384 |
385 | public void move(int position, boolean isSmooth) {
386 | if (position < 0 || position >= mAdapter.getItemCount()) {
387 | Log.e(TAG, "move positon error");
388 | return;
389 | }
390 | mMovePoi = position;
391 |
392 | if (isSmooth) {
393 | smoothScrollPosition(position);
394 | } else {
395 | scrollPosition(position);
396 | }
397 | }
398 |
399 |
400 | private void scrollPosition(int position) {
401 | int firstVisibleItemPosition = LayoutManagerUtil.getFirstVisibleItemPosition(getLayoutManager());
402 | int lastVisibleItemPosition = LayoutManagerUtil.getLastVisibleItemPosition(getLayoutManager());
403 |
404 | if (position < firstVisibleItemPosition) {
405 | mRecyclerView.scrollToPosition(position);
406 | } else if (position < lastVisibleItemPosition) {
407 | int top = mRecyclerView.getChildAt(position - firstVisibleItemPosition).getTop();
408 | mRecyclerView.scrollBy(0, top);
409 | } else {
410 | mReviseMovePoi = true;
411 | //Todo scrollToPosition不触发RecyclerView.OnScrollListener监听所以没校正位置 改成smoothScrollToPosition()方法即可 这个问题后期google应该会修复所以这里未改成smoothScrollToPosition()
412 | mRecyclerView.scrollToPosition(position);
413 | }
414 | }
415 |
416 | private void smoothScrollPosition(int position) {
417 | int firstVisibleItemPosition = LayoutManagerUtil.getFirstVisibleItemPosition(getLayoutManager());
418 | int lastVisibleItemPosition = LayoutManagerUtil.getLastVisibleItemPosition(getLayoutManager());
419 |
420 | if (position < firstVisibleItemPosition) {
421 | mRecyclerView.smoothScrollToPosition(position);
422 | } else if (position < lastVisibleItemPosition) {
423 | int top = mRecyclerView.getChildAt(position - firstVisibleItemPosition).getTop();
424 | mRecyclerView.smoothScrollBy(0, top);
425 | } else {
426 | mReviseMovePoi = true;
427 | mRecyclerView.smoothScrollToPosition(position);
428 | }
429 | }
430 |
431 | class ReviseMoveListener extends RecyclerView.OnScrollListener {
432 | @Override
433 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
434 | if (mReviseMovePoi && newState == RecyclerView.SCROLL_STATE_IDLE) {
435 | mReviseMovePoi = false;
436 | int n = mMovePoi - LayoutManagerUtil.getFirstVisibleItemPosition(getLayoutManager());
437 | if (n >= 0 && n < mRecyclerView.getChildCount()) {
438 | int top = mRecyclerView.getChildAt(n).getTop();
439 | mRecyclerView.scrollBy(0, top);
440 | }
441 | }
442 | }
443 |
444 | @Override
445 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
446 | }
447 | }
448 |
449 | }
450 |
--------------------------------------------------------------------------------
/ErvLibrary/src/main/java/com/zly/www/easyrecyclerview/ptrlib/header/MaterialProgressDrawable.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 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 | package com.zly.www.easyrecyclerview.ptrlib.header;
17 |
18 | import android.content.Context;
19 | import android.content.res.Resources;
20 | import android.graphics.Canvas;
21 | import android.graphics.Color;
22 | import android.graphics.ColorFilter;
23 | import android.graphics.Paint;
24 | import android.graphics.Paint.Style;
25 | import android.graphics.Path;
26 | import android.graphics.PixelFormat;
27 | import android.graphics.RadialGradient;
28 | import android.graphics.Rect;
29 | import android.graphics.RectF;
30 | import android.graphics.Shader;
31 | import android.graphics.drawable.Animatable;
32 | import android.graphics.drawable.Drawable;
33 | import android.graphics.drawable.ShapeDrawable;
34 | import android.graphics.drawable.shapes.OvalShape;
35 | import android.os.Build;
36 | import android.util.DisplayMetrics;
37 | import android.view.View;
38 | import android.view.animation.AccelerateDecelerateInterpolator;
39 | import android.view.animation.Animation;
40 | import android.view.animation.Interpolator;
41 | import android.view.animation.LinearInterpolator;
42 | import android.view.animation.Transformation;
43 |
44 | import com.zly.www.easyrecyclerview.ptrlib.util.PtrLocalDisplay;
45 |
46 | import java.util.ArrayList;
47 |
48 |
49 | /**
50 | * Fancy progress indicator for Material theme.
51 | * It's taken from {@link android.support.v4.widget}.
52 | * I've done some slight changes.
53 | *
54 | *
55 | */
56 | public class MaterialProgressDrawable extends Drawable implements Animatable {
57 |
58 | // Maps to ProgressBar.Large style
59 | public static final int LARGE = 0;
60 | // Maps to ProgressBar default style
61 | public static final int DEFAULT = 1;
62 | private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
63 | private static final Interpolator END_CURVE_INTERPOLATOR = new EndCurveInterpolator();
64 | private static final Interpolator START_CURVE_INTERPOLATOR = new StartCurveInterpolator();
65 | private static final Interpolator EASE_INTERPOLATOR = new AccelerateDecelerateInterpolator();
66 | // Maps to ProgressBar default style
67 | private static final int CIRCLE_DIAMETER = 40;
68 | private static final float CENTER_RADIUS = 8.75f; //should add up to 10 when + stroke_width
69 | private static final float STROKE_WIDTH = 2.5f;
70 | // Maps to ProgressBar.Large style
71 | private static final int CIRCLE_DIAMETER_LARGE = 56;
72 | private static final float CENTER_RADIUS_LARGE = 12.5f;
73 | private static final float STROKE_WIDTH_LARGE = 3f;
74 | /**
75 | * The duration of a single progress spin in milliseconds.
76 | */
77 | private static final int ANIMATION_DURATION = 1000 * 80 / 60;
78 | /**
79 | * The number of points in the progress "star".
80 | */
81 | private static final float NUM_POINTS = 5f;
82 | /**
83 | * Layout info for the arrowhead in dp
84 | */
85 | private static final int ARROW_WIDTH = 10;
86 | private static final int ARROW_HEIGHT = 5;
87 | private static final float ARROW_OFFSET_ANGLE = 5;
88 | /**
89 | * Layout info for the arrowhead for the large spinner in dp
90 | */
91 | private static final int ARROW_WIDTH_LARGE = 12;
92 | private static final int ARROW_HEIGHT_LARGE = 6;
93 | private static final float MAX_PROGRESS_ARC = .8f;
94 | private static final int KEY_SHADOW_COLOR = 0x1E000000;
95 | private static final int FILL_SHADOW_COLOR = 0x3D000000;
96 | private static final float SHADOW_RADIUS = 3.5f;
97 | private static final float X_OFFSET = 0f;
98 | private static final float Y_OFFSET = 1.75f;
99 | private final int[] COLORS = new int[]{
100 | 0xFFC93437,
101 | 0xFF375BF1,
102 | 0xFFF7D23E,
103 | 0xFF34A350
104 | };
105 | /**
106 | * The list of animators operating on this drawable.
107 | */
108 | private final ArrayList mAnimators = new ArrayList();
109 | /**
110 | * The indicator ring, used to manage animation state.
111 | */
112 | private final Ring mRing;
113 | private final Callback mCallback = new Callback() {
114 | @Override
115 | public void invalidateDrawable(Drawable d) {
116 | invalidateSelf();
117 | }
118 |
119 | @Override
120 | public void scheduleDrawable(Drawable d, Runnable what, long when) {
121 | scheduleSelf(what, when);
122 | }
123 |
124 | @Override
125 | public void unscheduleDrawable(Drawable d, Runnable what) {
126 | unscheduleSelf(what);
127 | }
128 | };
129 | /**
130 | * Canvas rotation in degrees.
131 | */
132 | private float mRotation;
133 | private Resources mResources;
134 | private View mParent;
135 | private Animation mAnimation;
136 | private float mRotationCount;
137 | private double mWidth;
138 | private double mHeight;
139 | private Animation mFinishAnimation;
140 | private int mBackgroundColor;
141 | private ShapeDrawable mShadow;
142 |
143 | public MaterialProgressDrawable(Context context, View parent) {
144 | mParent = parent;
145 | mResources = context.getResources();
146 | mRing = new Ring(mCallback);
147 | mRing.setColors(COLORS);
148 | updateSizes(DEFAULT);
149 | setupAnimators();
150 | }
151 |
152 | private void setSizeParameters(double progressCircleWidth, double progressCircleHeight,
153 | double centerRadius, double strokeWidth, float arrowWidth, float arrowHeight) {
154 | final Ring ring = mRing;
155 | final DisplayMetrics metrics = mResources.getDisplayMetrics();
156 | final float screenDensity = metrics.density;
157 | mWidth = progressCircleWidth * screenDensity;
158 | mHeight = progressCircleHeight * screenDensity;
159 | ring.setStrokeWidth((float) strokeWidth * screenDensity);
160 | ring.setCenterRadius(centerRadius * screenDensity);
161 | ring.setColorIndex(0);
162 | ring.setArrowDimensions(arrowWidth * screenDensity, arrowHeight * screenDensity);
163 | ring.setInsets((int) mWidth, (int) mHeight);
164 | setUp(mWidth);
165 | }
166 |
167 | private void setUp(final double diameter) {
168 | PtrLocalDisplay.init(mParent.getContext());
169 | final int shadowYOffset = PtrLocalDisplay.dp2px(Y_OFFSET);
170 | final int shadowXOffset = PtrLocalDisplay.dp2px(X_OFFSET);
171 | int mShadowRadius = PtrLocalDisplay.dp2px(SHADOW_RADIUS);
172 | OvalShape oval = new OvalShadow(mShadowRadius, (int) diameter);
173 | mShadow = new ShapeDrawable(oval);
174 | if (Build.VERSION.SDK_INT >= 11) {
175 | mParent.setLayerType(View.LAYER_TYPE_SOFTWARE, mShadow.getPaint());
176 | }
177 | mShadow.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset, KEY_SHADOW_COLOR);
178 | }
179 |
180 | /**
181 | * Set the overall size for the progress spinner. This updates the radius
182 | * and stroke width of the ring.
183 | *
184 | */
185 | public void updateSizes(int size) {
186 | if (size == LARGE) {
187 | setSizeParameters(CIRCLE_DIAMETER_LARGE, CIRCLE_DIAMETER_LARGE, CENTER_RADIUS_LARGE,
188 | STROKE_WIDTH_LARGE, ARROW_WIDTH_LARGE, ARROW_HEIGHT_LARGE);
189 | } else {
190 | setSizeParameters(CIRCLE_DIAMETER, CIRCLE_DIAMETER, CENTER_RADIUS, STROKE_WIDTH,
191 | ARROW_WIDTH, ARROW_HEIGHT);
192 | }
193 | }
194 |
195 | /**
196 | * @param show Set to true to display the arrowhead on the progress spinner.
197 | */
198 | public void showArrow(boolean show) {
199 | mRing.setShowArrow(show);
200 | }
201 |
202 | /**
203 | * @param scale Set the scale of the arrowhead for the spinner.
204 | */
205 | public void setArrowScale(float scale) {
206 | mRing.setArrowScale(scale);
207 | }
208 |
209 | /**
210 | * Set the start and end trim for the progress spinner arc.
211 | *
212 | * @param startAngle start angle
213 | * @param endAngle end angle
214 | */
215 | public void setStartEndTrim(float startAngle, float endAngle) {
216 | mRing.setStartTrim(startAngle);
217 | mRing.setEndTrim(endAngle);
218 | }
219 |
220 | /**
221 | * Set the amount of rotation to apply to the progress spinner.
222 | *
223 | * @param rotation Rotation is from [0..1]
224 | */
225 | public void setProgressRotation(float rotation) {
226 | mRing.setRotation(rotation);
227 | }
228 |
229 | /**
230 | * Update the background color of the circle image view.
231 | */
232 | public void setBackgroundColor(int color) {
233 | mBackgroundColor = color;
234 | mRing.setBackgroundColor(color);
235 | }
236 |
237 | /**
238 | * Set the colors used in the progress animation from color resources.
239 | * The first color will also be the color of the bar that grows in response
240 | * to a user swipe gesture.
241 | *
242 | * @param colors
243 | */
244 | public void setColorSchemeColors(int... colors) {
245 | mRing.setColors(colors);
246 | mRing.setColorIndex(0);
247 | }
248 |
249 | @Override
250 | public int getIntrinsicHeight() {
251 | return (int) mHeight;
252 | }
253 |
254 | @Override
255 | public int getIntrinsicWidth() {
256 | return (int) mWidth;
257 | }
258 |
259 | @Override
260 | public void draw(Canvas c) {
261 | if (mShadow != null) {
262 | mShadow.getPaint().setColor(mBackgroundColor);
263 | mShadow.draw(c);
264 | }
265 |
266 | final Rect bounds = getBounds();
267 | final int saveCount = c.save();
268 | c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());
269 | mRing.draw(c, bounds);
270 | c.restoreToCount(saveCount);
271 | }
272 |
273 | public int getAlpha() {
274 | return mRing.getAlpha();
275 | }
276 |
277 | @Override
278 | public void setAlpha(int alpha) {
279 | mRing.setAlpha(alpha);
280 | }
281 |
282 | @Override
283 | public void setColorFilter(ColorFilter colorFilter) {
284 | mRing.setColorFilter(colorFilter);
285 | }
286 |
287 | @SuppressWarnings("unused")
288 | private float getRotation() {
289 | return mRotation;
290 | }
291 |
292 | @SuppressWarnings("unused")
293 | void setRotation(float rotation) {
294 | mRotation = rotation;
295 | invalidateSelf();
296 | }
297 |
298 | @Override
299 | public int getOpacity() {
300 | return PixelFormat.TRANSLUCENT;
301 | }
302 |
303 | @Override
304 | public boolean isRunning() {
305 | final ArrayList animators = mAnimators;
306 | final int N = animators.size();
307 | for (int i = 0; i < N; i++) {
308 | final Animation animator = animators.get(i);
309 | if (animator.hasStarted() && !animator.hasEnded()) {
310 | return true;
311 | }
312 | }
313 | return false;
314 | }
315 |
316 | @Override
317 | public void start() {
318 | mAnimation.reset();
319 | mRing.storeOriginals();
320 | // Already showing some part of the ring
321 | if (mRing.getEndTrim() != mRing.getStartTrim()) {
322 | mParent.startAnimation(mFinishAnimation);
323 | } else {
324 | mRing.setColorIndex(0);
325 | mRing.resetOriginals();
326 | mParent.startAnimation(mAnimation);
327 | }
328 | }
329 |
330 | @Override
331 | public void stop() {
332 | mParent.clearAnimation();
333 | setRotation(0);
334 | mRing.setShowArrow(false);
335 | mRing.setColorIndex(0);
336 | mRing.resetOriginals();
337 | }
338 |
339 | private void setupAnimators() {
340 | final Ring ring = mRing;
341 | final Animation finishRingAnimation = new Animation() {
342 | public void applyTransformation(float interpolatedTime, Transformation t) {
343 | // shrink back down and complete a full rotation before starting other circles
344 | // Rotation goes between [0..1].
345 | float targetRotation = (float) (Math.floor(ring.getStartingRotation()
346 | / MAX_PROGRESS_ARC) + 1f);
347 | final float startTrim = ring.getStartingStartTrim()
348 | + (ring.getStartingEndTrim() - ring.getStartingStartTrim())
349 | * interpolatedTime;
350 | ring.setStartTrim(startTrim);
351 | final float rotation = ring.getStartingRotation()
352 | + ((targetRotation - ring.getStartingRotation()) * interpolatedTime);
353 | ring.setRotation(rotation);
354 | ring.setArrowScale(1 - interpolatedTime);
355 | }
356 | };
357 | finishRingAnimation.setInterpolator(EASE_INTERPOLATOR);
358 | finishRingAnimation.setDuration(ANIMATION_DURATION / 2);
359 | finishRingAnimation.setAnimationListener(new Animation.AnimationListener() {
360 | @Override
361 | public void onAnimationStart(Animation animation) {
362 | }
363 |
364 | @Override
365 | public void onAnimationEnd(Animation animation) {
366 | ring.goToNextColor();
367 | ring.storeOriginals();
368 | ring.setShowArrow(false);
369 | mParent.startAnimation(mAnimation);
370 | }
371 |
372 | @Override
373 | public void onAnimationRepeat(Animation animation) {
374 | }
375 | });
376 | final Animation animation = new Animation() {
377 | @Override
378 | public void applyTransformation(float interpolatedTime, Transformation t) {
379 | // The minProgressArc is calculated from 0 to create an angle that
380 | // matches the stroke width.
381 | final float minProgressArc = (float) Math.toRadians(ring.getStrokeWidth()
382 | / (2 * Math.PI * ring.getCenterRadius()));
383 | final float startingEndTrim = ring.getStartingEndTrim();
384 | final float startingTrim = ring.getStartingStartTrim();
385 | final float startingRotation = ring.getStartingRotation();
386 | // Offset the minProgressArc to where the endTrim is located.
387 | final float minArc = MAX_PROGRESS_ARC - minProgressArc;
388 | final float endTrim = startingEndTrim
389 | + (minArc * START_CURVE_INTERPOLATOR.getInterpolation(interpolatedTime));
390 | ring.setEndTrim(endTrim);
391 | final float startTrim = startingTrim
392 | + (MAX_PROGRESS_ARC * END_CURVE_INTERPOLATOR
393 | .getInterpolation(interpolatedTime));
394 | ring.setStartTrim(startTrim);
395 | final float rotation = startingRotation + (0.25f * interpolatedTime);
396 | ring.setRotation(rotation);
397 | float groupRotation = ((720.0f / NUM_POINTS) * interpolatedTime)
398 | + (720.0f * (mRotationCount / NUM_POINTS));
399 | setRotation(groupRotation);
400 | }
401 | };
402 | animation.setRepeatCount(Animation.INFINITE);
403 | animation.setRepeatMode(Animation.RESTART);
404 | animation.setInterpolator(LINEAR_INTERPOLATOR);
405 | animation.setDuration(ANIMATION_DURATION);
406 | animation.setAnimationListener(new Animation.AnimationListener() {
407 | @Override
408 | public void onAnimationStart(Animation animation) {
409 | mRotationCount = 0;
410 | }
411 |
412 | @Override
413 | public void onAnimationEnd(Animation animation) {
414 | // do nothing
415 | }
416 |
417 | @Override
418 | public void onAnimationRepeat(Animation animation) {
419 | ring.storeOriginals();
420 | ring.goToNextColor();
421 | ring.setStartTrim(ring.getEndTrim());
422 | mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
423 | }
424 | });
425 | mFinishAnimation = finishRingAnimation;
426 | mAnimation = animation;
427 | }
428 |
429 | private static class Ring {
430 | private final RectF mTempBounds = new RectF();
431 | private final Paint mArcPaint = new Paint();
432 | private final Paint mArrowPaint = new Paint();
433 | private final Callback mRingCallback;
434 | private final Paint mCirclePaint = new Paint();
435 | private float mStartTrim = 0.0f;
436 | private float mEndTrim = 0.0f;
437 | private float mRotation = 0.0f;
438 | private float mStrokeWidth = 5.0f;
439 | private float mStrokeInset = 2.5f;
440 | private int[] mColors;
441 | // mColorIndex represents the offset into the available mColors that the
442 | // progress circle should currently display. As the progress circle is
443 | // animating, the mColorIndex moves by one to the next available color.
444 | private int mColorIndex;
445 | private float mStartingStartTrim;
446 | private float mStartingEndTrim;
447 | private float mStartingRotation;
448 | private boolean mShowArrow;
449 | private Path mArrow;
450 | private float mArrowScale;
451 | private double mRingCenterRadius;
452 | private int mArrowWidth;
453 | private int mArrowHeight;
454 | private int mAlpha;
455 | private int mBackgroundColor;
456 |
457 | public Ring(Callback callback) {
458 | mRingCallback = callback;
459 | mArcPaint.setStrokeCap(Paint.Cap.SQUARE);
460 | mArcPaint.setAntiAlias(true);
461 | mArcPaint.setStyle(Style.STROKE);
462 | mArrowPaint.setStyle(Paint.Style.FILL);
463 | mArrowPaint.setAntiAlias(true);
464 |
465 | mCirclePaint.setAntiAlias(true);
466 | }
467 |
468 | public void setBackgroundColor(int color) {
469 | mBackgroundColor = color;
470 | }
471 |
472 | /**
473 | * Set the dimensions of the arrowhead.
474 | *
475 | * @param width Width of the hypotenuse of the arrow head
476 | * @param height Height of the arrow point
477 | */
478 | public void setArrowDimensions(float width, float height) {
479 | mArrowWidth = (int) width;
480 | mArrowHeight = (int) height;
481 | }
482 |
483 | /**
484 | * Draw the progress spinner
485 | */
486 | public void draw(Canvas c, Rect bounds) {
487 |
488 | mCirclePaint.setColor(mBackgroundColor);
489 | mCirclePaint.setAlpha(mAlpha);
490 |
491 | c.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), bounds.width() / 2, mCirclePaint);
492 |
493 | final RectF arcBounds = mTempBounds;
494 | arcBounds.set(bounds);
495 | arcBounds.inset(mStrokeInset, mStrokeInset);
496 | final float startAngle = (mStartTrim + mRotation) * 360;
497 | final float endAngle = (mEndTrim + mRotation) * 360;
498 | float sweepAngle = endAngle - startAngle;
499 | mArcPaint.setColor(mColors[mColorIndex]);
500 | mArcPaint.setAlpha(mAlpha);
501 | c.drawArc(arcBounds, startAngle, sweepAngle, false, mArcPaint);
502 | drawTriangle(c, startAngle, sweepAngle, bounds);
503 | }
504 |
505 | private void drawTriangle(Canvas c, float startAngle, float sweepAngle, Rect bounds) {
506 | if (mShowArrow) {
507 | if (mArrow == null) {
508 | mArrow = new android.graphics.Path();
509 | mArrow.setFillType(android.graphics.Path.FillType.EVEN_ODD);
510 | } else {
511 | mArrow.reset();
512 | }
513 | // Adjust the position of the triangle so that it is inset as
514 | // much as the arc, but also centered on the arc.
515 | float inset = (int) mStrokeInset / 2 * mArrowScale;
516 | float x = (float) (mRingCenterRadius * Math.cos(0) + bounds.exactCenterX());
517 | float y = (float) (mRingCenterRadius * Math.sin(0) + bounds.exactCenterY());
518 | // Update the path each time. This works around an issue in SKIA
519 | // where concatenating a rotation matrix to a scale matrix
520 | // ignored a starting negative rotation. This appears to have
521 | // been fixed as of API 21.
522 | mArrow.moveTo(0, 0);
523 | mArrow.lineTo(mArrowWidth * mArrowScale, 0);
524 | mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight
525 | * mArrowScale));
526 | mArrow.offset(x - inset, y);
527 | mArrow.close();
528 | // draw a triangle
529 | mArrowPaint.setColor(mColors[mColorIndex]);
530 | mArrowPaint.setAlpha(mAlpha);
531 | c.rotate(startAngle + sweepAngle - ARROW_OFFSET_ANGLE, bounds.exactCenterX(),
532 | bounds.exactCenterY());
533 | c.drawPath(mArrow, mArrowPaint);
534 | }
535 | }
536 |
537 | /**
538 | * Set the colors the progress spinner alternates between.
539 | *
540 | * @param colors Array of integers describing the colors. Must be non-null.
541 | */
542 | public void setColors(int[] colors) {
543 | mColors = colors;
544 | // if colors are reset, make sure to reset the color index as well
545 | setColorIndex(0);
546 | }
547 |
548 | /**
549 | * @param index Index into the color array of the color to display in
550 | * the progress spinner.
551 | */
552 | public void setColorIndex(int index) {
553 | mColorIndex = index;
554 | }
555 |
556 | /**
557 | * Proceed to the next available ring color. This will automatically
558 | * wrap back to the beginning of colors.
559 | */
560 | public void goToNextColor() {
561 | mColorIndex = (mColorIndex + 1) % (mColors.length);
562 | }
563 |
564 | public void setColorFilter(ColorFilter filter) {
565 | mArcPaint.setColorFilter(filter);
566 | invalidateSelf();
567 | }
568 |
569 | /**
570 | * @return Current alpha of the progress spinner and arrowhead.
571 | */
572 | public int getAlpha() {
573 | return mAlpha;
574 | }
575 |
576 | /**
577 | * @param alpha Set the alpha of the progress spinner and associated arrowhead.
578 | */
579 | public void setAlpha(int alpha) {
580 | mAlpha = alpha;
581 | }
582 |
583 | @SuppressWarnings("unused")
584 | public float getStrokeWidth() {
585 | return mStrokeWidth;
586 | }
587 |
588 | /**
589 | * @param strokeWidth Set the stroke width of the progress spinner in pixels.
590 | */
591 | public void setStrokeWidth(float strokeWidth) {
592 | mStrokeWidth = strokeWidth;
593 | mArcPaint.setStrokeWidth(strokeWidth);
594 | invalidateSelf();
595 | }
596 |
597 | @SuppressWarnings("unused")
598 | public float getStartTrim() {
599 | return mStartTrim;
600 | }
601 |
602 | @SuppressWarnings("unused")
603 | public void setStartTrim(float startTrim) {
604 | mStartTrim = startTrim;
605 | invalidateSelf();
606 | }
607 |
608 | public float getStartingStartTrim() {
609 | return mStartingStartTrim;
610 | }
611 |
612 | public float getStartingEndTrim() {
613 | return mStartingEndTrim;
614 | }
615 |
616 | @SuppressWarnings("unused")
617 | public float getEndTrim() {
618 | return mEndTrim;
619 | }
620 |
621 | @SuppressWarnings("unused")
622 | public void setEndTrim(float endTrim) {
623 | mEndTrim = endTrim;
624 | invalidateSelf();
625 | }
626 |
627 | @SuppressWarnings("unused")
628 | public float getRotation() {
629 | return mRotation;
630 | }
631 |
632 | @SuppressWarnings("unused")
633 | public void setRotation(float rotation) {
634 | mRotation = rotation;
635 | invalidateSelf();
636 | }
637 |
638 | public void setInsets(int width, int height) {
639 | final float minEdge = (float) Math.min(width, height);
640 | float insets;
641 | if (mRingCenterRadius <= 0 || minEdge < 0) {
642 | insets = (float) Math.ceil(mStrokeWidth / 2.0f);
643 | } else {
644 | insets = (float) (minEdge / 2.0f - mRingCenterRadius);
645 | }
646 | mStrokeInset = insets;
647 | }
648 |
649 | @SuppressWarnings("unused")
650 | public float getInsets() {
651 | return mStrokeInset;
652 | }
653 |
654 | public double getCenterRadius() {
655 | return mRingCenterRadius;
656 | }
657 |
658 | /**
659 | * @param centerRadius Inner radius in px of the circle the progress
660 | * spinner arc traces.
661 | */
662 | public void setCenterRadius(double centerRadius) {
663 | mRingCenterRadius = centerRadius;
664 | }
665 |
666 | /**
667 | * @param show Set to true to show the arrow head on the progress spinner.
668 | */
669 | public void setShowArrow(boolean show) {
670 | if (mShowArrow != show) {
671 | mShowArrow = show;
672 | invalidateSelf();
673 | }
674 | }
675 |
676 | /**
677 | * @param scale Set the scale of the arrowhead for the spinner.
678 | */
679 | public void setArrowScale(float scale) {
680 | if (scale != mArrowScale) {
681 | mArrowScale = scale;
682 | invalidateSelf();
683 | }
684 | }
685 |
686 | /**
687 | * @return The amount the progress spinner is currently rotated, between [0..1].
688 | */
689 | public float getStartingRotation() {
690 | return mStartingRotation;
691 | }
692 |
693 | /**
694 | * If the start / end trim are offset to begin with, store them so that
695 | * animation starts from that offset.
696 | */
697 | public void storeOriginals() {
698 | mStartingStartTrim = mStartTrim;
699 | mStartingEndTrim = mEndTrim;
700 | mStartingRotation = mRotation;
701 | }
702 |
703 | /**
704 | * Reset the progress spinner to default rotation, start and end angles.
705 | */
706 | public void resetOriginals() {
707 | mStartingStartTrim = 0;
708 | mStartingEndTrim = 0;
709 | mStartingRotation = 0;
710 | setStartTrim(0);
711 | setEndTrim(0);
712 | setRotation(0);
713 | }
714 |
715 | private void invalidateSelf() {
716 | mRingCallback.invalidateDrawable(null);
717 | }
718 | }
719 |
720 | /**
721 | * Squishes the interpolation curve into the second half of the animation.
722 | */
723 | private static class EndCurveInterpolator extends AccelerateDecelerateInterpolator {
724 | @Override
725 | public float getInterpolation(float input) {
726 | return super.getInterpolation(Math.max(0, (input - 0.5f) * 2.0f));
727 | }
728 | }
729 |
730 | /**
731 | * Squishes the interpolation curve into the first half of the animation.
732 | */
733 | private static class StartCurveInterpolator extends AccelerateDecelerateInterpolator {
734 | @Override
735 | public float getInterpolation(float input) {
736 | return super.getInterpolation(Math.min(1, input * 2.0f));
737 | }
738 | }
739 |
740 | /**
741 | * Taken from {@link package android.support.v4.widget}
742 | */
743 | private class OvalShadow extends OvalShape {
744 | private RadialGradient mRadialGradient;
745 | private int mShadowRadius;
746 | private Paint mShadowPaint;
747 | private int mCircleDiameter;
748 |
749 | public OvalShadow(int shadowRadius, int circleDiameter) {
750 | super();
751 | mShadowPaint = new Paint();
752 | mShadowRadius = shadowRadius;
753 | mCircleDiameter = circleDiameter;
754 | mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2,
755 | mShadowRadius, new int[]{
756 | FILL_SHADOW_COLOR, Color.TRANSPARENT
757 | }, null, Shader.TileMode.CLAMP);
758 | mShadowPaint.setShader(mRadialGradient);
759 | }
760 |
761 | @Override
762 | public void draw(Canvas canvas, Paint paint) {
763 | final int viewWidth = getBounds().width();
764 | final int viewHeight = getBounds().height();
765 | canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius),
766 | mShadowPaint);
767 | canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint);
768 | }
769 | }
770 | }
--------------------------------------------------------------------------------