├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── cc
│ │ └── library
│ │ └── view
│ │ ├── DragSwipeDemoAct.java
│ │ ├── MenuAct.java
│ │ ├── PtrSwipeDemoAct.java
│ │ ├── adapter
│ │ ├── CustomAdapter.java
│ │ ├── DragSwipeAdapter.java
│ │ └── SwipeAdapter.java
│ │ ├── base
│ │ └── BaseApplication.java
│ │ ├── util
│ │ ├── DisplayUtils.java
│ │ └── ToastUtil.java
│ │ └── widget
│ │ ├── MFooterView.java
│ │ └── MHeaderView.java
│ └── res
│ ├── layout
│ ├── activity_menu.xml
│ ├── activity_swipe_demo.xml
│ ├── adapter_recyclerview_menu_demo.xml
│ ├── content_view_for_test.xml
│ ├── m_footer_view.xml
│ ├── m_header_view.xml
│ ├── menu1_for_test.xml
│ └── menu_for_test.xml
│ ├── menu
│ └── prt_act_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
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── loadmore.jpg
├── refreshing.jpg
└── swipemenu.jpg
├── library
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── library
│ │ └── widget
│ │ ├── adapter
│ │ └── SwipeMenuAdapter.java
│ │ ├── baseview
│ │ ├── FooterView.java
│ │ ├── HeaderView.java
│ │ ├── PtrSwipeMenuRecyclerView.java
│ │ └── SwipeMenuLayout.java
│ │ ├── defaultimple
│ │ ├── DefaultFooterView.java
│ │ └── DefaultHeaderView.java
│ │ └── interfaces
│ │ └── OnMenuClickListener.java
│ └── res
│ ├── layout
│ ├── pull_to_refresh_footer.xml
│ └── pull_to_refresh_header.xml
│ └── values
│ └── strings.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ###PtrSwipeMenuRecyclerView
2 | ---
3 | ######本项目对RecyclerView进行再次封装,集合了最常用的下拉刷新、上拉加载更多和侧滑菜单功能。
4 | ######另,项目中也利用ItemTouchHelper简单实现了对item的拖拽操作,长按可对item进行拖拽移动
5 |
6 | >项目在RecyclerView的基础上实现了侧滑菜单、下拉刷新、滚动到底部自动加载更多的功能,支持自定义下拉刷新和加载更多的头部HeaderView、底
7 | 部FooterView,侧滑菜单可根据自身业务给不同的item设置不同的侧滑菜单,或作根据item的具体数据情况决定是否设置侧滑菜
8 | 单。 相关效果图等详见下面的说明。
9 |
10 | **1.效果图片**
11 |
12 | 侧滑菜单效果:
13 |
14 | 
15 |
16 | 下拉刷新效果:
17 |
18 | 
19 |
20 | 上拉加载效果:
21 |
22 | 
23 |
24 | 拖拽效果:
25 |
26 | 
27 |
28 |
29 |
30 | **2.使用说明**
31 | > 在所需引用此库的module的build.gradle文件中添加:
32 | ```
33 | dependencies {
34 | compile 'com.library.widget:library:1.0.1'
35 | }
36 | ```
37 |
38 | 注:项目功能为初步实现,仍在改进中。欢迎提出意见,共同讨论共同进步!
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.0"
6 | defaultConfig {
7 | applicationId "com.cc.library.view"
8 | minSdkVersion 15
9 | targetSdkVersion 25
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:25.0.0'
28 | testCompile 'junit:junit:4.12'
29 | compile project(':library')
30 | }
31 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\czyWorkSpace\envirment\android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cc/library/view/DragSwipeDemoAct.java:
--------------------------------------------------------------------------------
1 | package com.cc.library.view;
2 |
3 | import android.os.AsyncTask;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.support.v7.widget.helper.ItemTouchHelper;
9 | import android.view.View;
10 |
11 | import com.cc.library.view.adapter.DragSwipeAdapter;
12 | import com.cc.library.view.util.ToastUtil;
13 | import com.library.widget.baseview.PtrSwipeMenuRecyclerView;
14 | import com.library.widget.interfaces.OnMenuClickListener;
15 |
16 | import java.util.ArrayList;
17 | import java.util.Collections;
18 | import java.util.List;
19 |
20 | /**
21 | * Created by zhangyu on 2016/11/17.
22 | */
23 |
24 | public class DragSwipeDemoAct extends AppCompatActivity {
25 | private PtrSwipeMenuRecyclerView recyclerView;
26 | private DragSwipeAdapter adapter;
27 | public List datas;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | setContentView(R.layout.activity_swipe_demo);
33 | initResources();
34 | initView();
35 | }
36 |
37 | @Override
38 | public boolean onSupportNavigateUp() {
39 | finish();
40 | return super.onSupportNavigateUp();
41 | }
42 |
43 | private void initResources() {
44 | adapter = new DragSwipeAdapter();
45 | datas = new ArrayList<>();
46 | for (int i = 0; i < 17; i++) {
47 | datas.add(i);
48 | }
49 | adapter.setDatas(datas);
50 | }
51 |
52 | private void initView() {
53 | initActionBar();
54 |
55 | recyclerView = (PtrSwipeMenuRecyclerView) findViewById(R.id.recycler_view);
56 | //参数:context,横向或纵向滑动,是否颠倒显示数据
57 | recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
58 | recyclerView.setAdapter(adapter);
59 | // //设置不允许上拉加载更多
60 | // recyclerView.setPullLoadMoreEnable(false);
61 | // //设置不允许下拉刷新
62 | recyclerView.setPullToRefreshEnable(false);
63 | //添加菜单点击监听事件
64 | recyclerView.setOnMenuClickListener(onMenuClickListener);
65 | recyclerView.setOnPullListener(onPullListener);
66 | itemTouchHelper.attachToRecyclerView(recyclerView);
67 |
68 | }
69 |
70 | private void initActionBar() {
71 | android.support.v7.app.ActionBar actionBar = getSupportActionBar();
72 | actionBar.setDisplayHomeAsUpEnabled(true);
73 | }
74 |
75 |
76 | /**
77 | * 菜单点击事件监听
78 | */
79 | private OnMenuClickListener onMenuClickListener = new OnMenuClickListener() {
80 | @Override
81 | public void onMenuClick(View view, int position) {
82 | if (view.getId() == R.id.menu1)
83 | ToastUtil.showToast("position:" + position + ",menu1", 0);
84 | if (view.getId() == R.id.menu2)
85 | ToastUtil.showToast("position:" + position + ",menu2", 0);
86 | }
87 | };
88 |
89 | /**
90 | * 上拉加载、下拉刷新监听
91 | */
92 | private PtrSwipeMenuRecyclerView.OnPullListener onPullListener = new PtrSwipeMenuRecyclerView.OnPullListener() {
93 | @Override
94 | public void onRefresh() {
95 | new GetDataTask().execute();
96 | }
97 |
98 | @Override
99 | public void onLoadMore() {
100 | new LoadMoreTask().execute();
101 | }
102 | };
103 |
104 |
105 | /**
106 | * 使用itemTouchHelper完成拖拽效果
107 | * 由于拖拽与下拉刷新有冲突,所以暂时不建议放在一起使用
108 | */
109 | private ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
110 | @Override
111 | public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
112 | final int dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
113 | return makeMovementFlags(dragFlag, 0);
114 | }
115 |
116 | @Override
117 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
118 |
119 | int from = viewHolder.getAdapterPosition();
120 | int to = target.getAdapterPosition();
121 | if (to < datas.size() - 1) {//把Footerview的位置减掉(ViewType不一样)
122 | Collections.swap(datas, from, to);
123 | adapter.notifyItemMoved(from, to);
124 | return true;
125 | }
126 | return false;
127 | }
128 |
129 | @Override
130 | public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
131 |
132 | }
133 |
134 | @Override
135 | public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
136 | DragSwipeAdapter.ViewHolder holder = (DragSwipeAdapter.ViewHolder) viewHolder;
137 | //被选中时,改背景色
138 | if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
139 | holder.tv.setBackgroundColor(getResources().getColor(R.color.deepskyblue_pressed));
140 | }
141 | super.onSelectedChanged(viewHolder, actionState);
142 | }
143 |
144 | @Override
145 | public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
146 | super.clearView(recyclerView, viewHolder);
147 | DragSwipeAdapter.ViewHolder holder = (DragSwipeAdapter.ViewHolder) viewHolder;
148 | holder.tv.setBackgroundColor(getResources().getColor(R.color.deepskyblue));
149 | }
150 | });
151 |
152 | private class GetDataTask extends AsyncTask {
153 |
154 | @Override
155 | protected String[] doInBackground(Void... params) {
156 | try {
157 | Thread.sleep(3000);
158 | } catch (InterruptedException e) {
159 | }
160 | return null;
161 | }
162 |
163 | @Override
164 | protected void onPostExecute(String[] result) {
165 | recyclerView.onRefreshComplete();
166 | super.onPostExecute(result);
167 | }
168 | }
169 |
170 | private class LoadMoreTask extends AsyncTask {
171 |
172 | @Override
173 | protected String[] doInBackground(Void... params) {
174 | try {
175 | Thread.sleep(2000);
176 | } catch (InterruptedException e) {
177 | }
178 | return null;
179 | }
180 |
181 | @Override
182 | protected void onPostExecute(String[] result) {
183 | int count = datas.size();
184 | for (int i = count; i < count + 10; i++) {
185 | datas.add(i);
186 | }
187 | adapter.setDatas(datas);
188 | adapter.notifyDataSetChanged();
189 | recyclerView.onLoadMoreComplete();
190 | super.onPostExecute(result);
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cc/library/view/MenuAct.java:
--------------------------------------------------------------------------------
1 | package com.cc.library.view;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.support.v7.widget.RecyclerView;
9 |
10 | import com.cc.library.view.adapter.CustomAdapter;
11 |
12 |
13 | /**
14 | * Created by zhangyu on 2016/10/31.
15 | */
16 |
17 | public class MenuAct extends AppCompatActivity {
18 | private static final String TAG = "RVDMenu";
19 | private RecyclerView recyclerView;
20 | private CustomAdapter adapter = new CustomAdapter();
21 | private String[] data = {"下拉刷新、自动加载及侧滑菜单Demo", "长按拖拽Item及侧滑菜单Demo"};
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.activity_menu);
27 | initView();
28 | initResources();
29 | }
30 |
31 | private void initView() {
32 | recyclerView = (RecyclerView) findViewById(R.id.arvdm_recycler_view);
33 | //参数:context,横向或纵向滑动,是否颠倒显示数据
34 | recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
35 | recyclerView.setAdapter(adapter);
36 | }
37 |
38 | /**
39 | * 点击事件监听
40 | */
41 | private CustomAdapter.RecyclerViewOnClickListener recyclerViewOnClick = new CustomAdapter.RecyclerViewOnClickListener() {
42 | @Override
43 | public void onItemClick(int position) {
44 | switch (position){
45 | case 0:
46 | startActivity(new Intent(MenuAct.this,PtrSwipeDemoAct.class));
47 | break;
48 | case 1:
49 | startActivity(new Intent(MenuAct.this,DragSwipeDemoAct.class));
50 | break;
51 | }
52 | }
53 | };
54 |
55 | private void initResources() {
56 | adapter.setData(data);
57 | adapter.setRecyclerViewOnClick(recyclerViewOnClick);
58 | adapter.notifyDataSetChanged();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cc/library/view/PtrSwipeDemoAct.java:
--------------------------------------------------------------------------------
1 | package com.cc.library.view;
2 |
3 | import android.os.AsyncTask;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.view.Menu;
8 | import android.view.MenuInflater;
9 | import android.view.MenuItem;
10 | import android.view.View;
11 |
12 | import com.cc.library.view.adapter.SwipeAdapter;
13 | import com.cc.library.view.util.ToastUtil;
14 | import com.cc.library.view.widget.MFooterView;
15 | import com.cc.library.view.widget.MHeaderView;
16 | import com.library.widget.baseview.FooterView;
17 | import com.library.widget.baseview.HeaderView;
18 | import com.library.widget.baseview.PtrSwipeMenuRecyclerView;
19 | import com.library.widget.interfaces.OnMenuClickListener;
20 |
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | public class PtrSwipeDemoAct extends AppCompatActivity {
25 | private PtrSwipeMenuRecyclerView recyclerView;
26 | private SwipeAdapter swipeAdapter;
27 | public List datas;
28 | private MHeaderView mHeaderView;
29 | private MFooterView mFooterView;
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.activity_swipe_demo);
35 | initResources();
36 | initView();
37 |
38 | }
39 |
40 | @Override
41 | public boolean onSupportNavigateUp() {
42 | finish();
43 | return super.onSupportNavigateUp();
44 | }
45 |
46 | private void initResources() {
47 | mHeaderView = new MHeaderView(this);
48 | mFooterView = new MFooterView(this);
49 | initAdapter(null, null);
50 | }
51 |
52 | private void initAdapter(HeaderView headerView, FooterView footerView) {
53 | swipeAdapter = new SwipeAdapter();
54 |
55 | //不设置Header和Footer则会使用默认的布局
56 | if (headerView != null)
57 | swipeAdapter.setHeaderView(headerView);
58 | if (footerView != null)
59 | swipeAdapter.setFooterView(footerView);
60 | datas = new ArrayList<>();
61 | for (int i = 0; i < 17; i++) {
62 | datas.add(i);
63 | }
64 | swipeAdapter.setDatas(datas);
65 | }
66 |
67 | private void initView() {
68 | initActionBar();
69 |
70 | recyclerView = (PtrSwipeMenuRecyclerView) findViewById(R.id.recycler_view);
71 | //参数:context,横向或纵向滑动,是否颠倒显示数据
72 | recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
73 | recyclerView.setAdapter(swipeAdapter);
74 | // //设置不允许上拉加载更多
75 | // recyclerView.setPullLoadMoreEnable(false);
76 | // //设置不允许下拉刷新
77 | // recyclerView.setPullToRefreshEnable(false);
78 | //添加菜单点击监听事件
79 | recyclerView.setOnMenuClickListener(onMenuClickListener);
80 | recyclerView.setOnPullListener(onPullListener);
81 |
82 | }
83 |
84 | private void initActionBar() {
85 | android.support.v7.app.ActionBar actionBar = getSupportActionBar();
86 | actionBar.setDisplayHomeAsUpEnabled(true);
87 | }
88 |
89 |
90 | /**
91 | * 菜单点击事件监听
92 | */
93 | private OnMenuClickListener onMenuClickListener = new OnMenuClickListener() {
94 | @Override
95 | public void onMenuClick(View view, int position) {
96 | if (view.getId() == R.id.menu1)
97 | ToastUtil.showToast("position:" + position + ",menu1", 0);
98 | if (view.getId() == R.id.menu2)
99 | ToastUtil.showToast("position:" + position + ",menu2", 0);
100 | }
101 | };
102 |
103 | /**
104 | * 上拉加载、下拉刷新监听
105 | */
106 | private PtrSwipeMenuRecyclerView.OnPullListener onPullListener = new PtrSwipeMenuRecyclerView.OnPullListener() {
107 | @Override
108 | public void onRefresh() {
109 | new GetDataTask().execute();
110 | }
111 |
112 | @Override
113 | public void onLoadMore() {
114 | new LoadMoreTask().execute();
115 | }
116 | };
117 |
118 | private class GetDataTask extends AsyncTask {
119 |
120 | @Override
121 | protected String[] doInBackground(Void... params) {
122 | try {
123 | Thread.sleep(3000);
124 | } catch (InterruptedException e) {
125 | }
126 | return null;
127 | }
128 |
129 | @Override
130 | protected void onPostExecute(String[] result) {
131 | recyclerView.onRefreshComplete();
132 | super.onPostExecute(result);
133 | }
134 | }
135 |
136 | private class LoadMoreTask extends AsyncTask {
137 |
138 | @Override
139 | protected String[] doInBackground(Void... params) {
140 | try {
141 | Thread.sleep(2000);
142 | } catch (InterruptedException e) {
143 | }
144 | return null;
145 | }
146 |
147 | @Override
148 | protected void onPostExecute(String[] result) {
149 | int count = datas.size();
150 | for (int i = count; i < count + 10; i++) {
151 | datas.add(i);
152 | }
153 | swipeAdapter.setDatas(datas);
154 | swipeAdapter.notifyDataSetChanged();
155 | recyclerView.onLoadMoreComplete();
156 | super.onPostExecute(result);
157 | }
158 | }
159 |
160 | @Override
161 | public boolean onCreateOptionsMenu(Menu menu) {
162 | MenuInflater menuInflater = getMenuInflater();
163 | menuInflater.inflate(R.menu.prt_act_menu, menu);
164 | return super.onCreateOptionsMenu(menu);
165 | }
166 |
167 | @Override
168 | public boolean onOptionsItemSelected(MenuItem item) {
169 | int itemId = item.getItemId();
170 | switch (itemId) {
171 | case R.id.change_header_footer:
172 | initAdapter(mHeaderView, mFooterView);//重置adapter,改变header和footer
173 | recyclerView.setAdapter(swipeAdapter);
174 | ToastUtil.showToast("Header&Footer已改变", 0);
175 | break;
176 | }
177 | return super.onOptionsItemSelected(item);
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cc/library/view/adapter/CustomAdapter.java:
--------------------------------------------------------------------------------
1 | package com.cc.library.view.adapter;
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 com.cc.library.view.R;
9 |
10 |
11 | /**
12 | * RecyclerView使用的Adapter
13 | * 1.需要手动实现ViewHolder类(这里写成内部类)
14 | * 2.实现RecyclerView.Adapter的相关抽象方法:onCreateViewHolder()、onBindViewHolder()、getItemCount()
15 | * 3.额外实现了点击事件的监听
16 | * Created by zhangyu on 2016/10/31.
17 | */
18 |
19 | public class CustomAdapter extends RecyclerView.Adapter {
20 |
21 | private RecyclerViewOnClickListener recyclerViewOnClick;
22 | private String[] data;
23 |
24 | //RecyclerView需要手动实现一个ViewHolder ,跟ListView写ViewHolder差不多样
25 | protected class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
26 |
27 | public TextView tv;
28 | public View itemView;
29 |
30 | public ViewHolder(View itemView) {
31 | super(itemView);
32 | this.itemView = itemView;
33 | tv = (TextView) itemView.findViewById(R.id.ard_tv);
34 |
35 | //设置点击监听
36 | this.itemView.setOnClickListener(this);
37 | }
38 |
39 | @Override
40 | public void onClick(View v) {
41 | //用getAdapterPosition获取item在adapter中的位置
42 | if (v.getId() == itemView.getId()) {
43 | if (null != recyclerViewOnClick)
44 | recyclerViewOnClick.onItemClick(getAdapterPosition());
45 | }
46 | }
47 | }
48 |
49 | //设置ViewHolder 把需要绑定的布局传进去
50 | @Override
51 | public CustomAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
52 | View itemView = View.inflate(parent.getContext(), R.layout.adapter_recyclerview_menu_demo, null);
53 | return new ViewHolder(itemView);
54 | }
55 |
56 | //onBind 给布局设置数据
57 | // 同时添加点击事件监听(RecyclerView 默认未设置点击监听)
58 | @Override
59 | public void onBindViewHolder(ViewHolder holder, int position) {
60 |
61 | if (data != null && position < data.length)
62 | holder.tv.setText(data[position]);
63 |
64 | }
65 |
66 |
67 | //item数目
68 | @Override
69 | public int getItemCount() {
70 | return data == null ? 0 : data.length;
71 | }
72 |
73 | public void setData(String[] data) {
74 | this.data = data;
75 | }
76 |
77 | public void setRecyclerViewOnClick(RecyclerViewOnClickListener recyclerViewOnClick) {
78 | this.recyclerViewOnClick = recyclerViewOnClick;
79 | }
80 |
81 | @Override
82 | public int getItemViewType(int position) {
83 | return super.getItemViewType(position);
84 | }
85 |
86 | /**
87 | * 监听回调接口
88 | */
89 | public interface RecyclerViewOnClickListener {
90 | void onItemClick(int position);
91 |
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cc/library/view/adapter/DragSwipeAdapter.java:
--------------------------------------------------------------------------------
1 | package com.cc.library.view.adapter;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.LinearLayout;
8 | import android.widget.TextView;
9 |
10 | import com.cc.library.view.R;
11 | import com.library.widget.baseview.PtrSwipeMenuRecyclerView;
12 | import com.library.widget.adapter.SwipeMenuAdapter;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * Created by zhangyu on 2016/11/9.
18 | */
19 | public class DragSwipeAdapter extends SwipeMenuAdapter {
20 | public List datas;
21 |
22 | public class ViewHolder extends PtrSwipeMenuRecyclerView.ViewHolder {
23 | public TextView tv;
24 |
25 | public ViewHolder(View itemView) {
26 | super(itemView);
27 | tv = (TextView) itemView.findViewById(R.id.content_tv);
28 | }
29 | }
30 |
31 | @Override
32 | protected View createContentView(ViewGroup parent, int viewType) {
33 | View contentView = LayoutInflater.from(parent.getContext()).inflate(R.layout.content_view_for_test, parent, false);
34 | return contentView;
35 | }
36 |
37 | @Override
38 | protected LinearLayout createMenuView(ViewGroup parent, int viewType) {
39 | LinearLayout menuView = (LinearLayout) LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_for_test, parent, false);
40 |
41 | return menuView;
42 | }
43 |
44 | @Override
45 | public RecyclerView.ViewHolder onCreateThisViewHolder(ViewGroup contentView, int viewType) {
46 | return new ViewHolder(contentView);
47 | }
48 |
49 | @Override
50 | public void onBindThisViewHolder(ViewHolder holder, int position) {
51 | if (null != datas && position < datas.size()){
52 | holder.tv.setText("content" + datas.get(position));
53 | }
54 |
55 | }
56 |
57 | @Override
58 | public int getThisItemCount() {
59 | return datas == null ? 0 : datas.size();
60 | }
61 |
62 | public void setDatas(List datas) {
63 | this.datas = datas;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cc/library/view/adapter/SwipeAdapter.java:
--------------------------------------------------------------------------------
1 | package com.cc.library.view.adapter;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.LinearLayout;
8 | import android.widget.TextView;
9 |
10 | import com.cc.library.view.R;
11 | import com.library.widget.baseview.PtrSwipeMenuRecyclerView;
12 | import com.library.widget.adapter.SwipeMenuAdapter;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * Created by zhangyu on 2016/11/9.
18 | */
19 | public class SwipeAdapter extends SwipeMenuAdapter {
20 | public List datas;
21 | public final int ONE_MENU = 0X1132, NO_MENU = 0X1133, TWO_MENU = 0X1134;
22 |
23 | public class ViewHolder extends PtrSwipeMenuRecyclerView.ViewHolder {
24 | public TextView tv;
25 |
26 | public ViewHolder(View itemView) {
27 | super(itemView);
28 | tv = (TextView) itemView.findViewById(R.id.content_tv);
29 | }
30 | }
31 |
32 | @Override
33 | protected View createContentView(ViewGroup parent, int viewType) {
34 | View contentView = LayoutInflater.from(parent.getContext()).inflate(R.layout.content_view_for_test, parent, false);
35 | return contentView;
36 | }
37 |
38 | @Override
39 | protected LinearLayout createMenuView(ViewGroup parent, int viewType) {
40 | LinearLayout menuView = (LinearLayout) LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_for_test, parent, false);
41 | LinearLayout menuView1 = (LinearLayout) LayoutInflater.from(parent.getContext()).inflate(R.layout.menu1_for_test, parent, false);
42 |
43 | if (viewType == ONE_MENU)
44 | return menuView1;
45 | else if (viewType == TWO_MENU)
46 | return menuView;
47 | else
48 | return null;
49 | }
50 |
51 | @Override
52 | public RecyclerView.ViewHolder onCreateThisViewHolder(ViewGroup contentView, int viewType) {
53 | return new ViewHolder(contentView);
54 | }
55 |
56 | @Override
57 | public void onBindThisViewHolder(ViewHolder holder, int position) {
58 | if (null != datas && position < datas.size()){
59 | if (position % 3 == 0) {
60 | holder.tv.setText("我没有菜单 position:" + datas.get(position));
61 | } else if (position % 3 == 1) {
62 | holder.tv.setText("我有一个菜单 position:" + datas.get(position));
63 | } else
64 | holder.tv.setText("我有两个菜单 position:" + datas.get(position));
65 | }
66 |
67 | }
68 |
69 | @Override
70 | public int getThisItemViewType(int position) {
71 | if (position % 3 == 0) {
72 | return NO_MENU;
73 | } else if (position % 3 == 1) {
74 | return ONE_MENU;
75 | } else
76 | return TWO_MENU;
77 | }
78 |
79 |
80 | @Override
81 | public int getThisItemCount() {
82 | return datas == null ? 0 : datas.size();
83 | }
84 |
85 | public void setDatas(List datas) {
86 | this.datas = datas;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cc/library/view/base/BaseApplication.java:
--------------------------------------------------------------------------------
1 | package com.cc.library.view.base;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | /**
7 | * Created by zhangyu on 2016-07-11 18:49.
8 | */
9 | public class BaseApplication extends Application {
10 | public static Context context;
11 | @Override
12 | public void onCreate() {
13 | super.onCreate();
14 | context = getApplicationContext();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cc/library/view/util/DisplayUtils.java:
--------------------------------------------------------------------------------
1 | package com.cc.library.view.util;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.content.res.Resources;
7 | import android.graphics.Rect;
8 | import android.os.Build;
9 | import android.util.DisplayMetrics;
10 | import android.util.Log;
11 | import android.view.Display;
12 | import android.view.Window;
13 | import android.view.WindowManager;
14 |
15 | import com.cc.library.view.base.BaseApplication;
16 |
17 | import java.lang.reflect.Field;
18 | import java.lang.reflect.Method;
19 |
20 | /**
21 | * @author
22 | * @创建时间:2016-3-9下午5:06:06
23 | * @描述: dip/dp、sp 转换为 px 的工具类
24 | */
25 | public class DisplayUtils {
26 |
27 | /**
28 | * 将px值转换为dip或dp值,保证尺寸大小不变
29 | *
30 | * @param pxValue
31 | * @return
32 | */
33 | public static int px2dip(Context context, float pxValue) {
34 | final float scale = context.getResources().getDisplayMetrics().density;
35 | return (int) (pxValue / scale + 0.5f);
36 | }
37 |
38 | /**
39 | * 将dip或dp值转换为px值,保证尺寸大小不变
40 | *
41 | * @param dipValue
42 | * @return
43 | */
44 | public static int dip2px(Context context, float dipValue) {
45 | final float scale = context.getResources().getDisplayMetrics().density;
46 | return (int) (dipValue * scale + 0.5f);
47 | }
48 |
49 | /**
50 | * 将px值转换为sp值,保证文字大小不变
51 | *
52 | * @param pxValue
53 | * @return
54 | */
55 | public static int px2sp(Context context, float pxValue) {
56 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
57 | return (int) (pxValue / fontScale + 0.5f);
58 | }
59 |
60 | /**
61 | * 将sp值转换为px值,保证文字大小不变
62 | *
63 | * @param spValue
64 | * @return
65 | */
66 | public static int sp2px(Context context, float spValue) {
67 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
68 | return (int) (spValue * fontScale + 0.5f);
69 | }
70 |
71 | // 设置屏幕透明度
72 | public static void setBackgroundAlpha(Activity context, float bgAlpha) {
73 | WindowManager.LayoutParams lp = context.getWindow().getAttributes();
74 | lp.alpha = bgAlpha; // 0.0~1.0
75 | context.getWindow().setAttributes(lp);
76 | }
77 |
78 | /**
79 | * 获得设备的屏幕宽
80 | */
81 | public static int getWidth() {
82 | Context context = BaseApplication.context;
83 | if (null == context) {
84 | return 0;
85 | }
86 | DisplayMetrics dm = null;
87 | try {
88 | if (context != null) {
89 | dm = new DisplayMetrics();
90 | dm = context.getApplicationContext().getResources()
91 | .getDisplayMetrics();
92 | }
93 |
94 | return dm.widthPixels;
95 | } catch (Exception e) {
96 | e.printStackTrace();
97 | }
98 | return 0;
99 | }
100 |
101 | /**
102 | * 获得设备的屏幕高
103 | */
104 | public static int getHeight() {
105 | Context context = BaseApplication.context;
106 | if (null == context) {
107 | return 0;
108 | }
109 | DisplayMetrics dm = null;
110 | try {
111 | if (context != null) {
112 | dm = new DisplayMetrics();
113 | dm = context.getApplicationContext().getResources()
114 | .getDisplayMetrics();
115 | }
116 |
117 | return dm.heightPixels;
118 |
119 | } catch (Exception e) {
120 | e.printStackTrace();
121 | }
122 | return 0;
123 | }
124 |
125 | /**
126 | * 获取状态栏高度
127 | *
128 | * @param activity
129 | * @return
130 | */
131 | public static int getStatusHeight(Activity activity) {
132 | int statusHeight = 0;
133 | Rect localRect = new Rect();
134 | activity.getWindow().getDecorView()
135 | .getWindowVisibleDisplayFrame(localRect);
136 | statusHeight = localRect.top;
137 | if (0 == statusHeight) {
138 | Class> localClass;
139 | try {
140 | localClass = Class.forName("com.android.internal.R$dimen");
141 | Object localObject = localClass.newInstance();
142 | int i5 = Integer.parseInt(localClass
143 | .getField("status_bar_height").get(localObject)
144 | .toString());
145 | statusHeight = activity.getResources()
146 | .getDimensionPixelSize(i5);
147 | } catch (Exception e) {
148 | e.printStackTrace();
149 | }
150 | }
151 | return statusHeight;
152 | }
153 |
154 | /**
155 | * 状态栏在darkMode下,字体为黑色,小米手机MIUI6以上有效
156 | *
157 | * @param darkmode
158 | * @param activity
159 | */
160 | public static void setStatusBarDarkMode(boolean darkmode, Activity activity) {
161 | Class extends Window> clazz = activity.getWindow().getClass();
162 | try {
163 | int darkModeFlag = 0;
164 | Class> layoutParams = Class
165 | .forName("android.view.MiuiWindowManager$LayoutParams");
166 | Field field = layoutParams
167 | .getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
168 | darkModeFlag = field.getInt(layoutParams);
169 | Method extraFlagField = clazz.getMethod("setExtraFlags", int.class,
170 | int.class);
171 | extraFlagField.invoke(activity.getWindow(), darkmode ? darkModeFlag
172 | : 0, darkModeFlag);
173 | } catch (Exception e) {
174 | e.printStackTrace();
175 | }
176 | }
177 |
178 | /**
179 | * 设置状态栏在darkMode下,字体的颜色,争对魅族手机
180 | */
181 | public static boolean setStatusBarDarkIcon(Window window, boolean dark) {
182 | boolean result = false;
183 | if (window != null) {
184 | try {
185 | WindowManager.LayoutParams lp = window.getAttributes();
186 | Field darkFlag = WindowManager.LayoutParams.class
187 | .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
188 | Field meizuFlags = WindowManager.LayoutParams.class
189 | .getDeclaredField("meizuFlags");
190 | darkFlag.setAccessible(true);
191 | meizuFlags.setAccessible(true);
192 | int bit = darkFlag.getInt(null);
193 | int value = meizuFlags.getInt(lp);
194 | if (dark) {
195 | value |= bit;
196 | } else {
197 | value &= ~bit;
198 | }
199 | meizuFlags.setInt(lp, value);
200 | window.setAttributes(lp);
201 | result = true;
202 | } catch (Exception e) {
203 | Log.e("MeiZu", "setStatusBarDarkIcon: failed");
204 | }
205 | }
206 | return result;
207 | }
208 |
209 | /**
210 | * 判断底部navigator是否已经显示
211 | */
212 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
213 | public static boolean hasSoftKeys(Activity context) {
214 | Display d = context.getWindowManager().getDefaultDisplay();
215 |
216 | DisplayMetrics realDisplayMetrics = new DisplayMetrics();
217 | d.getRealMetrics(realDisplayMetrics);
218 |
219 | int realHeight = realDisplayMetrics.heightPixels;
220 | int realWidth = realDisplayMetrics.widthPixels;
221 |
222 | DisplayMetrics displayMetrics = new DisplayMetrics();
223 | d.getMetrics(displayMetrics);
224 |
225 | int displayHeight = displayMetrics.heightPixels;
226 | int displayWidth = displayMetrics.widthPixels;
227 |
228 | return (realWidth - displayWidth) > 0
229 | || (realHeight - displayHeight) > 0;
230 | }
231 |
232 | /**
233 | * 检查设备是否有虚拟导航栏
234 | *
235 | * @param context
236 | * @return
237 | */
238 | public static boolean checkDeviceHasNavigationBar(Context context) {
239 | boolean hasNavigationBar = false;
240 | Resources rs = context.getResources();
241 | int id = rs
242 | .getIdentifier("config_showNavigationBar", "bool", "android");
243 | if (id > 0) {
244 | hasNavigationBar = rs.getBoolean(id);
245 | }
246 | try {
247 | Class systemPropertiesClass = Class
248 | .forName("android.os.SystemProperties");
249 | Method m = systemPropertiesClass.getMethod("get", String.class);
250 | String navBarOverride = (String) m.invoke(systemPropertiesClass,
251 | "qemu.hw.mainkeys");
252 | if ("1".equals(navBarOverride)) {
253 | hasNavigationBar = false;
254 | } else if ("0".equals(navBarOverride)) {
255 | hasNavigationBar = true;
256 | }
257 | } catch (Exception e) {
258 | Log.e(DisplayUtils.class.getSimpleName(), e.toString());
259 | }
260 |
261 | return hasNavigationBar;
262 |
263 | }
264 |
265 | /**
266 | * 获取NavigationBar的高度
267 | *
268 | * @param context
269 | * @return
270 | */
271 | public static int getNavigationBarHeight(Context context) {
272 | int navigationBarHeight = 0;
273 | Resources rs = context.getResources();
274 | int id = rs.getIdentifier("navigation_bar_height", "dimen", "android");
275 | if (id > 0 && checkDeviceHasNavigationBar(context)) {
276 | navigationBarHeight = rs.getDimensionPixelSize(id);
277 | }
278 | return navigationBarHeight;
279 | }
280 |
281 | }
282 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cc/library/view/util/ToastUtil.java:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | */
4 | package com.cc.library.view.util;
5 |
6 | import android.content.Context;
7 | import android.widget.Toast;
8 |
9 | import com.cc.library.view.base.BaseApplication;
10 |
11 |
12 | public class ToastUtil {
13 |
14 | public static void show(Context context, String info) {
15 | Toast.makeText(context, info, Toast.LENGTH_LONG).show();
16 | }
17 |
18 | public static void show(Context context, int info) {
19 | Toast.makeText(context, info, Toast.LENGTH_LONG).show();
20 | }
21 |
22 | private static Toast toast;
23 | private static Context context = BaseApplication.context;
24 |
25 | /**
26 | * 显示吐司 后出现的吐司立即取代之前的
27 | *
28 | * @param msg 消息
29 | * @param time 时间 0为短时间 否则为长时间
30 | */
31 | public static void showToast(String msg, int time) {
32 | if (null != toast)
33 | toast.cancel();
34 |
35 | if (time == 0)
36 | toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
37 | else
38 | toast = Toast.makeText(context, msg, Toast.LENGTH_LONG);
39 |
40 | toast.show();
41 | }
42 |
43 | /**
44 | * 显示吐司 后出现的吐司立即取代之前的
45 | *
46 | * @param msg 消息字符资源
47 | * @param time 时间 0为短时间 否则为长时间
48 | */
49 | public static void showToast(int msg, int time) {
50 | if (null != toast)
51 | toast.cancel();
52 |
53 | if (time == 0)
54 | toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
55 | else
56 | toast = Toast.makeText(context, msg, Toast.LENGTH_LONG);
57 |
58 | toast.show();
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cc/library/view/widget/MFooterView.java:
--------------------------------------------------------------------------------
1 | package com.cc.library.view.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.ProgressBar;
6 | import android.widget.RelativeLayout;
7 | import android.widget.TextView;
8 |
9 | import com.cc.library.view.R;
10 | import com.library.widget.baseview.FooterView;
11 |
12 | /**
13 | * Created by zhangyu on 2016/11/18.
14 | */
15 |
16 | public class MFooterView extends FooterView {
17 |
18 | private TextView tv;
19 | private ProgressBar progressBar;
20 |
21 | public MFooterView(Context context) {
22 | super(context);
23 | }
24 |
25 | public MFooterView(Context context, AttributeSet attrs) {
26 | super(context, attrs);
27 | }
28 |
29 | public MFooterView(Context context, AttributeSet attrs, int defStyleAttr) {
30 | super(context, attrs, defStyleAttr);
31 | }
32 |
33 | @Override
34 | public int getLayoutView() {
35 | return R.layout.m_footer_view;
36 | }
37 |
38 | @Override
39 | public void initView() {
40 | tv = (TextView) findViewById(R.id.mfv_tv);
41 | progressBar = (ProgressBar) findViewById(R.id.mfv_progress_bar);
42 | }
43 |
44 | @Override
45 | public RelativeLayout getRootContainer() {
46 |
47 | return (RelativeLayout) findViewById(R.id.mfv_container);
48 | }
49 |
50 | @Override
51 | public void setLoadingViewState() {
52 | progressBar.setVisibility(VISIBLE);
53 | tv.setText("正在加载..\n(我是自定义FooterView)");
54 | }
55 |
56 | @Override
57 | public void setPullingViewState() {
58 | progressBar.setVisibility(INVISIBLE);
59 | tv.setText("上拉加载更多\n(我是自定义FooterView)");
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/cc/library/view/widget/MHeaderView.java:
--------------------------------------------------------------------------------
1 | package com.cc.library.view.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.ProgressBar;
6 | import android.widget.RelativeLayout;
7 | import android.widget.TextView;
8 |
9 | import com.cc.library.view.R;
10 | import com.library.widget.baseview.HeaderView;
11 |
12 | /**
13 | * Created by zhangyu on 2016/11/18.
14 | */
15 |
16 | public class MHeaderView extends HeaderView {
17 | private TextView tv;
18 | private ProgressBar progressBar;
19 | public MHeaderView(Context context) {
20 | super(context);
21 | }
22 |
23 | public MHeaderView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public MHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
28 | super(context, attrs, defStyleAttr);
29 | }
30 |
31 | @Override
32 | public void initView() {
33 | tv = (TextView) findViewById(R.id.mhv_tv);
34 | progressBar = (ProgressBar) findViewById(R.id.mhv_progress_bar);
35 | }
36 |
37 | @Override
38 | public RelativeLayout getRootContainer() {
39 |
40 | return (RelativeLayout) findViewById(R.id.mhv_container);
41 | }
42 |
43 | @Override
44 | public int getLayoutView() {
45 | return R.layout.m_header_view;
46 | }
47 |
48 | @Override
49 | public void setRefreshingViewState() {
50 | progressBar.setVisibility(VISIBLE);
51 | tv.setText("正在刷新..\n(我是自定义HeaderView)");
52 | }
53 |
54 | @Override
55 | public void setPullingViewState() {
56 | progressBar.setVisibility(INVISIBLE);
57 | tv.setText("下拉刷新\n(我是自定义HeaderView)");
58 | }
59 |
60 | @Override
61 | public void setReadyViewState() {
62 | progressBar.setVisibility(INVISIBLE);
63 | tv.setText("放开刷新\n(我是自定义HeaderView)");
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_swipe_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/adapter_recyclerview_menu_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
16 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_view_for_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/m_footer_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/m_header_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu1_for_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/menu_for_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/prt_act_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangyuChen1991/PtrSwipeMenuRecyclerView/1d246ddf5c102e6b0cb9555cda4a3c27f79cbce8/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangyuChen1991/PtrSwipeMenuRecyclerView/1d246ddf5c102e6b0cb9555cda4a3c27f79cbce8/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangyuChen1991/PtrSwipeMenuRecyclerView/1d246ddf5c102e6b0cb9555cda4a3c27f79cbce8/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangyuChen1991/PtrSwipeMenuRecyclerView/1d246ddf5c102e6b0cb9555cda4a3c27f79cbce8/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangyuChen1991/PtrSwipeMenuRecyclerView/1d246ddf5c102e6b0cb9555cda4a3c27f79cbce8/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #ffffff
8 |
9 | #0099CC
10 |
11 | #00000000
12 |
13 | #FFFFF0
14 |
15 | #FFFFE0
16 |
17 | #FFFF00
18 |
19 | #FFFAFA
20 |
21 | #FFFAF0
22 |
23 | #FFFACD
24 |
25 | #FFF8DC
26 |
27 | #FFF5EE
28 |
29 | #FFF0F5
30 |
31 | #FFEFD5
32 |
33 | #FFEBCD
34 |
35 | #FFE4E1
36 |
37 | #FFE4C4
38 |
39 | #FFE4B5
40 |
41 | #FFDEAD
42 |
43 | #FFDAB9
44 |
45 | #FFD700
46 |
47 | #FFC0CB
48 |
49 | #FFB6C1
50 |
51 | #FFA500
52 |
53 | #FFA07A
54 |
55 | #FF8C00
56 |
57 | #FF7F50
58 |
59 | #FF69B4
60 |
61 | #FF6347
62 |
63 | #FF4500
64 |
65 | #FF1493
66 |
67 | #FF00FF
68 |
69 | #FF00FF
70 |
71 | #FF0000
72 |
73 | #FDF5E6
74 |
75 | #FAFAD2
76 |
77 | #FAF0E6
78 |
79 | #FAEBD7
80 |
81 | #FA8072
82 |
83 | #F8F8FF
84 |
85 | #F5FFFA
86 |
87 | #F5F5F5
88 |
89 | #F5F5DC
90 |
91 | #F5DEB3
92 |
93 | #F4A460
94 |
95 | #F0FFFF
96 |
97 | #F0FFF0
98 |
99 | #F0F8FF
100 |
101 | #F0E68C
102 |
103 | #F08080
104 |
105 | #EEE8AA
106 |
107 | #EE82EE
108 |
109 | #E9967A
110 |
111 | #E6E6FA
112 |
113 | #E0FFFF
114 |
115 | #DEB887
116 |
117 | #DDA0DD
118 |
119 | #DCDCDC
120 |
121 | #DC143C
122 |
123 | #DB7093
124 |
125 | #DAA520
126 |
127 | #DA70D6
128 |
129 | #D8BFD8
130 |
131 | #D3D3D3
132 |
133 | #D3D3D3
134 |
135 | #D2B48C
136 |
137 | #D2691E
138 |
139 | #CD853F
140 |
141 | #CD5C5C
142 |
143 | #C71585
144 |
145 | #C0C0C0
146 |
147 | #BDB76B
148 |
149 | #BC8F8F
150 |
151 | #BA55D3
152 |
153 | #B8860B
154 |
155 | #B22222
156 |
157 | #B0E0E6
158 |
159 | #B0C4DE
160 |
161 | #AFEEEE
162 |
163 | #ADFF2F
164 |
165 | #ADD8E6
166 |
167 | #A9A9A9
168 |
169 | #A9A9A9
170 |
171 | #A52A2A
172 |
173 | #A0522D
174 |
175 | #9932CC
176 |
177 | #98FB98
178 |
179 | #9400D3
180 |
181 | #9370DB
182 |
183 | #90EE90
184 |
185 | #8FBC8F
186 |
187 | #8B4513
188 |
189 | #8B008B
190 |
191 | #8B0000
192 |
193 | #8A2BE2
194 |
195 | #87CEFA
196 |
197 | #87CEEB
198 |
199 | #808080
200 |
201 | #808080
202 |
203 | #808000
204 |
205 | #800080
206 |
207 | #800000
208 |
209 | #7FFFD4
210 |
211 | #7FFF00
212 |
213 | #7CFC00
214 |
215 | #7B68EE
216 |
217 | #778899
218 |
219 | #778899
220 |
221 | #708090
222 |
223 | #708090
224 |
225 | #6B8E23
226 |
227 | #6A5ACD
228 |
229 | #696969
230 |
231 | #696969
232 |
233 | #66CDAA
234 |
235 | #6495ED
236 |
237 | #5F9EA0
238 |
239 | #556B2F
240 |
241 | #4B0082
242 |
243 | #48D1CC
244 |
245 | #483D8B
246 |
247 | #4682B4
248 |
249 | #4169E1
250 |
251 | #40E0D0
252 |
253 | #3CB371
254 |
255 | #32CD32
256 |
257 | #2F4F4F
258 |
259 | #2F4F4F
260 |
261 | #2E8B57
262 |
263 | #228B22
264 |
265 | #20B2AA
266 |
267 | #1E90FF
268 |
269 | #191970
270 |
271 | #00FFFF
272 |
273 | #00FFFF
274 |
275 | #00FF7F
276 |
277 | #00FF00
278 |
279 | #00FA9A
280 |
281 | #00CED1
282 |
283 | #8700BFFF
284 | #00BFFF
285 |
286 | #008B8B
287 |
288 | #008080
289 |
290 | #008000
291 |
292 | #006400
293 |
294 | #0000FF
295 |
296 | #0000CD
297 |
298 | #00008B
299 |
300 | #000080
301 |
302 | #000000
303 |
304 |
305 | #c0ffbd21
306 | #b0000000
307 | #ffffffff
308 | #ffcc0000
309 | #60000000
310 |
311 | #99ffffff
312 | #00ffffff
313 |
314 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PtrSwipeMenuRecyclerView
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.2'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | 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 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangyuChen1991/PtrSwipeMenuRecyclerView/1d246ddf5c102e6b0cb9555cda4a3c27f79cbce8/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/images/loadmore.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangyuChen1991/PtrSwipeMenuRecyclerView/1d246ddf5c102e6b0cb9555cda4a3c27f79cbce8/images/loadmore.jpg
--------------------------------------------------------------------------------
/images/refreshing.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangyuChen1991/PtrSwipeMenuRecyclerView/1d246ddf5c102e6b0cb9555cda4a3c27f79cbce8/images/refreshing.jpg
--------------------------------------------------------------------------------
/images/swipemenu.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangyuChen1991/PtrSwipeMenuRecyclerView/1d246ddf5c102e6b0cb9555cda4a3c27f79cbce8/images/swipemenu.jpg
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.0"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | compile fileTree(include: ['*.jar'], dir: 'libs')
26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
27 | exclude group: 'com.android.support', module: 'support-annotations'
28 | })
29 | compile 'com.android.support:appcompat-v7:25.0.0'
30 | testCompile 'junit:junit:4.12'
31 | compile 'com.android.support:recyclerview-v7:25.0.0'
32 | }
33 |
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\czyWorkSpace\envirment\android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/library/src/main/java/com/library/widget/adapter/SwipeMenuAdapter.java:
--------------------------------------------------------------------------------
1 | package com.library.widget.adapter;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.util.Log;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.LinearLayout;
8 |
9 | import com.library.widget.defaultimple.DefaultFooterView;
10 | import com.library.widget.defaultimple.DefaultHeaderView;
11 | import com.library.widget.baseview.FooterView;
12 | import com.library.widget.baseview.HeaderView;
13 | import com.library.widget.baseview.PtrSwipeMenuRecyclerView;
14 | import com.library.widget.baseview.SwipeMenuLayout;
15 |
16 | /**
17 | * Created by zhangyu on 2016/11/9.
18 | */
19 | public abstract class SwipeMenuAdapter extends RecyclerView.Adapter {
20 | private static final String TAG = "SwipeMenuAdapter";
21 | private LinearLayout menuView;
22 | private View contentView;
23 |
24 |
25 | private HeaderView headerView;
26 |
27 | private FooterView footerView;
28 | public static final int HeaderType = 0x1099, FooterType = 0x1101;
29 | private HeaderFooterViewHolder headerViewHolder, footerViewHolder;
30 | private boolean footerViewEnable = true;
31 |
32 | public class HeaderFooterViewHolder extends RecyclerView.ViewHolder {
33 | public HeaderFooterViewHolder(View itemView) {
34 | super(itemView);
35 | }
36 | }
37 |
38 | @Override
39 | public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
40 | Log.d(TAG, "onCreateViewHolder.. viewType = " + viewType + " HT:" + HeaderType + " FT:" + FooterType);
41 | if (viewType == HeaderType) {
42 | headerView = headerView == null ? new DefaultHeaderView(parent.getContext()) : headerView;
43 | headerViewHolder = new HeaderFooterViewHolder(headerView);
44 | return headerViewHolder;
45 | }
46 | if (viewType == FooterType) {
47 | footerView = footerView == null ? new DefaultFooterView(parent.getContext()) : footerView;
48 | footerViewHolder = new HeaderFooterViewHolder(footerView);
49 | if(!footerViewEnable) { //不允许上拉加载更多,隐藏FooterView
50 | FooterView footerView = (FooterView) footerViewHolder.itemView;
51 | footerView.setNowState(FooterView.STATE.HIND);
52 | }
53 | return footerViewHolder;
54 | }
55 |
56 | menuView = createMenuView(parent, viewType);
57 | contentView = createContentView(parent, viewType);
58 | SwipeMenuLayout swipeMenuLayout = new SwipeMenuLayout(parent.getContext(), contentView, menuView);
59 | return onCreateThisViewHolder(swipeMenuLayout, viewType);
60 | }
61 |
62 | /**
63 | * 创建item内容的view布局
64 | *
65 | * @param parent
66 | * @param viewType
67 | * @return
68 | */
69 | protected abstract View createContentView(ViewGroup parent, int viewType);
70 |
71 | /**
72 | * 创建菜单view的布局(高度最好与contentView布局高度一致,否则可能引起布局混乱)
73 | * 如果不需要侧滑菜单,返回null
74 | *
75 | * @return
76 | */
77 | protected abstract LinearLayout createMenuView(ViewGroup parent, int viewType);
78 |
79 | /**
80 | * 创建ViewHolder
81 | *
82 | * @param contentView 已经在createContentView()中创建好,然后经过再次包裹了侧滑菜单布局的itemview
83 | * @param viewType
84 | * @return
85 | */
86 | public abstract RecyclerView.ViewHolder onCreateThisViewHolder(ViewGroup contentView, int viewType);
87 |
88 | @Override
89 | public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
90 | if (position != 0 && position != getItemCount() - 1)
91 | onBindThisViewHolder((V) holder, position - 1);
92 | }
93 |
94 | public abstract void onBindThisViewHolder(V holder, int position);
95 |
96 | @Override
97 | public final int getItemCount() {
98 | //添加Header和Footer的数目
99 | return getThisItemCount() + 2;
100 | }
101 |
102 | /**
103 | * 此方法执行RecyclerView.Adapter中getItemCount()的逻辑
104 | *
105 | * @return
106 | */
107 | public abstract int getThisItemCount();
108 |
109 | /**
110 | * 重写此方法时请注意保留父类方法的逻辑,否则导致header计数混乱,下拉刷新出错
111 | * 使用position时注意减1(减去header的位置)
112 | *
113 | * @param position
114 | * @return
115 | */
116 | @Override
117 | public final int getItemViewType(int position) {
118 | if (position == 0)
119 | return HeaderType;
120 | else if (position == getThisItemCount() + 1)
121 | return FooterType;
122 | else
123 | return getThisItemViewType(position - 1);//减1减去header位置
124 | }
125 |
126 | /**
127 | * 此方法替代原Adapter中的getItemViewType方法
128 | * 使用与
129 | * @return
130 | */
131 | public int getThisItemViewType(int position) {
132 | return 0;
133 | }
134 |
135 |
136 | /**
137 | * 获取HeaderView
138 | *
139 | * @return
140 | */
141 | public HeaderView getHeaderView() {
142 | return (HeaderView) headerViewHolder.itemView;
143 | }
144 |
145 | /**
146 | * 获取FooterView
147 | *
148 | * @return
149 | */
150 | public FooterView getFooterView() {
151 | if (null == footerViewHolder)
152 | return null;
153 | return (FooterView) footerViewHolder.itemView;
154 | }
155 |
156 | /**
157 | * 设置HeaderView
158 | *
159 | * @return
160 | */
161 | public void setHeaderView(HeaderView headerView) {
162 | this.headerView = headerView;
163 | notifyDataSetChanged();
164 | }
165 |
166 | /**
167 | * 设置FooterView
168 | *
169 | * @return
170 | */
171 | public void setFooterView(FooterView footerView) {
172 | this.footerView = footerView;
173 | }
174 |
175 | public void setFooterViewEnable(boolean enable) {
176 | this.footerViewEnable = enable;
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/library/src/main/java/com/library/widget/baseview/FooterView.java:
--------------------------------------------------------------------------------
1 | package com.library.widget.baseview;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.RelativeLayout;
7 |
8 | /**
9 | * Created by zhangyu on 2016/11/18.
10 | */
11 |
12 | public abstract class FooterView extends RelativeLayout{
13 | public RelativeLayout rootContainer;
14 | private STATE nowState;
15 | public enum STATE{
16 | LOADING,
17 | READY,
18 | HIND
19 | }
20 |
21 | public FooterView(Context context) {
22 | super(context);
23 | init(context,null);
24 | }
25 |
26 | public FooterView(Context context, AttributeSet attrs) {
27 | super(context, attrs);
28 | init(context,attrs);
29 | }
30 |
31 |
32 | public FooterView(Context context, AttributeSet attrs, int defStyleAttr) {
33 | super(context, attrs, defStyleAttr);
34 | init(context,attrs);
35 | }
36 |
37 |
38 | private void init(Context context, AttributeSet attrs) {
39 | View.inflate(context, getLayoutView(),this);
40 | rootContainer = getRootContainer();
41 | initView();
42 | }
43 |
44 | /**
45 | * 指定自定义的布局
46 | * @return
47 | */
48 | public abstract int getLayoutView();
49 |
50 | /**
51 | * 初始化布局
52 | */
53 | public abstract void initView();
54 |
55 | /**
56 | * 获取自定义布局的根布局容器
57 | * @return
58 | */
59 | public abstract RelativeLayout getRootContainer();
60 |
61 | /**
62 | * 设置正在加载时的view状态
63 | */
64 | public abstract void setLoadingViewState();
65 |
66 | /**
67 | * 设置上拉时的view状态
68 | */
69 | public abstract void setPullingViewState();
70 |
71 | public void setNowState(FooterView.STATE nowState) {
72 | this.nowState = nowState;
73 | refreshView();
74 | }
75 |
76 | private void refreshView() {
77 | switch (nowState){
78 | case LOADING:
79 | rootContainer.setVisibility(View.VISIBLE);
80 | setLoadingViewState();
81 | break;
82 | case READY:
83 | rootContainer.setVisibility(View.VISIBLE);
84 | setPullingViewState();
85 | break;
86 | case HIND:
87 | rootContainer.setVisibility(View.GONE);
88 | break;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/library/src/main/java/com/library/widget/baseview/HeaderView.java:
--------------------------------------------------------------------------------
1 | package com.library.widget.baseview;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.RelativeLayout;
7 |
8 | /**
9 | * Created by zhangyu on 2016/11/18.
10 | */
11 |
12 | public abstract class HeaderView extends RelativeLayout {
13 |
14 | public RelativeLayout rootContainer;
15 | protected STATE nowState;
16 | public enum STATE {
17 | PULLING,
18 | READY,
19 | REFRESHING,
20 | HIND
21 | }
22 |
23 | public HeaderView(Context context) {
24 | super(context);
25 | init(context,null);
26 | }
27 |
28 | public HeaderView(Context context, AttributeSet attrs) {
29 | super(context, attrs);
30 | init(context, attrs);
31 | }
32 |
33 |
34 | public HeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
35 | super(context, attrs, defStyleAttr);
36 | init(context,attrs);
37 | }
38 |
39 |
40 | private void init(Context context, AttributeSet attrs) {
41 | View.inflate(context, getLayoutView(), this);
42 | rootContainer = getRootContainer();
43 | initView();
44 | setNowState(HeaderView.STATE.HIND);
45 | }
46 |
47 |
48 | /**
49 | * 设置HeaderView的高度
50 | * @param height
51 | */
52 | public void setViewHeight(float height) {
53 | RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) rootContainer.getLayoutParams();
54 | params.height = (int) height;
55 | rootContainer.setLayoutParams(params);
56 | invalidate();
57 | }
58 |
59 | /**
60 | * 初始化布局
61 | */
62 | public abstract void initView();
63 |
64 | /**
65 | * 获取自定义布局的根布局容器
66 | * @return
67 | */
68 | public abstract RelativeLayout getRootContainer();
69 |
70 | /**
71 | * 指定自定义的布局
72 | *
73 | * @return
74 | */
75 | public abstract int getLayoutView();
76 |
77 | /**
78 | * 设置正在加载时的view状态
79 | */
80 | public abstract void setRefreshingViewState();
81 |
82 | /**
83 | * 设置下拉时的view状态
84 | */
85 | public abstract void setPullingViewState();
86 |
87 | /**
88 | * 设置准备好刷新时状态
89 | */
90 | public abstract void setReadyViewState();
91 |
92 | public void setNowState(HeaderView.STATE nowState) {
93 | this.nowState = nowState;
94 | refreshView();
95 | }
96 |
97 | private void refreshView() {
98 |
99 | switch (nowState) {
100 | case PULLING:
101 | rootContainer.setVisibility(View.VISIBLE);
102 | setPullingViewState();
103 | break;
104 | case READY:
105 | rootContainer.setVisibility(View.VISIBLE);
106 | setReadyViewState();
107 | break;
108 | case REFRESHING:
109 | rootContainer.setVisibility(View.VISIBLE);
110 | setRefreshingViewState();
111 | break;
112 | case HIND:
113 | rootContainer.setVisibility(View.GONE);
114 | break;
115 | }
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/library/src/main/java/com/library/widget/baseview/PtrSwipeMenuRecyclerView.java:
--------------------------------------------------------------------------------
1 | package com.library.widget.baseview;
2 |
3 | import android.animation.Animator;
4 | import android.animation.ObjectAnimator;
5 | import android.animation.ValueAnimator;
6 | import android.content.Context;
7 | import android.support.annotation.Nullable;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.util.AttributeSet;
10 | import android.util.Log;
11 | import android.view.MotionEvent;
12 | import android.view.View;
13 | import android.widget.LinearLayout;
14 |
15 | import com.library.widget.adapter.SwipeMenuAdapter;
16 | import com.library.widget.interfaces.OnMenuClickListener;
17 |
18 | import java.io.InvalidClassException;
19 |
20 | /**
21 | * Created by zhangyu on 2016/11/7.
22 | */
23 | public class PtrSwipeMenuRecyclerView extends RecyclerView {
24 | private static final String TAG = "PSMRecyclerView";
25 | private boolean isVerticalScroll = true;
26 | private Context context;
27 | private SwipeMenuAdapter adapter;
28 | //最新的touch Y坐标点
29 | private float touchCurrY;
30 | //是否正在刷新、是否正在加载更多
31 | private boolean refreshing = false, loading = false;
32 | //触发刷新的临界值
33 | private final float refreshHeight = 150f;
34 | //HeaderView最新的高度
35 | private int currHeaderHeight;
36 | //是否允许上拉加载更多
37 | private boolean pullLoadMoreEnable = true;
38 | //是否允许下拉刷新
39 | private boolean pullToRefreshEnable = true;
40 | private OnPullListener onPullListener;
41 |
42 | private static OnMenuClickListener onMenuClickListener;
43 |
44 | public PtrSwipeMenuRecyclerView(Context context) {
45 | super(context);
46 | init(context);
47 | }
48 |
49 | public PtrSwipeMenuRecyclerView(Context context, @Nullable AttributeSet attrs) {
50 | super(context, attrs);
51 | init(context);
52 | }
53 |
54 | public PtrSwipeMenuRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
55 | super(context, attrs, defStyle);
56 | init(context);
57 | }
58 |
59 | @Override
60 | public void setAdapter(Adapter adapter) {
61 | super.setAdapter(adapter);
62 | if (adapter instanceof SwipeMenuAdapter) {
63 | this.adapter = (SwipeMenuAdapter) adapter;
64 | } else {
65 | try {
66 | throw new InvalidClassException("所使用Adapter并非SwipeMenuAdapter的子类");
67 | } catch (InvalidClassException e) {
68 | Log.e(TAG, e.getMessage());
69 | e.printStackTrace();
70 | }
71 | }
72 | }
73 |
74 |
75 | private void init(Context context) {
76 | this.context = context;
77 |
78 |
79 | }
80 |
81 | /**
82 | * 监听滚动
83 | *
84 | * @param dx
85 | * @param dy
86 | */
87 | @Override
88 | public void onScrolled(int dx, int dy) {
89 | super.onScrolled(dx, dy);
90 | if (!canScrollVertically(1)) {//已经滑到底部
91 |
92 | if (loading || !pullLoadMoreEnable)
93 | super.onScrolled(dx, dy);
94 | else {
95 | startLoadMore();//自动加载
96 | }
97 | }
98 | }
99 |
100 | /**
101 | * 重写ViewHolder
102 | * 使用PtrSwipeMenuRecyclerView,必须配合PtrSwipeMenuRecyclerView.ViewHolder,否则将会出错
103 | */
104 | public static abstract class ViewHolder extends RecyclerView.ViewHolder {
105 | SwipeMenuLayout swipeMenuLayout;
106 | LinearLayout menuView;
107 |
108 | public ViewHolder(View itemView) {
109 | super(itemView);
110 | swipeMenuLayout = (SwipeMenuLayout) itemView;
111 | menuView = swipeMenuLayout.getMenuView();
112 | setOnMenuClickListener();
113 | }
114 |
115 | private void setOnMenuClickListener() {
116 | if (null != menuView)
117 | for (int i = 0; i < menuView.getChildCount(); i++) {
118 | menuView.getChildAt(i).setOnClickListener(onClickListener);
119 | }
120 |
121 | }
122 |
123 | private View.OnClickListener onClickListener = new OnClickListener() {
124 | @Override
125 | public void onClick(View view) {
126 | for (int i = 0; i < menuView.getChildCount(); i++) {
127 | if (view.getId() == menuView.getChildAt(i).getId()) {
128 | swipeMenuLayout.smoothCloseMenu();
129 | if (null != onMenuClickListener) {
130 | onMenuClickListener.onMenuClick(view, getAdapterPosition() - 1);//触发菜单点击事件,减去header的位置
131 | }
132 | }
133 | }
134 | }
135 | };
136 | }
137 |
138 | boolean thisTouchHadDeal = false;
139 | private int startX, nowTouchX, startY, nowTouchY,oldTouchPosition = -1;
140 | private SwipeMenuLayout newSwipeMenuLayout, oldSwipeMenuLayout;
141 |
142 | @Override
143 | public boolean onInterceptTouchEvent(MotionEvent ev) {
144 |
145 | switch (ev.getAction()) {
146 | case MotionEvent.ACTION_DOWN:
147 | isVerticalScroll = true;
148 | thisTouchHadDeal = false;
149 | startX = (int) ev.getX();
150 | startY = (int) ev.getY();
151 |
152 | int touchingPosition = getChildAdapterPosition(findChildViewUnder(startX, startY));
153 | if (touchingPosition != adapter.getItemCount() - 1 && touchingPosition != 0) {
154 | ViewHolder viewHolder = (ViewHolder) findViewHolderForAdapterPosition(touchingPosition);
155 |
156 | newSwipeMenuLayout = (SwipeMenuLayout) viewHolder.itemView;
157 | if (touchingPosition != oldTouchPosition && oldSwipeMenuLayout != null && oldSwipeMenuLayout.getScrollX() != 0)//上一次被弹出的菜单回滚回原位
158 | oldSwipeMenuLayout.smoothCloseMenu();
159 | oldSwipeMenuLayout = newSwipeMenuLayout;
160 | }
161 | oldTouchPosition = touchingPosition;
162 | break;
163 | case MotionEvent.ACTION_MOVE:
164 | if (!thisTouchHadDeal) {
165 | thisTouchHadDeal = true;
166 | nowTouchX = (int) ev.getX();
167 | nowTouchY = (int) ev.getY();
168 | isVerticalScroll = !isHorizontalScroll(startX, startY, nowTouchX, nowTouchY);
169 | }
170 | if (isVerticalScroll) { //如果是竖直方向滑动,拦截事件自己处理
171 | return true;
172 | } else { //横向滑动,不做响应事件,让子View处理事件
173 | return false;
174 | }
175 | case MotionEvent.ACTION_UP:
176 | isVerticalScroll = true;
177 | thisTouchHadDeal = false;
178 | break;
179 | }
180 |
181 | return super.onInterceptTouchEvent(ev);
182 | }
183 |
184 | @Override
185 | public boolean onTouchEvent(MotionEvent e) {
186 | switch (e.getAction()) {
187 | case MotionEvent.ACTION_DOWN:
188 | break;
189 | case MotionEvent.ACTION_MOVE:
190 |
191 | Log.d(TAG, "computeVerticalScrollOffset() = " + computeVerticalScrollOffset());
192 | //理论上应该computeVerticalScrollOffset == 0 但是因为隐藏header的缘故,这里的判断距离增加一个item的高度
193 | if (getChildAt(1) != null && computeVerticalScrollOffset() <= getChildAt(1).getHeight()) {//recyclerviwe已滑动到顶部
194 | if (refreshing || !pullToRefreshEnable)
195 | return super.onTouchEvent(e);
196 | touchCurrY = e.getY();
197 |
198 | float distanceY = touchCurrY - startY;
199 | if (distanceY > 0) {
200 | dealPulling(distanceY);
201 | return true;
202 | }
203 | }
204 |
205 | // if (!canScrollVertically(1)) {//已经滑到底部
206 | //
207 | // if (loading || !pullLoadMoreEnable)
208 | // return super.onTouchEvent(e);
209 | //
210 | // float distanceY = touchCurrY - startY;
211 | // if (distanceY < 0) {
212 | // startLoadMore();
213 | // return true;
214 | // }
215 | // }
216 | break;
217 | case MotionEvent.ACTION_UP:
218 | if (!refreshing) {
219 | currHeaderHeight = adapter.getHeaderView().getHeight();
220 | dealPullUp();
221 | }
222 | break;
223 | }
224 | return super.onTouchEvent(e);
225 | }
226 |
227 | /**
228 | * 开始加载更多
229 | */
230 | private void startLoadMore() {
231 | loading = true;
232 | if (null != onPullListener)
233 | onPullListener.onLoadMore();
234 |
235 | adapter.getFooterView().setNowState(FooterView.STATE.LOADING);
236 | }
237 |
238 | public void onLoadMoreComplete() {
239 | loading = false;
240 | adapter.getFooterView().setNowState(FooterView.STATE.READY);
241 | }
242 |
243 | private void dealPullUp() {
244 | HeaderView headerView = adapter.getHeaderView();
245 | int height = headerView.getHeight();
246 | if (height < refreshHeight) {
247 | startScrollBackAnim();
248 | } else {
249 | scrollToReadyAndRefresh();
250 | }
251 | }
252 |
253 |
254 | /**
255 | * 回滚到刷新状态的位置并开始刷新
256 | */
257 | private void scrollToReadyAndRefresh() {
258 | if (null != onPullListener)
259 | onPullListener.onRefresh();
260 | refreshing = true;
261 | ValueAnimator animator = ObjectAnimator.ofFloat(1, refreshHeight / currHeaderHeight);
262 | animator.setDuration(200);
263 | animator.addUpdateListener(scrollToReadyUpdateListener);
264 | animator.addListener(scrollToReadyAnimatorListener);
265 | animator.start();
266 | }
267 |
268 | private ValueAnimator.AnimatorUpdateListener scrollToReadyUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
269 | @Override
270 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
271 | float value = (float) valueAnimator.getAnimatedValue();
272 | adapter.getHeaderView().setViewHeight(currHeaderHeight * value);
273 | }
274 | };
275 |
276 | private Animator.AnimatorListener scrollToReadyAnimatorListener = new Animator.AnimatorListener() {
277 | @Override
278 | public void onAnimationStart(Animator animator) {
279 |
280 | }
281 |
282 | @Override
283 | public void onAnimationEnd(Animator animator) {
284 | adapter.getHeaderView().setNowState(HeaderView.STATE.REFRESHING);
285 | }
286 |
287 | @Override
288 | public void onAnimationCancel(Animator animator) {
289 |
290 | }
291 |
292 | @Override
293 | public void onAnimationRepeat(Animator animator) {
294 |
295 | }
296 | };
297 |
298 |
299 | /**
300 | * 开启回滚回原位的动画
301 | */
302 | private void startScrollBackAnim() {
303 | ValueAnimator animator = ObjectAnimator.ofFloat(1, 0);
304 | animator.setDuration(150);
305 | animator.addUpdateListener(scrollBackAnimatUpdateListener);
306 | animator.start();
307 | }
308 |
309 | private ValueAnimator.AnimatorUpdateListener scrollBackAnimatUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
310 | @Override
311 | public void onAnimationUpdate(ValueAnimator valueAnimator) {
312 | float value = (float) valueAnimator.getAnimatedValue();
313 | adapter.getHeaderView().setViewHeight(currHeaderHeight * value);
314 | if (value == 0) {
315 | refreshing = false;
316 | }
317 | }
318 | };
319 |
320 | /**
321 | * 处理下拉过程
322 | *
323 | * @param distanceY 手指下拉的距离
324 | */
325 | private void dealPulling(float distanceY) {
326 |
327 | float moveDistance = distanceY * 0.6f;
328 | adapter.getHeaderView().setViewHeight(moveDistance);
329 | if (moveDistance < refreshHeight) {
330 | adapter.getHeaderView().setNowState(HeaderView.STATE.PULLING);
331 | } else {
332 | adapter.getHeaderView().setNowState(HeaderView.STATE.READY);
333 | }
334 | }
335 |
336 | /**
337 | * 结束下拉刷新
338 | */
339 | public void onRefreshComplete() {
340 | currHeaderHeight = (int) refreshHeight;
341 | startScrollBackAnim();
342 | }
343 |
344 | /**
345 | * 设置菜单点击监听
346 | *
347 | * @param onMenuClickListener
348 | */
349 | public void setOnMenuClickListener(OnMenuClickListener onMenuClickListener) {
350 | this.onMenuClickListener = onMenuClickListener;
351 | }
352 |
353 | /**
354 | * 判断是横向滑动还是纵向滑动
355 | *
356 | * @param startX
357 | * @param startY
358 | * @param secondX
359 | * @param secondY
360 | * @return
361 | */
362 |
363 | private boolean isHorizontalScroll(int startX, int startY, int secondX, int secondY) {
364 | boolean ret = false;
365 | int distanceX = Math.abs(startX - secondX);
366 | int distanceY = Math.abs(startY - secondY);
367 |
368 | if (distanceX * Math.tan(2 * Math.PI / 360 * 30) > distanceY)//水平30度角度内算作水平滑动
369 | ret = true;
370 | return ret;
371 | }
372 |
373 | /**
374 | * 设置下拉刷新和上拉加载的事件监听
375 | *
376 | * @param onPullListener
377 | */
378 | public void setOnPullListener(OnPullListener onPullListener) {
379 | this.onPullListener = onPullListener;
380 | }
381 |
382 | /**
383 | * 设置是否允许上拉加载更多
384 | *
385 | * @param enable true 允许上拉加载更多;false 不允许上拉加载更多
386 | */
387 | public void setPullLoadMoreEnable(boolean enable) {
388 | this.pullLoadMoreEnable = enable;
389 |
390 | if (null != adapter && !enable)
391 | adapter.setFooterViewEnable(false);
392 | }
393 |
394 | /**
395 | * 设置是否允许下拉刷新
396 | *
397 | * @param enable true 允许下拉刷新;false 不允许下拉刷新
398 | */
399 | public void setPullToRefreshEnable(boolean enable) {
400 | this.pullToRefreshEnable = enable;
401 | }
402 |
403 | public interface OnPullListener {
404 | /**
405 | * 下拉刷新触发执行方法
406 | */
407 | void onRefresh();
408 |
409 | /**
410 | * 加载更多触发执行方法
411 | */
412 | void onLoadMore();
413 | }
414 |
415 | }
416 |
--------------------------------------------------------------------------------
/library/src/main/java/com/library/widget/baseview/SwipeMenuLayout.java:
--------------------------------------------------------------------------------
1 | package com.library.widget.baseview;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.util.Log;
6 | import android.view.MotionEvent;
7 | import android.view.View;
8 | import android.view.animation.Interpolator;
9 | import android.widget.FrameLayout;
10 | import android.widget.LinearLayout;
11 | import android.widget.Scroller;
12 |
13 | /**
14 | * Created by zhangyu on 2016/11/8.
15 | */
16 | public class SwipeMenuLayout extends FrameLayout {
17 | private static final String TAG = "SwipeMenuLayout";
18 |
19 | private View contentView;
20 | private LinearLayout menuView;
21 | private Context context;
22 | private Scroller mScroller;
23 |
24 | public SwipeMenuLayout(Context context, AttributeSet attrs) {
25 | super(context, attrs);
26 | init(context, attrs);
27 | }
28 |
29 | public SwipeMenuLayout(Context context) {
30 | super(context);
31 | init(context, null);
32 | }
33 |
34 | public SwipeMenuLayout(Context context, AttributeSet attrs, int defStyleAttr) {
35 | super(context, attrs, defStyleAttr);
36 | init(context, attrs);
37 | }
38 |
39 | public SwipeMenuLayout(Context context, View contentView, LinearLayout menuView) {
40 | super(context);
41 | this.contentView = contentView;
42 | this.menuView = menuView;
43 | init(context, null);
44 | }
45 |
46 | private void init(Context context, AttributeSet attrs) {
47 | this.context = context;
48 | mScroller = new Scroller(context);
49 |
50 |
51 | LayoutParams contentParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
52 | if (null != contentView) {
53 | addView(contentView);
54 | contentView.setLayoutParams(contentParams);
55 | }
56 | if (null != menuView) {
57 | addView(menuView);
58 | menuView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
59 | }
60 | setLayoutParams(contentParams);
61 |
62 |
63 | }
64 |
65 | private float preTouchX, preTouchY;
66 | private float nowTouchX, nowTouchY;
67 |
68 |
69 | @Override
70 | public boolean onTouchEvent(MotionEvent event) {
71 | switch (event.getAction()) {
72 | case MotionEvent.ACTION_DOWN:
73 | //点击停止滚动,或者开始下一轮滚动的数据设置
74 | if (!mScroller.computeScrollOffset()) { // 滚动已经结束
75 |
76 | Log.i(TAG, "ACTION_DOWN..");
77 | preTouchX = event.getX();
78 | preTouchY = event.getY();
79 | return true;
80 | }
81 | break;
82 | case MotionEvent.ACTION_MOVE:
83 | if (!mScroller.computeScrollOffset()) { // 滚动已结束
84 | nowTouchX = event.getX();
85 | nowTouchY = event.getY();
86 |
87 | if (null == menuView)
88 | return super.onTouchEvent(event);
89 |
90 | //当从0点往左滑动时,currx为整数,滑得越远,数越大
91 | int currX = mScroller.getCurrX();
92 | float distanceX = nowTouchX - preTouchX;
93 | if (currX < menuView.getWidth() && currX > 0) {//菜单已被拉出,往左右都可滑
94 | if (distanceX < 0) {//往左拖,打开菜单方向
95 | //防止滑动距离超过菜单宽度
96 | distanceX = (-distanceX + currX > menuView.getWidth()) ? currX - menuView.getWidth() : distanceX;
97 | }
98 |
99 | if (distanceX > 0) {//往右拖,关闭菜单方向
100 | distanceX = distanceX > currX ? currX : distanceX;
101 | }
102 | return doScroll(distanceX);
103 | }
104 |
105 | //菜单已被完全拉出,往右可滑
106 | if (currX >= menuView.getWidth() && distanceX > 0) {
107 | Log.i(TAG, "currX = " + currX + " ,distanceX = " + distanceX + " ,menuView.getWidth() = " + menuView.getWidth());
108 | return doScroll(distanceX);
109 | }
110 |
111 | //菜单未被拉出,往左可滑
112 | if (currX <= 0 && distanceX < 0) {
113 | Log.v(TAG, "currX = " + currX + " ,distanceX = " + distanceX + " ,menuView.getWidth() = " + menuView.getWidth());
114 | return doScroll(distanceX);
115 | }
116 |
117 | preTouchX = nowTouchX;
118 | preTouchY = nowTouchY;
119 | }
120 | break;
121 | case MotionEvent.ACTION_UP:
122 | Log.i(TAG, "ACTION_UP..");
123 | if (null != menuView)
124 | startAutoScroll();
125 | break;
126 | }
127 | return super.onTouchEvent(event);
128 | }
129 |
130 |
131 | private void startAutoScroll() {
132 | int scrollX = getScrollX();
133 | Log.i(TAG, "startAutoScroll..scrollX = " + scrollX);
134 | if (scrollX >= menuView.getWidth() * 0.5) {
135 | smoothOpenMenu();
136 | } else {
137 | smoothCloseMenu();
138 | }
139 | }
140 |
141 | private void smoothOpenMenu() {
142 | smoothScrollTo(menuView.getWidth(), 0);
143 | }
144 |
145 | public void smoothCloseMenu() {
146 | smoothScrollTo(0, 0);
147 | }
148 |
149 | private boolean doScroll(float distanceX) {
150 | setScrollDistance(distanceX);
151 | preTouchX = nowTouchX;
152 | return true;
153 | }
154 |
155 | /**
156 | * 设置当次滚动的距离
157 | *
158 | * @param distanceX
159 | */
160 | private void setScrollDistance(float distanceX) {
161 | float X = -getScrollX();
162 | mScroller.setFinalX((-(int) (X + distanceX)));
163 | invalidate();
164 | }
165 |
166 |
167 | @Override
168 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
169 | // Log.d(TAG, "contentView.getWidth() = " + contentView.getWidth() + "contentView.getHeight() = " + contentView.getHeight());
170 | super.onLayout(changed, left, top, right, bottom);
171 |
172 | int contentViewWidth = contentView.getWidth();
173 | int contentViewHeight = contentView.getHeight();
174 | if (contentView != null && contentViewWidth != 0 && contentViewHeight != 0) {
175 | contentView.layout(0, 0, contentView.getWidth(), contentView.getHeight());
176 |
177 | if (menuView != null) {
178 | int menuViewWidth = menuView.getWidth();
179 | menuView.layout(contentViewWidth, 0, contentViewWidth + menuViewWidth, contentViewHeight);
180 | }
181 | }
182 | }
183 |
184 | /**
185 | * 获取menuView
186 | *
187 | * @return
188 | */
189 | public LinearLayout getMenuView() {
190 | return menuView;
191 | }
192 |
193 | //调用此方法滚动到目标位置
194 | public void smoothScrollTo(int fx, int fy) {
195 | int dx = fx - mScroller.getFinalX();
196 | int dy = fy - mScroller.getFinalY();
197 | smoothScrollBy(dx, dy);
198 | }
199 |
200 | //调用此方法设置滚动的相对偏移
201 | public void smoothScrollBy(int dx, int dy) {
202 |
203 | //设置mScroller的滚动偏移量
204 | mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
205 | invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果
206 | }
207 |
208 | @Override
209 | public void computeScroll() {
210 |
211 | //先判断mScroller滚动是否完成
212 | if (mScroller.computeScrollOffset()) {
213 |
214 | //这里调用View的scrollTo()完成实际的滚动
215 | scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
216 |
217 | //必须调用该方法,否则不一定能看到滚动效果
218 | postInvalidate();
219 | }
220 | super.computeScroll();
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/library/src/main/java/com/library/widget/defaultimple/DefaultFooterView.java:
--------------------------------------------------------------------------------
1 | package com.library.widget.defaultimple;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.ProgressBar;
6 | import android.widget.RelativeLayout;
7 | import android.widget.TextView;
8 |
9 | import com.library.widget.R;
10 | import com.library.widget.baseview.FooterView;
11 |
12 | /**
13 | * Created by zhangyu on 2016/11/18.
14 | */
15 |
16 | public class DefaultFooterView extends FooterView {
17 |
18 | public TextView hintText;
19 | private ProgressBar progressBar;
20 |
21 | public DefaultFooterView(Context context) {
22 | super(context);
23 | }
24 |
25 | public DefaultFooterView(Context context, AttributeSet attrs) {
26 | super(context, attrs);
27 | }
28 |
29 | public DefaultFooterView(Context context, AttributeSet attrs, int defStyleAttr) {
30 | super(context, attrs, defStyleAttr);
31 | }
32 |
33 | @Override
34 | public int getLayoutView() {
35 | return R.layout.pull_to_refresh_footer;
36 | }
37 |
38 | @Override
39 | public void initView() {
40 | hintText = (TextView) findViewById(R.id.ptrf_tv);
41 | progressBar = (ProgressBar) findViewById(R.id.ptrf_progress_bar);
42 | }
43 |
44 | @Override
45 | public RelativeLayout getRootContainer() {
46 | return (RelativeLayout) findViewById(R.id.footer_container);
47 | }
48 |
49 | @Override
50 | public void setLoadingViewState() {
51 | progressBar.setVisibility(VISIBLE);
52 | hintText.setText("正在加载..");
53 | }
54 |
55 | @Override
56 | public void setPullingViewState() {
57 | progressBar.setVisibility(INVISIBLE);
58 | hintText.setText("上拉加载更多");
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/library/src/main/java/com/library/widget/defaultimple/DefaultHeaderView.java:
--------------------------------------------------------------------------------
1 | package com.library.widget.defaultimple;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.ProgressBar;
6 | import android.widget.RelativeLayout;
7 | import android.widget.TextView;
8 |
9 | import com.library.widget.R;
10 | import com.library.widget.baseview.HeaderView;
11 |
12 | /**
13 | * Created by zhangyu on 2016/11/18.
14 | */
15 |
16 | public class DefaultHeaderView extends HeaderView {
17 | public TextView hintText;
18 | private ProgressBar progressBar;
19 | public DefaultHeaderView(Context context) {
20 | super(context);
21 | }
22 |
23 | public DefaultHeaderView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public DefaultHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
28 | super(context, attrs, defStyleAttr);
29 | }
30 |
31 | @Override
32 | public void initView() {
33 | hintText = (TextView) findViewById(R.id.ptrh_tv);
34 | progressBar = (ProgressBar) findViewById(R.id.ptrh_progress_bar);
35 | }
36 |
37 | @Override
38 | public RelativeLayout getRootContainer() {
39 | return (RelativeLayout) findViewById(R.id.header_container);
40 | }
41 |
42 | @Override
43 | public int getLayoutView() {
44 | return R.layout.pull_to_refresh_header;
45 | }
46 |
47 | @Override
48 | public void setRefreshingViewState() {
49 | progressBar.setVisibility(VISIBLE);
50 | hintText.setText("正在刷新..");
51 | }
52 |
53 | @Override
54 | public void setPullingViewState() {
55 | progressBar.setVisibility(INVISIBLE);
56 | hintText.setText("下拉刷新");
57 | }
58 |
59 | @Override
60 | public void setReadyViewState() {
61 | progressBar.setVisibility(INVISIBLE);
62 | hintText.setText("放开刷新");
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/library/src/main/java/com/library/widget/interfaces/OnMenuClickListener.java:
--------------------------------------------------------------------------------
1 | package com.library.widget.interfaces;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Created by zhangyu on 2016/11/9.
7 | */
8 | public interface OnMenuClickListener {
9 | /**
10 | * 菜单点击监听
11 | * @param view 被点击菜单view
12 | * @param position 被点击菜单所在item的位置
13 | */
14 | void onMenuClick(View view,int position);
15 | }
16 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/pull_to_refresh_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
26 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/pull_to_refresh_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
26 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Library
3 |
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------