├── README.md
└── YRecycleview
├── app
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── cn
│ │ └── yu
│ │ └── yuan
│ │ ├── AddHeadViewActivity.java
│ │ ├── DemoAdapter.java
│ │ ├── DemoSwipeAdapter.java
│ │ ├── LoadMoreAndRefreshActivity.java
│ │ ├── LoadingEmptyViewActivity.java
│ │ ├── MainActivity.java
│ │ ├── SwipeDate.java
│ │ └── SwipeLoadMoreAndRefreshActivity.java
│ └── res
│ ├── layout
│ ├── activity_empty_view.xml
│ ├── activity_loading_empty_view.xml
│ ├── activity_loadmore_refresh.xml
│ ├── activity_main.xml
│ ├── head_custom.xml
│ ├── item.xml
│ ├── item_content.xml
│ └── item_swipe_menu.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── img
└── picture.gif
├── mylibrary
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── yuan
│ │ └── kuo
│ │ └── yu
│ │ ├── load
│ │ ├── BaseProgressController.java
│ │ ├── CircleLoadingView.java
│ │ └── CircleProgress.java
│ │ └── view
│ │ ├── YRecyclerView.java
│ │ ├── YRecycleviewRefreshFootView.java
│ │ └── YRecycleviewRefreshHeadView.java
│ └── res
│ ├── drawable-xhdpi
│ └── arrow_down.png
│ ├── layout
│ ├── emptyview.xml
│ ├── foot_recycleview_loadmore.xml
│ └── head_recycleview_refresh.xml
│ └── values
│ ├── attrs.xml
│ ├── colors.xml
│ └── strings.xml
└── settings.gradle
/README.md:
--------------------------------------------------------------------------------
1 | ## 这是一个下拉刷新和下拉加载的Recycleview的控件,实现的想法来自于XListview
2 | 使用方法很简单
3 |
4 | ## 看下效果图展示
5 | 
6 |
7 | ### maven引用方式
8 | ```
9 |
10 | cn.yuan.yu.recycleview
11 | mylibrary
12 | 1.0.4
13 | pom
14 |
15 | ```
16 | ### gradle引用方式
17 | ```
18 | compile 'cn.yuan.yu.recycleview:mylibrary:1.0.4'
19 | ```
20 | ```javascript
21 | 布局文件添加
22 |
26 |
27 |
28 |
29 | ```
30 | ### 至于更改上啦刷新和下拉加载的动画,我发现了一个更加简单的方式去实现,就是通过帧动画的形式,直接设置,方便diy设置
31 | ### 其他的适配器以及正常编写都是和官方的一样,没有任何改变上手比较容易简单
32 | # 有问题请反馈
33 | * 邮件(yukuoyuan@hotmail.com)
34 | * QQ: 152046273
35 | * 博客: [CSDN博客](http://blog.csdn.net/easkshark)
36 |
37 | ### 更新日志
38 | - 2018/6/23:修复了上拉加载更多的时候出现空白间隙问题的修改
39 | - 2018/2/10:增加添加头布局功能
40 | - 2018/2/5 :重构了空数据下拉刷新的方法的修改,移除了无用的方法
41 | - 2017/5/27:提交了设置空布局,以及空布局可以下拉加载的方法以及Demo
42 | - 2017/5/16:有同学提出,刷新单个条目的时候,有问题,这个问题是因为处理headview导致的,感谢[PascalGUO](https://github.com/pascalGuo)提的bug.
43 | - 2017/4/19:修复了因为没有上啦刷新,导致删除数据索引越界的问题,感谢[XiaoDada](https://github.com/XiaoDada)提的bug.
44 | - 2016/3/30:提交了第一个版本的YRecycleview.
45 |
--------------------------------------------------------------------------------
/YRecycleview/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion '25.0.0'
6 |
7 | defaultConfig {
8 | applicationId "cn.yu.yuan"
9 | minSdkVersion 15
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(include: ['*.jar'], dir: 'libs')
24 | compile 'com.android.support:appcompat-v7:23.3.0'
25 | compile 'com.daimajia.swipelayout:library:1.2.0@aar'
26 | compile project(':mylibrary')
27 | compile 'cn.yuan.yu.recycleview:mylibrary:1.0.4'
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
23 |
26 |
27 |
30 |
31 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/java/cn/yu/yuan/AddHeadViewActivity.java:
--------------------------------------------------------------------------------
1 | package cn.yu.yuan;
2 |
3 | import android.os.Bundle;
4 | import android.os.Handler;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.view.View;
9 |
10 | import java.util.ArrayList;
11 |
12 | import yuan.kuo.yu.view.YRecyclerView;
13 |
14 | /**
15 | * Created by yukuoyuan on 2017/5/26.
16 | * 这是一个列表添加头布局
17 | */
18 | public class AddHeadViewActivity extends AppCompatActivity implements YRecyclerView.OnRefreshAndLoadMoreListener {
19 |
20 | private YRecyclerView rcv_empty_view;
21 | private ArrayList arrayList = new ArrayList<>();
22 | private DemoAdapter demoAdapter;
23 |
24 | @Override
25 | protected void onCreate(@Nullable Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_empty_view);
28 | rcv_empty_view = (YRecyclerView) findViewById(R.id.rcv_empty_view);
29 | rcv_empty_view.setLayoutManager(new LinearLayoutManager(this));
30 |
31 | View headView = View.inflate(this, R.layout.head_custom, null);
32 | rcv_empty_view.addHeadView(headView);
33 | demoAdapter = new DemoAdapter(arrayList);
34 | rcv_empty_view.setAdapter(demoAdapter);
35 | rcv_empty_view.setRefreshAndLoadMoreListener(this);
36 | rcv_empty_view.showLoadingEmptyView("没数据,等着吧");
37 | }
38 |
39 | @Override
40 | public void onRefresh() {
41 | new Handler().postDelayed(new Runnable() {
42 | public void run() {
43 | rcv_empty_view.setLoadingEmptyViewGone();
44 | demoAdapter.addReFreshData();
45 | rcv_empty_view.setReFreshComplete();
46 | }
47 | }, 2500);
48 | }
49 |
50 | @Override
51 | public void onLoadMore() {
52 | new Handler().postDelayed(new Runnable() {
53 | public void run() {
54 | demoAdapter.addRLoadMOreData();
55 | rcv_empty_view.setReFreshComplete();
56 | }
57 | }, 2500);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/java/cn/yu/yuan/DemoAdapter.java:
--------------------------------------------------------------------------------
1 | package cn.yu.yuan;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.TextView;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * Created by yukuo on 2016/4/30.
13 | */
14 | public class DemoAdapter extends RecyclerView.Adapter {
15 | private List list = new ArrayList<>();
16 |
17 | public DemoAdapter(List list) {
18 | this.list = list;
19 | }
20 |
21 | public void addReFreshData() {
22 | list.add(0,"添加刷新数据");
23 | notifyDataSetChanged();
24 | }
25 |
26 | public void addRLoadMOreData() {
27 | list.add("添加加载更多数据");
28 | notifyDataSetChanged();
29 | }
30 |
31 | @Override
32 | public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
33 | View view = View.inflate(parent.getContext(), R.layout.item, null);
34 | return new MyHolder(view);
35 | }
36 |
37 | @Override
38 | public void onBindViewHolder(MyHolder holder, int position) {
39 | holder.tv_item.setText(list.get(position));
40 | }
41 |
42 | @Override
43 | public int getItemCount() {
44 | return list.size();
45 | }
46 |
47 | class MyHolder extends RecyclerView.ViewHolder {
48 |
49 | private final TextView tv_item;
50 |
51 | public MyHolder(View itemView) {
52 | super(itemView);
53 | tv_item = (TextView) itemView.findViewById(R.id.tv_item);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/java/cn/yu/yuan/DemoSwipeAdapter.java:
--------------------------------------------------------------------------------
1 | package cn.yu.yuan;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.TextView;
7 | import android.widget.Toast;
8 |
9 | import com.daimajia.swipe.SwipeLayout;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | /**
15 | * Created by yukuo on 2016/4/30.
16 | * 这是一个测试带有侧滑删除的列表适配器
17 | */
18 | public class DemoSwipeAdapter extends RecyclerView.Adapter {
19 | private List list = new ArrayList<>();
20 |
21 | public DemoSwipeAdapter(List list) {
22 | this.list = list;
23 | }
24 |
25 | public void addReFreshData() {
26 | SwipeDate swipeDate = new SwipeDate();
27 | swipeDate.name = "添加刷新数据";
28 | swipeDate.type = 0;
29 | list.add(0, swipeDate);
30 | notifyDataSetChanged();
31 | }
32 |
33 | public void addRLoadMOreData() {
34 | SwipeDate swipeDate = new SwipeDate();
35 | swipeDate.name = "添加加载更多数据";
36 | swipeDate.type = 0;
37 | list.add(list.size(), swipeDate);
38 | notifyDataSetChanged();
39 | }
40 |
41 | /**
42 | * 删除一个数据的方法
43 | *
44 | * @param position 索引
45 | */
46 | public void removeData(int position) {
47 | // TODO 使用刷新单一个条目会出现问题,所以请不要使用
48 | list.remove(position);
49 | notifyDataSetChanged();
50 | }
51 |
52 | @Override
53 | public int getItemViewType(int position) {
54 | return list.get(position).type;
55 | }
56 |
57 | @Override
58 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
59 | View view;
60 | if (viewType == 1) {
61 | view = View.inflate(parent.getContext(), R.layout.item_swipe_menu, null);
62 | return new MySwipeMenuHolder(view);
63 | } else {
64 | view = View.inflate(parent.getContext(), R.layout.item_content, null);
65 | return new MyHolder(view);
66 | }
67 | }
68 |
69 | @Override
70 | public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
71 | if (holder instanceof MyHolder) {
72 | MyHolder myHolder = (MyHolder) holder;
73 | myHolder.item_content.setText(list.get(position).name);
74 | } else if (holder instanceof MySwipeMenuHolder) {
75 | final MySwipeMenuHolder myHolders = (MySwipeMenuHolder) holder;
76 | myHolders.item_content.setText(list.get(position).name + "######" + position);
77 | myHolders.item_content.setOnClickListener(new View.OnClickListener() {
78 | @Override
79 | public void onClick(View v) {
80 | Toast.makeText(v.getContext(), list.get(position).name, Toast.LENGTH_SHORT).show();
81 | }
82 | });
83 | myHolders.delete.setOnClickListener(new View.OnClickListener() {
84 | @Override
85 | public void onClick(View v) {
86 | removeData(position);
87 | }
88 | });
89 | }
90 | }
91 |
92 | @Override
93 | public int getItemCount() {
94 | return list.size();
95 | }
96 |
97 | class MyHolder extends RecyclerView.ViewHolder {
98 |
99 | private final TextView item_content;
100 |
101 | public MyHolder(View itemView) {
102 | super(itemView);
103 | item_content = (TextView) itemView.findViewById(R.id.item_content);
104 | }
105 | }
106 |
107 | class MySwipeMenuHolder extends RecyclerView.ViewHolder {
108 | private final TextView delete;
109 | private final TextView ok;
110 | private final TextView item_content;
111 | private final SwipeLayout srl_item;
112 |
113 | public MySwipeMenuHolder(View itemView) {
114 | super(itemView);
115 | item_content = (TextView) itemView.findViewById(R.id.item_content);
116 | srl_item = (SwipeLayout) itemView.findViewById(R.id.srl_item);
117 | delete = (TextView) itemView.findViewById(R.id.delete);
118 | ok = (TextView) itemView.findViewById(R.id.ok);
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/java/cn/yu/yuan/LoadMoreAndRefreshActivity.java:
--------------------------------------------------------------------------------
1 | package cn.yu.yuan;
2 |
3 | import android.os.Bundle;
4 | import android.os.Handler;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.util.Log;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | import yuan.kuo.yu.view.YRecyclerView;
14 |
15 | /**
16 | * Created by yukuoyuan on 2017/3/25.
17 | * 这是一个测试上啦刷新下拉加载更多的界面
18 | */
19 | public class LoadMoreAndRefreshActivity extends AppCompatActivity {
20 | private List list = new ArrayList<>();
21 | private YRecyclerView ycl;
22 |
23 |
24 | @Override
25 | protected void onCreate(@Nullable Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_loadmore_refresh);
28 | ycl = (YRecyclerView) findViewById(R.id.ycl);
29 | initData();
30 | }
31 |
32 | private void initData() {
33 | for (int i = 0; i < 20; i++) {
34 | list.add("于" + i);
35 | }
36 | final DemoAdapter demoAdapter = new DemoAdapter(list);
37 | ycl.setLayoutManager(new LinearLayoutManager(this));
38 | ycl.setAdapter(demoAdapter);
39 | ycl.setRefreshAndLoadMoreListener(new YRecyclerView.OnRefreshAndLoadMoreListener() {
40 | @Override
41 | public void onRefresh() {
42 | new Handler().postDelayed(new Runnable() {
43 | public void run() {
44 | demoAdapter.addReFreshData();
45 | ycl.setReFreshComplete();
46 | }
47 | }, 2500);
48 | }
49 |
50 | @Override
51 | public void onLoadMore() {
52 | Log.i("加载更多", "000");
53 | new Handler().postDelayed(new Runnable() {
54 | public void run() {
55 | demoAdapter.addRLoadMOreData();
56 | ycl.setloadMoreComplete();
57 | }
58 | }, 2500);
59 | }
60 | });
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/java/cn/yu/yuan/LoadingEmptyViewActivity.java:
--------------------------------------------------------------------------------
1 | package cn.yu.yuan;
2 |
3 | import android.os.Bundle;
4 | import android.os.Handler;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.LinearLayoutManager;
8 |
9 | import java.util.ArrayList;
10 |
11 | import yuan.kuo.yu.view.YRecyclerView;
12 |
13 | /**
14 | * Created by yukuoyuan on 2017/5/26.
15 | * 这是一个可下拉刷新加载更多的空布局界面
16 | */
17 | public class LoadingEmptyViewActivity extends AppCompatActivity implements YRecyclerView.OnRefreshAndLoadMoreListener {
18 |
19 | private YRecyclerView rcv_loading_empty_view;
20 | private ArrayList arrayList = new ArrayList<>();
21 | private DemoAdapter demoAdapter;
22 |
23 | @Override
24 | protected void onCreate(@Nullable Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.activity_loading_empty_view);
27 | rcv_loading_empty_view = (YRecyclerView) findViewById(R.id.rcv_loading_empty_view);
28 | rcv_loading_empty_view.setLayoutManager(new LinearLayoutManager(this));
29 | rcv_loading_empty_view.showLoadingEmptyView("稍等哦亲!暂无数据~");
30 | demoAdapter = new DemoAdapter(arrayList);
31 | rcv_loading_empty_view.setAdapter(demoAdapter);
32 | rcv_loading_empty_view.setRefreshAndLoadMoreListener(this);
33 | }
34 |
35 | @Override
36 | public void onRefresh() {
37 | new Handler().postDelayed(new Runnable() {
38 | @Override
39 | public void run() {
40 | demoAdapter.addReFreshData();
41 | rcv_loading_empty_view.setReFreshComplete();
42 | rcv_loading_empty_view.setLoadingEmptyViewGone();
43 | }
44 | }, 2500);
45 | }
46 |
47 | @Override
48 | public void onLoadMore() {
49 | new Handler().postDelayed(new Runnable() {
50 | @Override
51 | public void run() {
52 | demoAdapter.addRLoadMOreData();
53 | rcv_loading_empty_view.reSetStatus();
54 | }
55 | }, 2500);
56 |
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/java/cn/yu/yuan/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cn.yu.yuan;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 | import android.widget.Button;
8 |
9 | public class MainActivity extends AppCompatActivity implements View.OnClickListener {
10 |
11 | private Button bt_load_more_refresh;
12 | private Button bt_load_more_refresh_swipe;
13 | private Intent intent;
14 | private Button bt_set_empty_view;
15 | private Button bt_set_loading_empty_view;
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.activity_main);
21 | bt_load_more_refresh = (Button) findViewById(R.id.bt_load_more_refresh);
22 | bt_load_more_refresh_swipe = (Button) findViewById(R.id.bt_load_more_refresh_swipe);
23 | bt_set_empty_view = (Button) findViewById(R.id.bt_set_empty_view);
24 | bt_set_loading_empty_view = (Button) findViewById(R.id.bt_set_loading_empty_view);
25 | bt_load_more_refresh.setOnClickListener(this);
26 | bt_load_more_refresh_swipe.setOnClickListener(this);
27 | bt_set_empty_view.setOnClickListener(this);
28 | bt_set_loading_empty_view.setOnClickListener(this);
29 | }
30 |
31 | @Override
32 | public void onClick(View v) {
33 | switch (v.getId()) {
34 | case R.id.bt_load_more_refresh:
35 | intent = new Intent(MainActivity.this, LoadMoreAndRefreshActivity.class);
36 | startActivity(intent);
37 | break;
38 | case R.id.bt_load_more_refresh_swipe:
39 | intent = new Intent(MainActivity.this, SwipeLoadMoreAndRefreshActivity.class);
40 | startActivity(intent);
41 | break;
42 | case R.id.bt_set_empty_view:
43 | intent = new Intent(MainActivity.this, AddHeadViewActivity.class);
44 | startActivity(intent);
45 | break;
46 | case R.id.bt_set_loading_empty_view:
47 | intent = new Intent(MainActivity.this, LoadingEmptyViewActivity.class);
48 | startActivity(intent);
49 | break;
50 | default:
51 | break;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/java/cn/yu/yuan/SwipeDate.java:
--------------------------------------------------------------------------------
1 | package cn.yu.yuan;
2 |
3 | /**
4 | * Created by yukuoyuan on 2017/3/25.
5 | */
6 |
7 | public class SwipeDate {
8 | public int type;
9 | public String name;
10 | }
11 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/java/cn/yu/yuan/SwipeLoadMoreAndRefreshActivity.java:
--------------------------------------------------------------------------------
1 | package cn.yu.yuan;
2 |
3 | import android.os.Bundle;
4 | import android.os.Handler;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.util.Log;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | import yuan.kuo.yu.view.YRecyclerView;
14 |
15 | /**
16 | * Created by yukuoyuan on 2017/3/25.
17 | * 这是一个测试上啦刷新,下拉加载更多的界面
18 | */
19 | public class SwipeLoadMoreAndRefreshActivity extends AppCompatActivity {
20 | private List list = new ArrayList<>();
21 | private YRecyclerView ycl;
22 |
23 | @Override
24 | protected void onCreate(@Nullable Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.activity_loadmore_refresh);
27 | ycl = (YRecyclerView) findViewById(R.id.ycl);
28 | initData();
29 | }
30 |
31 | private void initData() {
32 | for (int i = 0; i < 20; i++) {
33 | SwipeDate swipeDate = new SwipeDate();
34 | swipeDate.name = "于" + i;
35 | swipeDate.type = 1;
36 | list.add(swipeDate);
37 | }
38 | final DemoSwipeAdapter demoAdapter = new DemoSwipeAdapter(list);
39 | ycl.setLayoutManager(new LinearLayoutManager(this));
40 | ycl.setAdapter(demoAdapter);
41 | ycl.setRefreshAndLoadMoreListener(new YRecyclerView.OnRefreshAndLoadMoreListener() {
42 | @Override
43 | public void onRefresh() {
44 | new Handler().postDelayed(new Runnable() {
45 | public void run() {
46 | demoAdapter.addReFreshData();
47 | ycl.setReFreshComplete();
48 | }
49 | }, 2500);
50 | }
51 |
52 | @Override
53 | public void onLoadMore() {
54 | Log.i("加载更多", "000");
55 | new Handler().postDelayed(new Runnable() {
56 | public void run() {
57 | demoAdapter.addRLoadMOreData();
58 | ycl.setloadMoreComplete();
59 | }
60 | }, 2500);
61 | }
62 | });
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/layout/activity_empty_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
19 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/layout/activity_loading_empty_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/layout/activity_loadmore_refresh.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
18 |
19 |
24 |
25 |
30 |
31 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/layout/head_custom.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/layout/item.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
18 |
19 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/layout/item_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
21 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/layout/item_swipe_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
26 |
27 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukuoyuan/YRecyclerView/d73b6f3441e9567124eee7393719e1ec21f28b81/YRecycleview/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukuoyuan/YRecyclerView/d73b6f3441e9567124eee7393719e1ec21f28b81/YRecycleview/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukuoyuan/YRecyclerView/d73b6f3441e9567124eee7393719e1ec21f28b81/YRecycleview/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukuoyuan/YRecyclerView/d73b6f3441e9567124eee7393719e1ec21f28b81/YRecycleview/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukuoyuan/YRecyclerView/d73b6f3441e9567124eee7393719e1ec21f28b81/YRecycleview/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | YRecycleview
3 |
4 |
--------------------------------------------------------------------------------
/YRecycleview/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/YRecycleview/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.3'
9 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
10 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | maven { url "https://jitpack.io" }
20 | }
21 | }
22 |
23 | //task clean(type: Delete) {
24 | // delete rootProject.buildDir
25 | //}
26 |
--------------------------------------------------------------------------------
/YRecycleview/img/picture.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukuoyuan/YRecyclerView/d73b6f3441e9567124eee7393719e1ec21f28b81/YRecycleview/img/picture.gif
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | apply plugin: 'com.jfrog.bintray'
4 | version = "1.0.4"
5 | android {
6 | compileSdkVersion 23
7 | buildToolsVersion "25.0.0"
8 | resourcePrefix "yukuoyuanyrecycleview" //这个随便填
9 | defaultConfig {
10 | minSdkVersion 15
11 | targetSdkVersion 22
12 | versionCode 1
13 | versionName "1.0"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | }
22 | def siteUrl = 'https://github.com/yukuoyuan/YRecycleview' // 项目的主页
23 | def gitUrl = 'https://github.com/yukuoyuan/YRecycleview.git' // Git仓库的url
24 | group = "cn.yuan.yu.recycleview" // Maven Group ID for the artifact,一般填你唯一的包名
25 | install {
26 | repositories.mavenInstaller {
27 | // This generates POM.xml with proper parameters
28 | pom {
29 | project {
30 | packaging 'aar'
31 | // Add your description here
32 | name 'Android Recycleview' //项目描述
33 | url siteUrl
34 | // Set your license
35 | licenses {
36 | license {
37 | name 'The Apache Software License, Version 2.0'
38 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
39 | }
40 | }
41 | developers {
42 | developer {
43 | id 'yukuoyuan' //填写的一些基本信息
44 | name 'yukuoyuan'
45 | email 'yukuoyuan.@hotmail.com'
46 | }
47 | }
48 | scm {
49 | connection gitUrl
50 | developerConnection gitUrl
51 | url siteUrl
52 | }
53 | }
54 | }
55 | }
56 | }
57 | task sourcesJar(type: Jar) {
58 | from android.sourceSets.main.java.srcDirs
59 | classifier = 'sources'
60 | }
61 | task javadoc(type: Javadoc) {
62 | source = android.sourceSets.main.java.srcDirs
63 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
64 | }
65 | task javadocJar(type: Jar, dependsOn: javadoc) {
66 | classifier = 'javadoc'
67 | from javadoc.destinationDir
68 | }
69 | artifacts {
70 | archives javadocJar
71 | archives sourcesJar
72 | }
73 | Properties properties = new Properties()
74 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
75 | bintray {
76 | user = properties.getProperty("bintray.user")
77 | key = properties.getProperty("bintray.apikey")
78 | configurations = ['archives']
79 | pkg {
80 | repo = "maven"
81 | name = "YRecycleview" //发布到JCenter上的项目名字
82 | websiteUrl = siteUrl
83 | vcsUrl = gitUrl
84 | licenses = ["Apache-2.0"]
85 | publish = true
86 | }
87 | }
88 | dependencies {
89 | compile fileTree(include: ['*.jar'], dir: 'libs')
90 | compile 'com.android.support:appcompat-v7:23.3.0'
91 | compile 'com.android.support:recyclerview-v7:23.3.0'
92 | }
93 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/java/yuan/kuo/yu/load/BaseProgressController.java:
--------------------------------------------------------------------------------
1 | package yuan.kuo.yu.load;
2 |
3 | import android.animation.Animator;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.view.View;
7 |
8 | import java.util.List;
9 |
10 | /**
11 | * Created by yukuo on 2016/4/29.
12 | */
13 | public abstract class BaseProgressController {
14 |
15 |
16 | private View mTarget;
17 |
18 | private List mAnimators;
19 |
20 |
21 | public void setTarget(View target) {
22 | this.mTarget = target;
23 | }
24 |
25 | public View getTarget() {
26 | return mTarget;
27 | }
28 |
29 |
30 | public int getWidth() {
31 | return mTarget.getWidth();
32 | }
33 |
34 | public int getHeight() {
35 | return mTarget.getHeight();
36 | }
37 |
38 | public void postInvalidate() {
39 | mTarget.postInvalidate();
40 | }
41 |
42 | /**
43 | * draw
44 | *
45 | * @param canvas canvas
46 | * @param paint paint
47 | */
48 | public abstract void draw(Canvas canvas, Paint paint);
49 |
50 | public abstract List createAnimation();
51 |
52 | public void initAnimation() {
53 | mAnimators = createAnimation();
54 | }
55 |
56 |
57 | public void setAnimationStatus(AnimStatus animStatus) {
58 | if (mAnimators == null) {
59 | return;
60 | }
61 | int count = mAnimators.size();
62 | for (int i = 0; i < count; i++) {
63 | Animator animator = mAnimators.get(i);
64 | boolean isRunning = animator.isRunning();
65 | switch (animStatus) {
66 | case START:
67 | if (!isRunning) {
68 | animator.start();
69 | }
70 | break;
71 | case END:
72 | if (isRunning) {
73 | animator.end();
74 | }
75 | break;
76 | case CANCEL:
77 | if (isRunning) {
78 | animator.cancel();
79 | }
80 | break;
81 | }
82 | }
83 | }
84 |
85 |
86 | public enum AnimStatus {
87 | START, END, CANCEL
88 | }
89 |
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/java/yuan/kuo/yu/load/CircleLoadingView.java:
--------------------------------------------------------------------------------
1 | package yuan.kuo.yu.load;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.util.AttributeSet;
9 | import android.view.View;
10 |
11 | import yuan.kuo.yu.R;
12 |
13 |
14 | /**
15 | * Created by yukuo on 2016/4/29.
16 | * Custom round progress bar
17 | */
18 | public class CircleLoadingView extends View {
19 | public static final int DEFAULT_SIZE = 30;
20 | int mIndicatorColor;
21 | Paint mPaint;
22 |
23 | BaseProgressController mIndicatorController;
24 |
25 | private boolean mHasAnimation;
26 |
27 |
28 | public CircleLoadingView(Context context) {
29 | super(context);
30 | init(null, 0);
31 | }
32 |
33 | public CircleLoadingView(Context context, AttributeSet attrs) {
34 | super(context, attrs);
35 | init(attrs, 0);
36 | }
37 |
38 | public CircleLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
39 | super(context, attrs, defStyleAttr);
40 | init(attrs, defStyleAttr);
41 | }
42 |
43 | private void init(AttributeSet attrs, int defStyle) {
44 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CircleLoadingView);
45 | mIndicatorColor = a.getColor(R.styleable.CircleLoadingView_progress_color, Color.WHITE);
46 | a.recycle();
47 | mPaint = new Paint();
48 | mPaint.setColor(mIndicatorColor);
49 | mPaint.setStyle(Paint.Style.FILL);
50 | mPaint.setAntiAlias(true);
51 | applyIndicator();
52 | }
53 |
54 | public void setProgressbarColor(int color) {
55 | mIndicatorColor = color;
56 | mPaint.setColor(mIndicatorColor);
57 | this.invalidate();
58 | }
59 |
60 | private void applyIndicator() {
61 | mIndicatorController = new CircleProgress();
62 | mIndicatorController.setTarget(this);
63 | }
64 |
65 | @Override
66 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
67 | int width = measureDimension(dp2px(DEFAULT_SIZE), widthMeasureSpec);
68 | int height = measureDimension(dp2px(DEFAULT_SIZE), heightMeasureSpec);
69 | setMeasuredDimension(width, height);
70 | }
71 |
72 | private int measureDimension(int defaultSize, int measureSpec) {
73 | int result = defaultSize;
74 | int specMode = MeasureSpec.getMode(measureSpec);
75 | int specSize = MeasureSpec.getSize(measureSpec);
76 | if (specMode == MeasureSpec.EXACTLY) {
77 | result = specSize;
78 | } else if (specMode == MeasureSpec.AT_MOST) {
79 | result = Math.min(defaultSize, specSize);
80 | } else {
81 | result = defaultSize;
82 | }
83 | return result;
84 | }
85 |
86 | @Override
87 | protected void onDraw(Canvas canvas) {
88 | super.onDraw(canvas);
89 | drawIndicator(canvas);
90 | }
91 |
92 | @Override
93 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
94 | super.onLayout(changed, left, top, right, bottom);
95 | if (!mHasAnimation) {
96 | mHasAnimation = true;
97 | applyAnimation();
98 | }
99 | }
100 |
101 | @Override
102 | public void setVisibility(int v) {
103 | if (getVisibility() != v) {
104 | super.setVisibility(v);
105 | if (v == GONE || v == INVISIBLE) {
106 | mIndicatorController.setAnimationStatus(BaseProgressController.AnimStatus.END);
107 | } else {
108 | mIndicatorController.setAnimationStatus(BaseProgressController.AnimStatus.START);
109 | }
110 | }
111 | }
112 |
113 | @Override
114 | protected void onDetachedFromWindow() {
115 | super.onDetachedFromWindow();
116 | mIndicatorController.setAnimationStatus(BaseProgressController.AnimStatus.CANCEL);
117 | }
118 |
119 | @Override
120 | protected void onAttachedToWindow() {
121 | super.onAttachedToWindow();
122 | mIndicatorController.setAnimationStatus(BaseProgressController.AnimStatus.START);
123 | }
124 |
125 | private void drawIndicator(Canvas canvas) {
126 | mIndicatorController.draw(canvas, mPaint);
127 | }
128 |
129 | private void applyAnimation() {
130 | mIndicatorController.initAnimation();
131 | }
132 |
133 | private int dp2px(int dpValue) {
134 | return (int) getContext().getResources().getDisplayMetrics().density * dpValue;
135 | }
136 |
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/java/yuan/kuo/yu/load/CircleProgress.java:
--------------------------------------------------------------------------------
1 | package yuan.kuo.yu.load;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ValueAnimator;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | *Created by yukuo on 2016/4/29.
13 | */
14 | public class CircleProgress extends BaseProgressController {
15 |
16 | public static final float SCALE=1.0f;
17 |
18 | public static final int ALPHA=255;
19 |
20 | float[] scaleFloats=new float[]{SCALE,
21 | SCALE,
22 | SCALE,
23 | SCALE,
24 | SCALE,
25 | SCALE,
26 | SCALE,
27 | SCALE};
28 |
29 | int[] alphas=new int[]{ALPHA,
30 | ALPHA,
31 | ALPHA,
32 | ALPHA,
33 | ALPHA,
34 | ALPHA,
35 | ALPHA,
36 | ALPHA};
37 |
38 |
39 | @Override
40 | public void draw(Canvas canvas, Paint paint) {
41 | float radius=getWidth()/10;
42 | for (int i = 0; i < 8; i++) {
43 | canvas.save();
44 | Point point=circleAt(getWidth(),getHeight(),getWidth()/2-radius,i*(Math.PI/4));
45 | canvas.translate(point.x,point.y);
46 | canvas.scale(scaleFloats[i],scaleFloats[i]);
47 | paint.setAlpha(alphas[i]);
48 | canvas.drawCircle(0,0,radius,paint);
49 | canvas.restore();
50 | }
51 | }
52 |
53 | /**
54 | * 圆O的圆心为(a,b),半径为R,点A与到X轴的为角α.
55 | *则点A的坐标为(a+R*cosα,b+R*sinα)
56 | * @param width
57 | * @param height
58 | * @param radius
59 | * @param angle
60 | * @return
61 | */
62 | Point circleAt(int width,int height,float radius,double angle){
63 | float x= (float) (width/2+radius*(Math.cos(angle)));
64 | float y= (float) (height/2+radius*(Math.sin(angle)));
65 | return new Point(x,y);
66 | }
67 |
68 | @Override
69 | public List createAnimation() {
70 | List animators=new ArrayList<>();
71 | int[] delays= {0, 120, 240, 360, 480, 600, 720, 780, 840};
72 | for (int i = 0; i < 8; i++) {
73 | final int index=i;
74 | ValueAnimator scaleAnim=ValueAnimator.ofFloat(1,0.4f,1);
75 | scaleAnim.setDuration(1000);
76 | scaleAnim.setRepeatCount(-1);
77 | scaleAnim.setStartDelay(delays[i]);
78 | scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
79 | @Override
80 | public void onAnimationUpdate(ValueAnimator animation) {
81 | scaleFloats[index] = (float) animation.getAnimatedValue();
82 | postInvalidate();
83 | }
84 | });
85 | scaleAnim.start();
86 |
87 | ValueAnimator alphaAnim=ValueAnimator.ofInt(255, 77, 255);
88 | alphaAnim.setDuration(1000);
89 | alphaAnim.setRepeatCount(-1);
90 | alphaAnim.setStartDelay(delays[i]);
91 | alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
92 | @Override
93 | public void onAnimationUpdate(ValueAnimator animation) {
94 | alphas[index] = (int) animation.getAnimatedValue();
95 | postInvalidate();
96 | }
97 | });
98 | alphaAnim.start();
99 | animators.add(scaleAnim);
100 | animators.add(alphaAnim);
101 | }
102 | return animators;
103 | }
104 |
105 | final class Point{
106 | public float x;
107 | public float y;
108 |
109 | public Point(float x, float y){
110 | this.x=x;
111 | this.y=y;
112 | }
113 | }
114 |
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/java/yuan/kuo/yu/view/YRecyclerView.java:
--------------------------------------------------------------------------------
1 | package yuan.kuo.yu.view;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.widget.GridLayoutManager;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.support.v7.widget.StaggeredGridLayoutManager;
9 | import android.util.AttributeSet;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | /**
18 | * GitHub: https://github.com/yukuoyuan
19 | * Blog : http://blog.csdn.net/easkshark
20 | * Date : 2018/1/16
21 | * Time : 09:07
22 | * Desc : 这是一个重构版的自动刷新布局
23 | * Editor: yukuoyuan
24 | *
25 | * @author yukuoyuan
26 | */
27 |
28 | public class YRecyclerView extends RecyclerView {
29 |
30 | /**
31 | * 是否可以刷新的常量(默认可以刷新)
32 | */
33 | private boolean pullRefreshEnabled = true;
34 | /**
35 | * 是否可以加载更多的常量(默认可以加载更多)
36 | */
37 | private boolean loadMoreEnabled = true;
38 | /**
39 | * 头布局集合
40 | */
41 | private ArrayList mHeaderViews = new ArrayList<>();
42 | /**
43 | * 脚布局集合
44 | */
45 | private ArrayList mFootViews = new ArrayList<>();
46 | /**
47 | * 当前的头布局
48 | */
49 | private YRecycleviewRefreshHeadView mHeadView;
50 | /**
51 | * 刷新或者加载更多的监听器
52 | */
53 | private OnRefreshAndLoadMoreListener refreshAndLoadMoreListener;
54 | /**
55 | * 是否在加载中
56 | */
57 | private boolean isLoadingData = false;
58 | /**
59 | * 是否没有更多的数据了
60 | */
61 | private boolean isNoMore = false;
62 | /**
63 | * 最后的y坐标(默认为-1)
64 | */
65 | private float mLastY = -1;
66 | /**
67 | * 下拉的阻率
68 | */
69 | private static final float DRAG_RATE = 2;
70 | /**
71 | * 适配器
72 | */
73 | private WrapAdapter mWrapAdapter;
74 | /**
75 | * 刷新头布局的类型
76 | */
77 | private static final int TYPE_REFRESH_HEADER = -5;
78 | /**
79 | * 正常数据类型
80 | */
81 | private static final int TYPE_NORMAL = 0;
82 | /**
83 | * 脚布局类型
84 | */
85 | private static final int TYPE_FOOTER = -3;
86 | /**
87 | * 头布局类型集合
88 | */
89 | private static List sHeaderTypes = new ArrayList<>();
90 | /**
91 | * 数据观察者
92 | */
93 | private final RecyclerView.AdapterDataObserver mDataObserver = new DataObserver();
94 | private static final int HEADER_INIT_INDEX = 10000;
95 |
96 | public YRecyclerView(Context context) {
97 | this(context, null);
98 | }
99 |
100 | public YRecyclerView(Context context, @Nullable AttributeSet attrs) {
101 | this(context, attrs, 0);
102 | }
103 |
104 | public YRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
105 | super(context, attrs, defStyle);
106 | /**
107 | * 初始化
108 | */
109 | init(context);
110 | }
111 |
112 |
113 | /**
114 | * 这是一个初始化的方法
115 | *
116 | * @param context
117 | */
118 | private void init(Context context) {
119 | //可以进行刷新
120 | if (pullRefreshEnabled) {
121 | //获取头布局
122 | YRecycleviewRefreshHeadView refreshHeader = new YRecycleviewRefreshHeadView(getContext());
123 | //添加进去
124 | mHeaderViews.add(0, refreshHeader);
125 | mHeadView = refreshHeader;
126 | }
127 | YRecycleviewRefreshFootView footView = new YRecycleviewRefreshFootView(getContext());
128 | // LinearLayout.LayoutParams mFootViewlayoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
129 | // footView.setLayoutParams(mFootViewlayoutParams);
130 | addFootView(footView);
131 | mFootViews.get(0).setVisibility(GONE);
132 | }
133 |
134 | /**
135 | * 当滚动状态改变的时候调用的方法
136 | *
137 | * @param screenState 滚动状态,0是滚动停止的状态,1正在被外部拖拽,一般为用户正在用手指滚动,2自动滚动开始
138 | */
139 | @Override
140 | public void onScrollStateChanged(int screenState) {
141 | super.onScrollStateChanged(screenState);
142 | /**
143 | * 如果滚动状态停止,并且刷新监听不为空,并且不是正在加载数据,并且可以进行刷新
144 | */
145 | if (screenState == RecyclerView.SCROLL_STATE_IDLE && refreshAndLoadMoreListener != null && !isLoadingData && loadMoreEnabled) {
146 | /**
147 | * 获取布局管理
148 | */
149 | LayoutManager layoutManager = getLayoutManager();
150 | /**
151 | * 最后一个显示的条目
152 | */
153 | int lastVisibleItemPosition;
154 | /**
155 | * 如果是网格布局
156 | */
157 | if (layoutManager instanceof GridLayoutManager) {
158 | lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
159 | } else if (layoutManager instanceof StaggeredGridLayoutManager) {
160 | /**
161 | * 如果是瀑布流布局
162 | */
163 | int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
164 | ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into);
165 | lastVisibleItemPosition = findMax(into);
166 | } else {
167 | /**
168 | * 如果是线性布局
169 | */
170 | lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
171 | }
172 | /**
173 | * 如果说有数据,最后一条显示的数据是最后一个条目,并且自孩子条目大于,并且还有数据,并且脚布局的状态是不在刷新中
174 | */
175 | if (layoutManager.getChildCount() > 0
176 | && lastVisibleItemPosition >= layoutManager.getItemCount() - 2 && layoutManager.getItemCount() > layoutManager.getChildCount()
177 | && !isNoMore && mHeadView.getStatus() < YRecycleviewRefreshHeadView.STATE_REFRESHING) {
178 | /**
179 | * 获取脚布局
180 | */
181 | View footView = mFootViews.get(0);
182 | isLoadingData = true;
183 | /**
184 | * 如果脚布局是刷新脚布局的话(设置刷新中)
185 | */
186 | if (footView instanceof YRecycleviewRefreshFootView) {
187 | ((YRecycleviewRefreshFootView) footView).setState(YRecycleviewRefreshFootView.STATE_LOADING);
188 | } else {
189 | /**
190 | * 如果是其他布局的话就显示
191 | */
192 | footView.setVisibility(View.VISIBLE);
193 | }
194 | /**
195 | * 回调刷新中的监听
196 | */
197 | refreshAndLoadMoreListener.onLoadMore();
198 | }
199 | }
200 | }
201 |
202 | /**
203 | * 当触摸控件的时候调用的方法
204 | *
205 | * @param e 触摸事件
206 | * @return 是否拦截事件
207 | */
208 | @Override
209 | public boolean onTouchEvent(MotionEvent e) {
210 | if (mLastY == -1) {
211 | mLastY = e.getRawY();
212 | }
213 | switch (e.getAction()) {
214 | /**
215 | * 当按下的时候
216 | */
217 | case MotionEvent.ACTION_DOWN:
218 | mLastY = e.getRawY();
219 | break;
220 | /**
221 | * 当移动的时候
222 | */
223 | case MotionEvent.ACTION_MOVE:
224 | final float deltaY = e.getRawY() - mLastY;
225 | mLastY = e.getRawY();
226 | if (isOnTop() && pullRefreshEnabled) {
227 | mHeadView.onMove(deltaY / DRAG_RATE);
228 | if (mHeadView.getVisibleHeight() > 0 && mHeadView.getStatus() < YRecycleviewRefreshHeadView.STATE_REFRESHING) {
229 | return false;
230 | }
231 | }
232 | break;
233 | /**
234 | * 手指离开
235 | */
236 | default:
237 | mLastY = -1;
238 | /**
239 | * 如果在顶部,并且还可以刷新
240 | */
241 | if (isOnTop() && pullRefreshEnabled) {
242 | /**
243 | * 如果头布局释放意图,就回调刷新
244 | */
245 | if (mHeadView.releaseAction()) {
246 | if (refreshAndLoadMoreListener != null) {
247 | refreshAndLoadMoreListener.onRefresh();
248 | }
249 | }
250 | }
251 | break;
252 | }
253 | return super.onTouchEvent(e);
254 | }
255 |
256 | /**
257 | * 设置适配器
258 | *
259 | * @param adapter 包装的适配器
260 | */
261 | @Override
262 | public void setAdapter(Adapter adapter) {
263 | mWrapAdapter = new WrapAdapter(adapter);
264 | super.setAdapter(mWrapAdapter);
265 | adapter.registerAdapterDataObserver(mDataObserver);
266 | mDataObserver.onChanged();
267 | }
268 |
269 |
270 | /**
271 | * 包装的适配器
272 | */
273 | private class WrapAdapter extends RecyclerView.Adapter {
274 | private RecyclerView.Adapter adapter;
275 | /**
276 | * 选择的位置
277 | */
278 | private int mCurrentPosition;
279 | /**
280 | * 头布局位置
281 | */
282 | private int headerPosition = 1;
283 |
284 | public WrapAdapter(Adapter adapter) {
285 | this.adapter = adapter;
286 | }
287 |
288 | /**
289 | * 获取条目类型
290 | *
291 | * @param position
292 | * @return
293 | */
294 | @Override
295 | public int getItemViewType(int position) {
296 | if (isRefreshHeader(position)) {
297 | return TYPE_REFRESH_HEADER;
298 | }
299 | if (isHeader(position)) {
300 | position = position - 1;
301 | return sHeaderTypes.get(position);
302 | }
303 | if (isFooter(position)) {
304 | return TYPE_FOOTER;
305 | }
306 | int adjPosition = position - getHeadersCount();
307 | int adapterCount;
308 | if (adapter != null) {
309 | adapterCount = adapter.getItemCount();
310 | if (adjPosition < adapterCount) {
311 | return adapter.getItemViewType(adjPosition);
312 | }
313 | }
314 | return TYPE_NORMAL;
315 | }
316 |
317 | @Override
318 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
319 | if (viewType == TYPE_REFRESH_HEADER) {
320 | mCurrentPosition++;
321 | return new SimpleViewHolder(mHeaderViews.get(0));
322 | } else if (isContentHeader(mCurrentPosition)) {
323 | if (viewType == sHeaderTypes.get(mCurrentPosition - 1)) {
324 | mCurrentPosition++;
325 | return new SimpleViewHolder(mHeaderViews.get(headerPosition++));
326 | }
327 | } else if (viewType == TYPE_FOOTER) {
328 | return new SimpleViewHolder(mFootViews.get(0));
329 | }
330 | return adapter.onCreateViewHolder(parent, viewType);
331 | }
332 |
333 | @Override
334 | public void onBindViewHolder(ViewHolder holder, int position) {
335 | /**
336 | * 处理回来的position索引
337 | */
338 | if (isHeader(position)) {
339 | return;
340 | }
341 | int adjPosition = position - getHeadersCount();
342 | int adapterCount;
343 | if (adapter != null) {
344 | adapterCount = adapter.getItemCount();
345 | if (adjPosition < adapterCount) {
346 | adapter.onBindViewHolder(holder, adjPosition);
347 | return;
348 | }
349 | }
350 | }
351 |
352 | /**
353 | * 获取条目列表数据
354 | *
355 | * @return
356 | */
357 | @Override
358 | public int getItemCount() {
359 | if (adapter != null) {
360 | return getHeadersCount() + getFootersCount() + adapter.getItemCount();
361 | } else {
362 | return getHeadersCount() + getFootersCount();
363 | }
364 | }
365 |
366 | @Override
367 | public long getItemId(int position) {
368 | if (adapter != null && position >= getHeadersCount()) {
369 | int adjPosition = position - getHeadersCount();
370 | int adapterCount = adapter.getItemCount();
371 | if (adjPosition < adapterCount) {
372 | return adapter.getItemId(adjPosition);
373 | }
374 | }
375 | return -1;
376 | }
377 |
378 | @Override
379 | public void onAttachedToRecyclerView(RecyclerView recyclerView) {
380 | super.onAttachedToRecyclerView(recyclerView);
381 | RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
382 | if (manager instanceof GridLayoutManager) {
383 | final GridLayoutManager gridManager = ((GridLayoutManager) manager);
384 | gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
385 | @Override
386 | public int getSpanSize(int position) {
387 | return (isHeader(position) || isFooter(position))
388 | ? gridManager.getSpanCount() : 1;
389 | }
390 | });
391 | }
392 | }
393 |
394 | @Override
395 | public void onViewAttachedToWindow(ViewHolder holder) {
396 | super.onViewAttachedToWindow(holder);
397 | ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
398 | if (lp != null
399 | && lp instanceof StaggeredGridLayoutManager.LayoutParams
400 | && (isHeader(holder.getLayoutPosition()) || isFooter(holder.getLayoutPosition()))) {
401 | StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
402 | p.setFullSpan(true);
403 | }
404 | }
405 |
406 | /**
407 | * 是否是头布局
408 | *
409 | * @param position
410 | * @return
411 | */
412 | public boolean isHeader(int position) {
413 | return position >= 0 && position < mHeaderViews.size();
414 | }
415 |
416 | /**
417 | * 是否刷新头布局
418 | *
419 | * @param position
420 | * @return
421 | */
422 | public boolean isRefreshHeader(int position) {
423 | return position == 0;
424 | }
425 |
426 | /**
427 | * 是否是内容头布局
428 | *
429 | * @param position
430 | * @return
431 | */
432 | public boolean isContentHeader(int position) {
433 | return position >= 1 && position < mHeaderViews.size();
434 | }
435 |
436 | /**
437 | * 是否是低脚布局
438 | *
439 | * @param position
440 | * @return
441 | */
442 | public boolean isFooter(int position) {
443 | return position < getItemCount() && position >= getItemCount() - mFootViews.size();
444 | }
445 |
446 | /**
447 | * 获取头布局条目
448 | *
449 | * @return
450 | */
451 | public int getHeadersCount() {
452 | return mHeaderViews.size();
453 | }
454 |
455 | /**
456 | * 获取脚布局的条目
457 | *
458 | * @return
459 | */
460 | public int getFootersCount() {
461 | return mFootViews.size();
462 | }
463 |
464 | @Override
465 | public void registerAdapterDataObserver(AdapterDataObserver observer) {
466 | if (adapter != null) {
467 | adapter.registerAdapterDataObserver(observer);
468 | }
469 | }
470 |
471 | @Override
472 | public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
473 | if (adapter != null) {
474 | adapter.unregisterAdapterDataObserver(observer);
475 | }
476 | }
477 |
478 | private class SimpleViewHolder extends RecyclerView.ViewHolder {
479 | public SimpleViewHolder(View itemView) {
480 | super(itemView);
481 | }
482 | }
483 | }
484 |
485 | /**
486 | * 这是一个数据观察者
487 | */
488 | private class DataObserver extends RecyclerView.AdapterDataObserver {
489 | @Override
490 | public void onChanged() {
491 | if (mWrapAdapter != null) {
492 | mWrapAdapter.notifyDataSetChanged();
493 | }
494 | }
495 |
496 | @Override
497 | public void onItemRangeInserted(int positionStart, int itemCount) {
498 | mWrapAdapter.notifyItemRangeInserted(positionStart, itemCount);
499 | }
500 |
501 | @Override
502 | public void onItemRangeChanged(int positionStart, int itemCount) {
503 | mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount);
504 | }
505 |
506 | @Override
507 | public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
508 | mWrapAdapter.notifyItemRangeChanged(positionStart, itemCount, payload);
509 | }
510 |
511 | @Override
512 | public void onItemRangeRemoved(int positionStart, int itemCount) {
513 | mWrapAdapter.notifyItemRangeRemoved(positionStart, itemCount);
514 | }
515 |
516 | @Override
517 | public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
518 | mWrapAdapter.notifyItemMoved(fromPosition, toPosition);
519 | }
520 |
521 | }
522 |
523 | /**
524 | * 是否是顶部
525 | *
526 | * @return 是否在顶部
527 | */
528 | public boolean isOnTop() {
529 | /**
530 | * 如果有头布局.并且头布局的父布局不为空
531 | */
532 | return !(mHeaderViews == null || mHeaderViews.isEmpty()) && mHeaderViews.get(0).getParent() != null;
533 | }
534 | /**
535 | * 添加头布局
536 | *
537 | * @param headView 刷新头布局
538 | */
539 | public void addHeadView(View headView) {
540 | if (pullRefreshEnabled && !(mHeaderViews.get(0) instanceof YRecycleviewRefreshHeadView)) {
541 | YRecycleviewRefreshHeadView refreshHeader = new YRecycleviewRefreshHeadView(getContext());
542 | mHeaderViews.add(0, refreshHeader);
543 | mHeadView = refreshHeader;
544 | }
545 | mHeaderViews.add(headView);
546 | sHeaderTypes.add(HEADER_INIT_INDEX + mHeaderViews.size());
547 | }
548 | /**
549 | * 添加底部布局
550 | *
551 | * @param footView 布局
552 | */
553 | private void addFootView(View footView) {
554 | mFootViews.add(footView);
555 | }
556 |
557 | /**
558 | * 获取最大值
559 | *
560 | * @param into 数组数据源
561 | * @return 返回最大值
562 | */
563 | private int findMax(int[] into) {
564 | int max = into[0];
565 | for (int value : into) {
566 | if (value > max) {
567 | max = value;
568 | }
569 | }
570 | return max;
571 | }
572 |
573 | /**
574 | * 设置刷新和加载更多的监听器
575 | *
576 | * @param refreshAndLoadMoreListener 监听器
577 | */
578 | public void setRefreshAndLoadMoreListener(OnRefreshAndLoadMoreListener refreshAndLoadMoreListener) {
579 | this.refreshAndLoadMoreListener = refreshAndLoadMoreListener;
580 | }
581 |
582 | /**
583 | * 刷新和加载更多的监听器
584 | */
585 | public interface OnRefreshAndLoadMoreListener {
586 | /**
587 | * 刷新数据的回调
588 | */
589 | void onRefresh();
590 |
591 | /**
592 | * 加载数据的回调
593 | */
594 | void onLoadMore();
595 | }
596 |
597 | /**
598 | * 设置是否启用下拉刷新功能
599 | *
600 | * @param isEnabled 是否可以进行下拉刷新
601 | */
602 | public void setRefreshEnabled(boolean isEnabled) {
603 | pullRefreshEnabled = isEnabled;
604 | }
605 |
606 | /**
607 | * 设置刷新完成
608 | */
609 | public void setReFreshComplete() {
610 | mHeadView.refreshComplete();
611 | }
612 |
613 | /**
614 | * 设置加载更多完成
615 | */
616 | public void setloadMoreComplete() {
617 | //设置加载数据为false
618 | isLoadingData = false;
619 | View footView = mFootViews.get(0);
620 | if (footView instanceof YRecycleviewRefreshFootView) {
621 | ((YRecycleviewRefreshFootView) footView).setState(YRecycleviewRefreshFootView.STATE_COMPLETE);
622 | } else {
623 | footView.setVisibility(View.GONE);
624 | }
625 | }
626 |
627 | /**
628 | * 设置是否启用上拉加载功能
629 | *
630 | * @param isEnabled 是否启用上啦加载更多
631 | */
632 | public void setLoadMoreEnabled(boolean isEnabled) {
633 | loadMoreEnabled = isEnabled;
634 | //如果不启用加载更多功能,就隐藏脚布局
635 | if (!isEnabled && mFootViews.size() > 0) {
636 | mFootViews.get(0).setVisibility(GONE);
637 | }
638 | }
639 |
640 | /**
641 | * 设置没有更多数据了
642 | *
643 | * @param noMore 是否没有更多数据
644 | */
645 | public void setNoMoreData(boolean noMore) {
646 | this.isNoMore = noMore;
647 | View footView = mFootViews.get(0);
648 | ((YRecycleviewRefreshFootView) footView).setState(isNoMore ? YRecycleviewRefreshFootView.STATE_NOMORE : YRecycleviewRefreshFootView.STATE_COMPLETE);
649 | }
650 |
651 | /**
652 | * 设置是否没有数据(列表数据为空)
653 | *
654 | * @param isNo 是否没有数据
655 | * @param emptyContext 空数据展示的提示
656 | */
657 | public void setIsNoData(boolean isNo, String emptyContext) {
658 | this.isNoMore = isNo;
659 | View footView = mFootViews.get(0);
660 | ((YRecycleviewRefreshFootView) footView).setState(isNoMore ? YRecycleviewRefreshFootView.STATE_NODATA : YRecycleviewRefreshFootView.STATE_COMPLETE);
661 | ((YRecycleviewRefreshFootView) footView).setEmptyTips(emptyContext);
662 | }
663 |
664 | /**
665 | * 还原所有的状态
666 | */
667 | public void reSetStatus() {
668 | setloadMoreComplete();
669 | setReFreshComplete();
670 | }
671 |
672 | /**
673 | * 设置可以加载的空布局
674 | *
675 | * @param emptyContext 空数据展示的提示
676 | */
677 | public void showLoadingEmptyView(String emptyContext) {
678 | setIsNoData(true, emptyContext);
679 | }
680 |
681 | /**
682 | * 设置隐藏脚布局
683 | */
684 | public void setLoadingEmptyViewGone() {
685 | setIsNoData(false, "");
686 | }
687 | }
688 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/java/yuan/kuo/yu/view/YRecycleviewRefreshFootView.java:
--------------------------------------------------------------------------------
1 | package yuan.kuo.yu.view;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.Gravity;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.LinearLayout;
9 | import android.widget.TextView;
10 |
11 | import yuan.kuo.yu.R;
12 | import yuan.kuo.yu.load.CircleLoadingView;
13 |
14 |
15 | /**
16 | * @author yukuoyuan
17 | */
18 | public class YRecycleviewRefreshFootView extends LinearLayout {
19 |
20 | private CircleLoadingView pb_y_recycleview_foot_loadmore_progressbar;
21 | private TextView tv_y_recycleview_foot_loadmore_status;
22 | private TextView tv_y_recycleview_foot_loadmore_nodata;
23 | private LinearLayout ll_recycleview_foot_loadmore_no_more_data;
24 | /**
25 | * 加载中
26 | */
27 | public final static int STATE_LOADING = 0;
28 | /**
29 | * 加载完成
30 | */
31 | public final static int STATE_COMPLETE = 1;
32 | /**
33 | * 没有更多的数据
34 | */
35 | public final static int STATE_NOMORE = 2;
36 | /**
37 | * 没有数据
38 | */
39 | public final static int STATE_NODATA = 3;
40 |
41 | public YRecycleviewRefreshFootView(Context context) {
42 | super(context);
43 | initView(context);
44 | }
45 |
46 | public YRecycleviewRefreshFootView(Context context, AttributeSet attrs) {
47 | super(context, attrs);
48 | initView(context);
49 | }
50 |
51 | /**
52 | * 初始化
53 | */
54 | private void initView(Context context) {
55 | //设置内部内容居中
56 | setGravity(Gravity.CENTER);
57 | //设置宽高
58 | setLayoutParams(new ViewGroup.LayoutParams(
59 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
60 | //底部布局
61 | View mContentView = View.inflate(context, R.layout.foot_recycleview_loadmore, null);
62 | pb_y_recycleview_foot_loadmore_progressbar = (CircleLoadingView) mContentView.findViewById(R.id.pb_y_recycleview_foot_loadmore_progressbar);
63 | tv_y_recycleview_foot_loadmore_status = (TextView) mContentView.findViewById(R.id.tv_y_recycleview_foot_loadmore_status);
64 | tv_y_recycleview_foot_loadmore_nodata = (TextView) mContentView.findViewById(R.id.tv_y_recycleview_foot_loadmore_nodata);
65 | ll_recycleview_foot_loadmore_no_more_data = (LinearLayout) mContentView.findViewById(R.id.ll_recycleview_foot_loadmore_no_more_data);
66 | addView(mContentView);
67 | }
68 |
69 | /**
70 | * 设置当前状态
71 | *
72 | * @param state 需要展示的状态
73 | */
74 | public void setState(int state) {
75 | switch (state) {
76 | case STATE_LOADING:
77 | pb_y_recycleview_foot_loadmore_progressbar.setVisibility(View.VISIBLE);
78 | tv_y_recycleview_foot_loadmore_status.setText("加载中...");
79 | ll_recycleview_foot_loadmore_no_more_data.setVisibility(VISIBLE);
80 | tv_y_recycleview_foot_loadmore_nodata.setVisibility(GONE);
81 |
82 | this.setVisibility(View.VISIBLE);
83 | break;
84 | case STATE_COMPLETE:
85 | tv_y_recycleview_foot_loadmore_status.setText("加载中...");
86 | this.setVisibility(View.GONE);
87 | break;
88 | case STATE_NOMORE:
89 | tv_y_recycleview_foot_loadmore_status.setText("没有更多了..");
90 | pb_y_recycleview_foot_loadmore_progressbar.setVisibility(View.GONE);
91 | ll_recycleview_foot_loadmore_no_more_data.setVisibility(VISIBLE);
92 | tv_y_recycleview_foot_loadmore_nodata.setVisibility(GONE);
93 | this.setVisibility(View.VISIBLE);
94 | break;
95 | case STATE_NODATA:
96 | tv_y_recycleview_foot_loadmore_nodata.setVisibility(VISIBLE);
97 | ll_recycleview_foot_loadmore_no_more_data.setVisibility(GONE);
98 | this.setVisibility(View.VISIBLE);
99 | break;
100 | default:
101 | break;
102 | }
103 | }
104 |
105 | /**
106 | * 设置空布局的提示内容
107 | *
108 | * @param emptyText 空布局展示的提示语
109 | */
110 | public void setEmptyTips(String emptyText) {
111 | tv_y_recycleview_foot_loadmore_nodata.setText(emptyText);
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/java/yuan/kuo/yu/view/YRecycleviewRefreshHeadView.java:
--------------------------------------------------------------------------------
1 | package yuan.kuo.yu.view;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Context;
5 | import android.os.Handler;
6 | import android.util.AttributeSet;
7 | import android.view.Gravity;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.view.animation.Animation;
11 | import android.view.animation.RotateAnimation;
12 | import android.widget.ImageView;
13 | import android.widget.LinearLayout;
14 | import android.widget.TextView;
15 |
16 | import yuan.kuo.yu.R;
17 | import yuan.kuo.yu.load.CircleLoadingView;
18 |
19 |
20 | /**
21 | * Created by yukuo on 2016/4/29.
22 | * 这是一个recycleview的刷新的头布局
23 | */
24 | public class YRecycleviewRefreshHeadView extends LinearLayout {
25 | //刷新头布局
26 | LinearLayout mContentView;
27 | //这是一个旋转动画
28 | private RotateAnimation mRotateUpAnim;
29 | private RotateAnimation mRotateDownAnim;
30 | //旋转默认的动画时间
31 | private static final int ROTATE_ANIM_DURATION = 180;
32 | private int mMeasuredHeight;
33 | private int mStatus;
34 | //正在刷新的状态
35 | public final static int STATE_REFRESHING = 2;
36 | //刷新结束的状态
37 | public final static int STATE_DONE = 3;
38 | //没有任何的状态
39 | public final static int STATE_NORMAL = 0;
40 | //准备刷新的状态
41 | private final int STATE_RELEASE_TO_REFRESH = 1;
42 | private ImageView iv_y_recycleview_head_refresh_status;
43 | private CircleLoadingView pb_y_recycleview_head_refresh_progressbar;
44 | private TextView tv_y_recycleview_head_refresh_status;
45 |
46 | public YRecycleviewRefreshHeadView(Context context) {
47 | super(context);
48 | initView(context);
49 | }
50 |
51 | public YRecycleviewRefreshHeadView(Context context, AttributeSet attrs) {
52 | super(context, attrs);
53 | initView(context);
54 | }
55 |
56 | /**
57 | * 初始化
58 | */
59 | private void initView(Context context) {
60 | mContentView = (LinearLayout) View.inflate(context, R.layout.head_recycleview_refresh, null);
61 | //宽高
62 | LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
63 | //距离父布局的距离
64 | lp.setMargins(0, 0, 0, 0);
65 | //设置布局的宽高的属性
66 | this.setLayoutParams(lp);
67 | //设置内容距离布局的边界的尺寸
68 | this.setPadding(0, 0, 0, 0);
69 | //添加内容布局并且设置宽是屏幕的宽.高为0
70 | addView(mContentView, new LayoutParams(LayoutParams.MATCH_PARENT, 0));
71 | //设置内容位于布局的下方
72 | setGravity(Gravity.BOTTOM);
73 | //查找头布局控件
74 | //刷新状态字体
75 | tv_y_recycleview_head_refresh_status = (TextView) findViewById(R.id.tv_y_recycleview_head_refresh_status);
76 | //刷新状态的箭头
77 | iv_y_recycleview_head_refresh_status = (ImageView) findViewById(R.id.iv_y_recycleview_head_refresh_status);
78 | //正在刷新的进度条
79 | pb_y_recycleview_head_refresh_progressbar = (CircleLoadingView) findViewById(R.id.pb_y_recycleview_head_refresh_progressbar);
80 | //定义一个向上旋转动画
81 | mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,
82 | Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
83 | mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);
84 | //设置旋转之后保持旋转后的状态
85 | mRotateUpAnim.setFillAfter(true);
86 | //定义一个向下旋转的动画
87 | mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,
88 | Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
89 | mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);
90 | mRotateDownAnim.setFillAfter(true);
91 | //测量
92 | measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
93 | //获取高度
94 | mMeasuredHeight = getMeasuredHeight();
95 | }
96 |
97 | /**
98 | * 设置这个刷新状态的方法
99 | *
100 | * @param status 设置展示的状态
101 | */
102 | public void setState(int status) {
103 | //如果状态一致就不要更改
104 | if (status == mStatus) {
105 | return;
106 | }
107 | /**
108 | * 设置当前布局格局状态隐藏和显示
109 | */
110 | //设置刷新状态
111 | if (status == STATE_REFRESHING) {
112 | //清除箭头的动画效果并且隐藏
113 | iv_y_recycleview_head_refresh_status.clearAnimation();
114 | iv_y_recycleview_head_refresh_status.setVisibility(View.INVISIBLE);
115 | //展示进度条
116 | pb_y_recycleview_head_refresh_progressbar.setVisibility(View.VISIBLE);
117 | } else if (status == STATE_DONE) {
118 | //刷新结束状态
119 | iv_y_recycleview_head_refresh_status.setVisibility(View.INVISIBLE);
120 | pb_y_recycleview_head_refresh_progressbar.setVisibility(View.INVISIBLE);
121 | } else {
122 | //正常状态隐藏进度条显示箭头
123 | iv_y_recycleview_head_refresh_status.setVisibility(View.VISIBLE);
124 | pb_y_recycleview_head_refresh_progressbar.setVisibility(View.INVISIBLE);
125 | }
126 | /**
127 | * 根据状态进行动画的调整
128 | */
129 | switch (status) {
130 | case STATE_NORMAL:
131 | //如果当前状态是准备做刷新就开始动画
132 | if (mStatus == STATE_RELEASE_TO_REFRESH) {
133 | iv_y_recycleview_head_refresh_status.startAnimation(mRotateDownAnim);
134 | }
135 | //如果当前状态正在刷新,就停止动画
136 | if (mStatus == STATE_REFRESHING) {
137 | iv_y_recycleview_head_refresh_status.clearAnimation();
138 | }
139 | tv_y_recycleview_head_refresh_status.setText("下拉刷新");
140 | break;
141 |
142 | case STATE_RELEASE_TO_REFRESH:
143 | //如果当前状态不是准备去刷新
144 | if (mStatus != STATE_RELEASE_TO_REFRESH) {
145 | iv_y_recycleview_head_refresh_status.clearAnimation();
146 | iv_y_recycleview_head_refresh_status.startAnimation(mRotateUpAnim);
147 | tv_y_recycleview_head_refresh_status.setText("松开刷新");
148 | }
149 | break;
150 | case STATE_REFRESHING:
151 | tv_y_recycleview_head_refresh_status.setText("正在刷新");
152 | break;
153 | case STATE_DONE:
154 | tv_y_recycleview_head_refresh_status.setText("刷新完成");
155 | break;
156 | default:
157 | }
158 | mStatus = status;
159 | }
160 |
161 | /**
162 | * 返回当前的状态
163 | *
164 | * @return 当前的状态
165 | */
166 | public int getStatus() {
167 | return mStatus;
168 | }
169 |
170 | /**
171 | * 是否在刷新
172 | *
173 | * @return 是否正在刷新
174 | */
175 | public boolean releaseAction() {
176 | //是否在刷新
177 | boolean isOnRefresh = false;
178 | //获取显示的高度
179 | int height = getVisibleHeight();
180 | if (height == 0) // not visible.
181 | isOnRefresh = false;//没有刷新
182 | if (getVisibleHeight() > mMeasuredHeight && mStatus < STATE_REFRESHING) {
183 | setState(STATE_REFRESHING);
184 | isOnRefresh = true;//正在刷新
185 | }
186 | // refreshing and header isn't shown fully. do nothing.
187 | if (mStatus == STATE_REFRESHING && height <= mMeasuredHeight) {
188 | //return;
189 | }
190 | int destHeight = 0; // default: scroll back to dismiss header.
191 | // is refreshing, just scroll back to show all the header.
192 | if (mStatus == STATE_REFRESHING) {
193 | destHeight = mMeasuredHeight;
194 | }
195 | smoothScrollTo(destHeight);
196 |
197 | return isOnRefresh;
198 | }
199 |
200 | /**
201 | * 移动距离
202 | *
203 | * @param delta 移动的距离
204 | */
205 | public void onMove(float delta) {
206 | if (getVisibleHeight() > 0 || delta > 0) {
207 | setVisibleHeight((int) delta + getVisibleHeight());
208 | if (mStatus <= STATE_RELEASE_TO_REFRESH) { // 未处于刷新状态,更新箭头
209 | if (getVisibleHeight() > mMeasuredHeight) {
210 | setState(STATE_RELEASE_TO_REFRESH);
211 | } else {
212 | setState(STATE_NORMAL);
213 | }
214 | }
215 | }
216 | }
217 |
218 | /**
219 | * 这是一个让布局高度改变的方法
220 | *
221 | * @param Height 动态的高度
222 | */
223 | public void smoothScrollTo(int Height) {
224 | ValueAnimator animator = ValueAnimator.ofInt(getVisibleHeight(), Height);
225 | animator.setDuration(300).start();
226 | animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
227 | @Override
228 | public void onAnimationUpdate(ValueAnimator animation) {
229 | setVisibleHeight((int) animation.getAnimatedValue());
230 | }
231 | });
232 | animator.start();
233 | }
234 |
235 | /**
236 | * 设置布局高度为0,并设置状态为正常状态
237 | */
238 | public void reset() {
239 | smoothScrollTo(0);
240 | new Handler().postDelayed(new Runnable() {
241 | public void run() {
242 | setState(STATE_NORMAL);
243 | }
244 | }, 500);
245 | }
246 |
247 | /**
248 | * 刷新完成
249 | */
250 | public void refreshComplete() {
251 | setState(STATE_DONE);
252 | new Handler().postDelayed(new Runnable() {
253 | public void run() {
254 | reset();
255 | }
256 | }, 200);
257 | }
258 |
259 | /**
260 | * 获取显示的高度
261 | *
262 | * @return 获取显示的高度
263 | */
264 | public int getVisibleHeight() {
265 | LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
266 | return lp.height;
267 | }
268 |
269 | /**
270 | * 设置显示的高度
271 | *
272 | * @param height 设置显示的高度
273 | */
274 | public void setVisibleHeight(int height) {
275 | if (height < 0) height = 0;
276 | LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
277 | lp.height = height;
278 | mContentView.setLayoutParams(lp);
279 | }
280 |
281 | }
282 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/res/drawable-xhdpi/arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yukuoyuan/YRecyclerView/d73b6f3441e9567124eee7393719e1ec21f28b81/YRecycleview/mylibrary/src/main/res/drawable-xhdpi/arrow_down.png
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/res/layout/emptyview.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/res/layout/foot_recycleview_loadmore.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
21 |
22 |
28 |
29 |
30 |
37 |
38 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/res/layout/head_recycleview_refresh.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
21 |
22 |
27 |
28 |
29 |
30 |
38 |
39 |
48 |
49 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/YRecycleview/mylibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | My Library
3 |
4 |
--------------------------------------------------------------------------------
/YRecycleview/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':mylibrary'
2 |
--------------------------------------------------------------------------------